v2.4.2 (#482)
Co-authored-by: Kim-Adeline Miguel <kimadeline.miguel@mistral.ai> Co-authored-by: Clement Sirieix <clem.sirieix@gmail.com> Co-authored-by: laurens <laurens@mistral.ai>
This commit is contained in:
parent
e9428bce23
commit
9421fbc08e
21 changed files with 370 additions and 40 deletions
152
tests/cli/test_ui_skill_dispatch.py
Normal file
152
tests/cli/test_ui_skill_dispatch.py
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
import time
|
||||
|
||||
import pytest
|
||||
|
||||
from tests.conftest import build_test_vibe_app, build_test_vibe_config
|
||||
from tests.skills.conftest import create_skill
|
||||
from vibe.cli.textual_ui.app import VibeApp
|
||||
from vibe.cli.textual_ui.widgets.chat_input.container import ChatInputContainer
|
||||
from vibe.cli.textual_ui.widgets.messages import ErrorMessage, UserMessage
|
||||
|
||||
SKILL_BODY = "## Instructions\n\nDo the thing."
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def vibe_app_with_skills(tmp_path: Path) -> VibeApp:
|
||||
skills_dir = tmp_path / "skills"
|
||||
skills_dir.mkdir()
|
||||
create_skill(skills_dir, "my-skill", body=SKILL_BODY)
|
||||
return build_test_vibe_app(config=build_test_vibe_config(skill_paths=[skills_dir]))
|
||||
|
||||
|
||||
async def _wait_for_user_message_containing(
|
||||
vibe_app: VibeApp, pilot, text: str, timeout: float = 1.0
|
||||
) -> UserMessage:
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
for message in vibe_app.query(UserMessage):
|
||||
if text in message._content:
|
||||
return message
|
||||
await pilot.pause(0.05)
|
||||
raise TimeoutError(
|
||||
f"UserMessage containing {text!r} did not appear within {timeout}s"
|
||||
)
|
||||
|
||||
|
||||
async def _wait_for_error_message_containing(
|
||||
vibe_app: VibeApp, pilot, text: str, timeout: float = 1.0
|
||||
) -> ErrorMessage:
|
||||
deadline = time.monotonic() + timeout
|
||||
while time.monotonic() < deadline:
|
||||
for error in vibe_app.query(ErrorMessage):
|
||||
if text in error._error:
|
||||
return error
|
||||
await pilot.pause(0.05)
|
||||
raise TimeoutError(
|
||||
f"ErrorMessage containing {text!r} did not appear within {timeout}s"
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skill_without_args_sends_skill_content(
|
||||
vibe_app_with_skills: VibeApp,
|
||||
) -> None:
|
||||
async with vibe_app_with_skills.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app_with_skills.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/my-skill"))
|
||||
await pilot.pause(0.1)
|
||||
|
||||
message = await _wait_for_user_message_containing(
|
||||
vibe_app_with_skills, pilot, "Do the thing."
|
||||
)
|
||||
assert "Do the thing." in message._content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skill_with_args_prepends_invocation_line(
|
||||
vibe_app_with_skills: VibeApp,
|
||||
) -> None:
|
||||
async with vibe_app_with_skills.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app_with_skills.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/my-skill foo bar"))
|
||||
await pilot.pause(0.1)
|
||||
|
||||
message = await _wait_for_user_message_containing(
|
||||
vibe_app_with_skills, pilot, "Do the thing."
|
||||
)
|
||||
assert "/my-skill foo bar" in message._content
|
||||
assert "Do the thing." in message._content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_unknown_skill_falls_through_to_agent(
|
||||
vibe_app_with_skills: VibeApp,
|
||||
) -> None:
|
||||
async with vibe_app_with_skills.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app_with_skills.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/nonexistent-skill"))
|
||||
await pilot.pause(0.2)
|
||||
|
||||
skill_errors = [
|
||||
e
|
||||
for e in vibe_app_with_skills.query(ErrorMessage)
|
||||
if "skill" in str(getattr(e, "_error", "")).lower()
|
||||
]
|
||||
assert not skill_errors
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_bare_slash_falls_through(vibe_app_with_skills: VibeApp) -> None:
|
||||
async with vibe_app_with_skills.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app_with_skills.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/"))
|
||||
await pilot.pause(0.2)
|
||||
|
||||
assert not any(
|
||||
"Do the thing." in m._content
|
||||
for m in vibe_app_with_skills.query(UserMessage)
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skill_without_args_does_not_prepend_invocation_line(
|
||||
vibe_app_with_skills: VibeApp,
|
||||
) -> None:
|
||||
async with vibe_app_with_skills.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app_with_skills.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/my-skill"))
|
||||
await pilot.pause(0.1)
|
||||
|
||||
message = await _wait_for_user_message_containing(
|
||||
vibe_app_with_skills, pilot, "Do the thing."
|
||||
)
|
||||
assert "/my-skill" not in message._content
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_skill_with_missing_file_shows_error(tmp_path: Path) -> None:
|
||||
skills_dir = tmp_path / "skills"
|
||||
skills_dir.mkdir()
|
||||
skill_dir = create_skill(skills_dir, "my-skill", body=SKILL_BODY)
|
||||
vibe_app = build_test_vibe_app(
|
||||
config=build_test_vibe_config(skill_paths=[skills_dir])
|
||||
)
|
||||
(skill_dir / "SKILL.md").unlink()
|
||||
|
||||
async with vibe_app.run_test() as pilot:
|
||||
await pilot.pause(0.1)
|
||||
chat_input = vibe_app.query_one(ChatInputContainer)
|
||||
chat_input.post_message(ChatInputContainer.Submitted("/my-skill"))
|
||||
|
||||
error = await _wait_for_error_message_containing(
|
||||
vibe_app, pilot, "Failed to read skill file"
|
||||
)
|
||||
assert "Failed to read skill file" in error._error
|
||||
Loading…
Add table
Add a link
Reference in a new issue