Assistant: codex Assistant-Model: gpt-5.6-sol Assistant-Session: 01a023c0-a0a3-7c03-b395-5a0d2757214d
105 lines
3.7 KiB
Python
105 lines
3.7 KiB
Python
from __future__ import annotations
|
|
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
from pathlib import Path
|
|
|
|
from repo_manager.provenance import assistant_report, install_hook
|
|
|
|
SOURCE_HOOK = Path(__file__).parents[1] / ".githooks" / "prepare-commit-msg"
|
|
ASSISTANT_ENV_PREFIXES = ("ASSISTANT_", "CLAUDE", "CODEX_", "GROK", "XAI_")
|
|
|
|
|
|
def _human_env() -> dict[str, str]:
|
|
return {
|
|
key: value
|
|
for key, value in os.environ.items()
|
|
if not any(key.startswith(prefix) for prefix in ASSISTANT_ENV_PREFIXES)
|
|
}
|
|
|
|
|
|
def _git(repo: Path, *args: str, env: dict[str, str] | None = None) -> None:
|
|
subprocess.run(["git", *args], cwd=repo, env=env, check=True, capture_output=True)
|
|
|
|
|
|
def _fixture(tmp_path: Path) -> Path:
|
|
repo = tmp_path / "pilot"
|
|
repo.mkdir()
|
|
_git(repo, "init")
|
|
_git(repo, "config", "user.email", "test@example.com")
|
|
_git(repo, "config", "user.name", "Test")
|
|
hooks = repo / ".githooks"
|
|
hooks.mkdir()
|
|
shutil.copy2(SOURCE_HOOK, hooks / "prepare-commit-msg")
|
|
(hooks / "prepare-commit-msg").chmod(0o755)
|
|
_git(repo, "config", "core.hooksPath", str(hooks))
|
|
return repo
|
|
|
|
|
|
def _commit(repo: Path, name: str, *, session: str | None = None) -> None:
|
|
(repo / name).write_text(name, encoding="utf-8")
|
|
_git(repo, "add", name)
|
|
env = _human_env()
|
|
if session:
|
|
env.update(
|
|
ASSISTANT_NAME="codex",
|
|
ASSISTANT_MODEL="gpt-test",
|
|
ASSISTANT_PROCESS="42@test-host",
|
|
ASSISTANT_SESSION=session,
|
|
)
|
|
_git(repo, "commit", "-m", name, env=env)
|
|
|
|
|
|
def test_hook_is_silent_for_humans_and_idempotent_for_assistants(tmp_path: Path) -> None:
|
|
message = tmp_path / "message"
|
|
message.write_text("Change records\n", encoding="utf-8")
|
|
subprocess.run([str(SOURCE_HOOK), str(message)], env=_human_env(), check=True)
|
|
assert message.read_text(encoding="utf-8") == "Change records\n"
|
|
|
|
env = _human_env()
|
|
env.update(
|
|
ASSISTANT_NAME="codex",
|
|
ASSISTANT_MODEL="gpt-test",
|
|
ASSISTANT_PROCESS="42@test-host",
|
|
ASSISTANT_SESSION="session-a",
|
|
)
|
|
subprocess.run([str(SOURCE_HOOK), str(message)], env=env, check=True)
|
|
subprocess.run([str(SOURCE_HOOK), str(message)], env=env, check=True)
|
|
text = message.read_text(encoding="utf-8")
|
|
assert text.count("Assistant: codex") == 1
|
|
assert text.count("Assistant-Model: gpt-test") == 1
|
|
assert text.count("Assistant-Session: session-a") == 1
|
|
|
|
|
|
def test_report_detects_interleaved_sessions_and_unknown_human(tmp_path: Path) -> None:
|
|
repo = _fixture(tmp_path)
|
|
_commit(repo, "human")
|
|
_commit(repo, "a1", session="session-a")
|
|
_commit(repo, "b1", session="session-b")
|
|
_commit(repo, "a2", session="session-a")
|
|
|
|
report = assistant_report(repo)
|
|
assert report["assistants"]["codex"]["commit_count"] == 3
|
|
assert report["assistants"]["codex"]["models"] == ["gpt-test"]
|
|
assert report["interleaved_sessions"] == [["session-a", "session-b"]]
|
|
assert report["unattributed"]["before_cutover"] == 1
|
|
|
|
|
|
def test_install_sets_only_global_hooks_path(tmp_path: Path, monkeypatch) -> None:
|
|
hooks = tmp_path / "hooks"
|
|
hooks.mkdir()
|
|
shutil.copy2(SOURCE_HOOK, hooks / "prepare-commit-msg")
|
|
(hooks / "prepare-commit-msg").chmod(0o755)
|
|
global_config = tmp_path / "gitconfig"
|
|
monkeypatch.setenv("GIT_CONFIG_GLOBAL", str(global_config))
|
|
result = install_hook(hooks)
|
|
assert result["ok"] is True
|
|
configured = subprocess.run(
|
|
["git", "config", "--global", "--get", "core.hooksPath"],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
env={**os.environ, "GIT_CONFIG_GLOBAL": str(global_config)},
|
|
).stdout.strip()
|
|
assert configured == str(hooks.resolve())
|