Co-authored-by: Antoine <33425718+anth2o@users.noreply.github.com> Co-authored-by: Bastien <bastien.baret@gmail.com> Co-authored-by: Clément Sirieix <clement.sirieix@mistral.ai> Co-authored-by: Kim-Adeline Miguel <51720070+kimadeline@users.noreply.github.com> Co-authored-by: Mathias Gesbert <mathias.gesbert@mistral.ai> Co-authored-by: Maxime Dolores <maxime.dolores@ext.mistral.ai> Co-authored-by: Michel Thomazo <51709227+michelTho@users.noreply.github.com> Co-authored-by: Nelson PROIA <144663685+Nelson-PROIA@users.noreply.github.com> Co-authored-by: Pierre Rossinès <pierre.rossines@mistral.ai> Co-authored-by: Quentin <quentin.torroba@mistral.ai> Co-authored-by: Robin Gullo <robin.gullo@mistral.ai> Co-authored-by: Mistral Vibe <vibe@mistral.ai>
87 lines
2.7 KiB
Python
87 lines
2.7 KiB
Python
from __future__ import annotations
|
|
|
|
import json
|
|
import os
|
|
from pathlib import Path
|
|
import tomllib
|
|
|
|
import pexpect
|
|
import pytest
|
|
import tomli_w
|
|
|
|
from tests.e2e.common import (
|
|
SpawnedVibeProcessFixture,
|
|
poll_until,
|
|
send_ctrl_c_until_quit_confirmation,
|
|
wait_for_main_screen,
|
|
wait_for_rendered_text,
|
|
wait_for_request_count,
|
|
)
|
|
from tests.e2e.mock_server import StreamingMockServer
|
|
|
|
|
|
def _enable_hooks(vibe_home: Path, invocation_path: Path) -> None:
|
|
config_path = vibe_home / "config.toml"
|
|
config = tomllib.loads(config_path.read_text(encoding="utf-8"))
|
|
config["enable_experimental_hooks"] = True
|
|
config_path.write_bytes(tomli_w.dumps(config).encode())
|
|
|
|
script = vibe_home / "_record_hook.py"
|
|
script.write_text(
|
|
"import json, sys\n"
|
|
"from pathlib import Path\n"
|
|
f"Path({str(invocation_path)!r}).write_text("
|
|
"json.dumps(json.load(sys.stdin)), encoding='utf-8')\n",
|
|
encoding="utf-8",
|
|
)
|
|
with (vibe_home / "hooks.toml").open("wb") as f:
|
|
tomli_w.dump(
|
|
{
|
|
"hooks": [
|
|
{
|
|
"name": "record-invocation",
|
|
"type": "post_agent_turn",
|
|
"command": f"uv run python {script}",
|
|
}
|
|
]
|
|
},
|
|
f,
|
|
)
|
|
|
|
|
|
@pytest.mark.timeout(20)
|
|
def test_spawn_cli_runs_configured_hook_after_turn(
|
|
streaming_mock_server: StreamingMockServer,
|
|
setup_e2e_env: None,
|
|
e2e_workdir: Path,
|
|
spawned_vibe_process: SpawnedVibeProcessFixture,
|
|
) -> None:
|
|
vibe_home = Path(os.environ["VIBE_HOME"])
|
|
invocation_path = vibe_home / "hook-invocation.json"
|
|
_enable_hooks(vibe_home, invocation_path)
|
|
|
|
with spawned_vibe_process(e2e_workdir) as (child, captured):
|
|
wait_for_main_screen(child, timeout=15)
|
|
child.send("Run the configured hook")
|
|
child.send("\r")
|
|
|
|
wait_for_request_count(
|
|
lambda: len(streaming_mock_server.requests), expected_count=1, timeout=10
|
|
)
|
|
wait_for_rendered_text(
|
|
child, captured, needle="Hello from mock server", timeout=10
|
|
)
|
|
poll_until(
|
|
invocation_path.is_file,
|
|
timeout=10,
|
|
message=f"Timed out waiting for hook output file: {invocation_path}",
|
|
)
|
|
|
|
send_ctrl_c_until_quit_confirmation(child, captured, timeout=5)
|
|
child.expect(pexpect.EOF, timeout=10)
|
|
|
|
assert len(streaming_mock_server.requests) == 1
|
|
invocation = json.loads(invocation_path.read_text(encoding="utf-8"))
|
|
assert invocation["hook_event_name"] == "post_agent_turn"
|
|
assert isinstance(invocation["cwd"], str) and invocation["cwd"]
|
|
assert isinstance(invocation["session_id"], str) and invocation["session_id"]
|