"""Tests for backend-owned, fixed-SHA source access through a bare mirror.""" from __future__ import annotations import os import json import subprocess import argparse import importlib.util from pathlib import Path from collections import namedtuple import pytest from investibook.core.code_context import ( CodeContextError, LocalCodeContextService, code_mirror_status, persist_code_investigator_envelope, sync_code_mirror, ) from investibook.core.code_repositories import CodeRepository from investibook.api import code_context as code_context_api from investibook.api.code_context import _replace_workspace_code_source from investibook import config as cfg CODE_INVESTIGATOR_ENVELOPE_MARKER = "" TIDB_REPO = "pingcap/tidb" def _run(*command: str, cwd: Path | None = None) -> str: return subprocess.run( command, cwd=cwd, check=True, text=True, capture_output=True ).stdout.strip() def _make_mirror( tmp_path: Path, *, symlink_target: str | None = None, extra_files: dict[str, str] | None = None, ) -> tuple[Path, str]: source = tmp_path / "source" source.mkdir() _run("git", "init", "-q", str(source)) _run("git", "-C", str(source), "config", "user.email", "test@example.com") _run("git", "-C", str(source), "config", "user.name", "Test") (source / "pkg").mkdir() (source / "pkg" / "logic.go").write_text("package pkg\n\n", encoding="utf-8") (source / "pkg" / "logic_test.go").write_text("package pkg\n", encoding="utf-8") (source / "tests").mkdir() (source / "tests" / "fixture.txt").write_text("fixture\n", encoding="utf-8") if symlink_target: os.symlink(symlink_target, source / "pkg" / "source-link") for relative, contents in (extra_files or {}).items(): target = source / relative target.parent.mkdir(parents=True, exist_ok=True) target.write_text(contents, encoding="utf-8") _run("git", "-C", str(source), "add", ".") _run("git", "-C", str(source), "commit", "-qm", "fixture") sha = _run("git", "-C", str(source), "rev-parse", "HEAD") mirror = tmp_path / "cache" / "mirrors" / "pingcap-tidb.git" mirror.parent.mkdir(parents=True) _run("git", "clone", "--mirror", "-q", str(source), str(mirror)) return mirror, sha def test_prepare_uses_only_the_existing_bare_mirror_and_includes_tests( tmp_path: Path, ) -> None: _, sha = _make_mirror(tmp_path) service = LocalCodeContextService(tmp_path / "cache") created = service.prepare_context(repo=TIDB_REPO, commit_sha=sha) assert created.reused is True assert created.context_id == f"tidb:{sha}:source" assert not (tmp_path / "cache" / "snapshots").exists() assert ( service.read_context_ranges( context_id=created.context_id, ranges=[("pkg/logic_test.go", 1, 1)] )["ranges"][0]["path"] == "pkg/logic_test.go" ) assert service.list_context_files(context_id=created.context_id) == [ "pkg/logic.go", "pkg/logic_test.go", "tests/fixture.txt", ] def test_legacy_no_tests_context_remains_readable_after_profile_expansion( tmp_path: Path, ) -> None: _, sha = _make_mirror(tmp_path) service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context( repo=TIDB_REPO, commit_sha=sha, profile="no-tests" ) assert context.context_id == f"tidb:{sha}:no-tests" assert ( service.read_context_ranges( context_id=context.context_id, ranges=[("tests/fixture.txt", 1, 1)] )["ranges"][0]["path"] == "tests/fixture.txt" ) def test_prepare_returns_sha_derived_navigation_without_persisting_it( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: _, sha = _make_mirror( tmp_path, extra_files={ "go.mod": "module example.com/project\n", "cmd/main.go": "package main\n", }, ) cache = tmp_path / "cache" service = LocalCodeContextService(cache) context = service.prepare_context(repo=TIDB_REPO, commit_sha=sha) expected_navigation = { "language": "go", "manifests": ["go.mod"], "top_level_directories": ["cmd", "pkg", "tests"], } assert service.context_navigation(context_id=context.context_id) == expected_navigation assert not (cache / "snapshots").exists() script_path = ( Path(__file__).parents[2] / "agentkit/skills/builtin/tidb-code-context/scripts/code_context.py" ) spec = importlib.util.spec_from_file_location("code_context_helper_prepare", script_path) assert spec and spec.loader helper = importlib.util.module_from_spec(spec) spec.loader.exec_module(helper) helper.LocalCodeContextService = lambda: service calls: list[list[str]] = [] original_run = subprocess.run def tracked_run(*args, **kwargs): command = args[0] if args else kwargs.get("args") if isinstance(command, list): calls.append(command) return original_run(*args, **kwargs) monkeypatch.setattr("investibook.core.code_context.subprocess.run", tracked_run) result = helper._prepare( argparse.Namespace(repo=TIDB_REPO, sha=sha, profile="source") ) assert result["navigation"] == expected_navigation assert len([command for command in calls if "ls-tree" in command]) == 1 def test_resolve_accepts_an_exact_release_tag_from_the_bare_mirror( tmp_path: Path, ) -> None: mirror, sha = _make_mirror(tmp_path) _run("git", "-C", str(mirror), "tag", "v7.5.1", sha) service = LocalCodeContextService(tmp_path / "cache") assert service.resolve_commit(repo=TIDB_REPO, commit_ref="v7.5.1") == sha with pytest.raises(CodeContextError, match="release tag"): service.resolve_commit(repo=TIDB_REPO, commit_ref="v7.5") def test_list_release_tags_returns_only_supported_release_series( tmp_path: Path, ) -> None: mirror, sha = _make_mirror(tmp_path) for tag in ("v7.5.1", "v8.5.2", "v8.4.0", "v9.0.0-rc.1"): _run("git", "-C", str(mirror), "tag", tag, sha) assert LocalCodeContextService(tmp_path / "cache").list_release_tags( repo=TIDB_REPO ) == [ "v8.5.2", "v7.5.1", ] def test_tikv_context_uses_its_own_mirror_and_release_tags(tmp_path: Path) -> None: mirror, sha = _make_mirror( tmp_path, extra_files={ "Cargo.toml": "[workspace]\nmembers = []\n", "components/example/src/lib.rs": "pub struct Example;\n", }, ) tikv_mirror = mirror.with_name("pingcap-tikv.git") mirror.rename(tikv_mirror) _run("git", "-C", str(tikv_mirror), "tag", "v7.5.1", sha) _run("git", "-C", str(tikv_mirror), "tag", "v8.4.0", sha) _run("git", "-C", str(tikv_mirror), "tag", "v8.5.2", sha) _run("git", "-C", str(tikv_mirror), "tag", "v9.0.0-rc.1", sha) service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context(repo="pingcap/tikv", commit_sha=sha) assert context.repo == "pingcap/tikv" assert context.context_id == f"tikv:{sha}:source" assert service.context_navigation(context_id=context.context_id) == { "language": "rust", "manifests": ["Cargo.toml"], "top_level_directories": ["components", "pkg", "tests"], } assert service.resolve_commit(repo="pingcap/tikv", commit_ref="v8.5.2") == sha assert service.list_release_tags(repo="pingcap/tikv") == ["v8.5.2", "v7.5.1"] assert ( service.read_context_ranges( context_id=context.context_id, ranges=[("pkg/logic.go", 1, 1)] )["ranges"][0]["path"] == "pkg/logic.go" ) def test_history_queries_are_bounded_to_the_selected_source_lineage( tmp_path: Path, ) -> None: mirror, first_sha = _make_mirror(tmp_path) source = tmp_path / "source" (source / "pkg" / "logic.go").write_text( "package pkg\n\nconst Version = 2\n", encoding="utf-8" ) _run("git", "-C", str(source), "add", "pkg/logic.go") _run("git", "-C", str(source), "commit", "-qm", "add version") second_sha = _run("git", "-C", str(source), "rev-parse", "HEAD") _run("git", "-C", str(mirror), "fetch", "-q", "origin") service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context(repo=TIDB_REPO, commit_sha=second_sha) history = service.history_log(context_id=context.context_id, path="pkg/logic.go") assert [entry["commit_sha"] for entry in history] == [second_sha, first_sha] blame = service.history_blame( context_id=context.context_id, path="pkg/logic.go", start=1, end=3 ) assert [line["commit_sha"] for line in blame] == [first_sha, first_sha, second_sha] assert "add version" in service.history_show( context_id=context.context_id, commit_sha=second_sha ) assert "+const Version = 2" in service.history_diff( context_id=context.context_id, from_commit=first_sha, to_commit=second_sha ) with pytest.raises(CodeContextError, match="unavailable"): service.history_show(context_id=context.context_id, commit_sha="a" * 40) with pytest.raises(CodeContextError, match="ordered"): service.history_diff( context_id=context.context_id, from_commit=second_sha, to_commit=first_sha ) def test_successful_prepare_replaces_all_prior_versions_for_the_same_workspace_repo( monkeypatch: pytest.MonkeyPatch, ) -> None: stored = { "workspace-1": [ { "id": "source-old", "createdAt": "earlier", "type": "git_repo", "name": "TiDB v7.5.0", "meta": {"repository_id": "tidb", "commit_sha": "a" * 40}, }, { "id": "source-duplicate", "type": "git_repo", "name": "TiDB v7.5.1", "meta": {"repository_id": "tidb", "commit_sha": "b" * 40}, }, { "id": "source-other", "type": "git_repo", "name": "TiKV", "meta": {"repository_id": "tikv"}, }, ] } monkeypatch.setattr( "investibook.api.code_context._db_get_blob", lambda _table, workspace_id: stored[workspace_id], ) monkeypatch.setattr( "investibook.api.code_context._db_set_blob", lambda _table, workspace_id, sources: stored.__setitem__(workspace_id, sources), ) _replace_workspace_code_source( workspace_id="workspace-1", repository_id="tidb", repo="pingcap/tidb", repository_label="TiDB", version_label="v8.5.6", commit_sha="c" * 40, context_id=f"tidb:{'c' * 40}:source", ) sources = stored["workspace-1"] tidb_sources = [ source for source in sources if source.get("meta", {}).get("repository_id") == "tidb" ] assert len(tidb_sources) == 1 assert tidb_sources[0]["id"] == "source-old" assert tidb_sources[0]["name"] == "TiDB v8.5.6" assert tidb_sources[0]["meta"]["commit_sha"] == "c" * 40 assert any(source["id"] == "source-other" for source in sources) def test_prepare_endpoint_accepts_full_sha_with_no_version_label(monkeypatch): from fastapi import FastAPI from fastapi.testclient import TestClient app = FastAPI() app.include_router(code_context_api.router) app.dependency_overrides[code_context_api.get_current_user] = lambda: { "id": "user-1", "email": "user@example.com", } sha = "1fa258b833ff113883beeba40bc130be7ce66610" async def fake_require_workspace_owner(_workspace_id, _current_user): return {"id": "workspace-1"} class FakeService: def resolve_commit(self, *, repo, commit_ref): assert repo == TIDB_REPO assert commit_ref == sha return sha def prepare_context(self, *, repo, commit_sha): assert repo == TIDB_REPO assert commit_sha == sha return type("Context", (), {"context_id": f"tidb:{sha}:source"})() monkeypatch.setattr( code_context_api, "require_workspace_owner", fake_require_workspace_owner ) monkeypatch.setattr(code_context_api, "LocalCodeContextService", FakeService) monkeypatch.setattr( code_context_api, "_db_backfill_user_code_contexts_from_sources", lambda: None ) monkeypatch.setattr( code_context_api, "_db_get_user_code_context", lambda *_args: None ) monkeypatch.setattr( code_context_api, "_db_upsert_user_code_context", lambda **_kwargs: None ) monkeypatch.setattr(code_context_api, "_db_get_blob", lambda *_args: []) monkeypatch.setattr(code_context_api, "_db_set_blob", lambda *_args: None) with TestClient(app) as client: response = client.post( "/api/code-context/prepare", json={ "workspace_id": "workspace-1", "repo": "pingcap/tidb", "commit_sha": sha, }, ) assert response.status_code == 200 assert response.json() == { "repository_id": "tidb", "repo": "pingcap/tidb", "label": "TiDB", "version_label": None, "commit_sha": sha, "context_id": f"tidb:{sha}:source", } def test_bare_mirror_rejects_symlinked_source_reads(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path, symlink_target="/etc/hosts") context = LocalCodeContextService(tmp_path / "cache").prepare_context( repo=TIDB_REPO, commit_sha=sha ) with pytest.raises(CodeContextError, match="unavailable"): LocalCodeContextService(tmp_path / "cache").read_context_ranges( context_id=context.context_id, ranges=[("pkg/source-link", 1, 1)] ) def test_prepare_never_fetches_for_missing_sha( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: _, _ = _make_mirror(tmp_path) service = LocalCodeContextService(tmp_path / "cache") calls: list[list[str]] = [] original_run = subprocess.run def tracking_run(command: list[str], *args: object, **kwargs: object): calls.append(command) return original_run(command, *args, **kwargs) monkeypatch.setattr("investibook.core.code_context.subprocess.run", tracking_run) with pytest.raises(CodeContextError, match="local TiDB mirror"): service.prepare_context(repo=TIDB_REPO, commit_sha="a" * 40) assert all("fetch" not in command and "clone" not in command for command in calls) def test_prepare_rejects_unknown_source_profile(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path) with pytest.raises(CodeContextError, match="unsupported code source profile"): LocalCodeContextService(tmp_path / "cache").prepare_context( repo=TIDB_REPO, commit_sha=sha, profile="snapshot" ) def test_scheduled_mirror_sync_clones_then_fetches_a_shared_mirror( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: source = tmp_path / "source" source.mkdir() _run("git", "init", "-q", str(source)) _run("git", "-C", str(source), "config", "user.email", "test@example.com") _run("git", "-C", str(source), "config", "user.name", "Test") (source / "README.md").write_text("first\n", encoding="utf-8") _run("git", "-C", str(source), "add", ".") _run("git", "-C", str(source), "commit", "-qm", "first") repository = CodeRepository( "tidb", TIDB_REPO, "TiDB", "pingcap-tidb.git", "tidb", str(source), r"^v[0-9]+\\.[0-9]+\\.[0-9]+$", True, ) monkeypatch.setattr( "investibook.core.code_context.code_repository", lambda repo: repository if repo == TIDB_REPO else None, ) monkeypatch.setattr(cfg, "CODE_MIRROR_PVC_HIGH_WATERMARK_RATIO", 1.0) first = sync_code_mirror(repo=TIDB_REPO, cache_dir=tmp_path / "cache") assert first.result == "success" assert (tmp_path / "cache" / "mirrors" / "pingcap-tidb.git").is_dir() (source / "README.md").write_text("second\n", encoding="utf-8") _run("git", "-C", str(source), "commit", "-am", "second", "-q") second = sync_code_mirror(repo=TIDB_REPO, cache_dir=tmp_path / "cache") assert second.result == "success" status = code_mirror_status(tmp_path / "cache") assert status["result"] == "success" assert status["mirror_size_bytes"] > 0 def test_scheduled_mirror_sync_stops_at_pvc_high_water_mark( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: usage = namedtuple("usage", "total used free")(100, 80, 20) monkeypatch.setattr( "investibook.core.code_context.shutil.disk_usage", lambda _: usage ) monkeypatch.setattr(cfg, "CODE_MIRROR_PVC_HIGH_WATERMARK_RATIO", 0.8) outcome = sync_code_mirror(repo=TIDB_REPO, cache_dir=tmp_path / "cache") assert outcome.result == "capacity_blocked" assert outcome.error is not None assert not (tmp_path / "cache" / "mirrors" / "pingcap-tidb.git").exists() def test_combined_mirror_sync_continues_after_one_repository_fails( monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: from investibook.scripts import sync_code_mirrors repositories = [ CodeRepository("tidb", TIDB_REPO, "TiDB", "tidb.git", "tidb", "", "", True), CodeRepository( "tikv", "pingcap/tikv", "TiKV", "tikv.git", "tikv", "", "", True ), ] calls: list[str] = [] class Outcome: result = "success" def as_dict(self) -> dict[str, str]: return {"result": self.result} def sync(*, repo: str) -> Outcome: calls.append(repo) if repo == TIDB_REPO: raise RuntimeError("TiDB fetch failed") return Outcome() monkeypatch.setattr( sync_code_mirrors, "enabled_code_repositories", lambda: repositories ) monkeypatch.setattr(sync_code_mirrors, "sync_code_mirror", sync) assert sync_code_mirrors.main() == 1 assert calls == [TIDB_REPO, "pingcap/tikv"] assert json.loads(capsys.readouterr().out) == [ {"error": "TiDB fetch failed", "repo": TIDB_REPO, "result": "failed"}, {"repo": "pingcap/tikv", "result": "success"}, ] def test_read_context_ranges_batches_bounded_source_reads(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path) service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context(repo=TIDB_REPO, commit_sha=sha) result = service.read_context_ranges( context_id=context.context_id, ranges=[("pkg/logic.go", 1, 2), ("pkg/logic.go", 1, 1)], ) assert result["context_id"] == context.context_id assert result["ranges"][0]["lines"][0]["reference"] == "pkg/logic.go:1" with pytest.raises(CodeContextError, match="source ranges"): service.read_context_ranges( context_id=context.context_id, ranges=[("pkg/logic.go", 1, 1)] * 13, ) def test_search_reads_the_bare_mirror_without_materializing_a_snapshot( tmp_path: Path, ) -> None: _, sha = _make_mirror( tmp_path, extra_files={"pkg/search.go": 'const Needle = "needle"\n'} ) service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context(repo=TIDB_REPO, commit_sha=sha) matches = service.search_context( context_id=context.context_id, expression="Needle", max_results=10 ) assert matches == [ {"reference": f"pkg/search.go:1@{sha}", "text": 'const Needle = "needle"'} ] assert not (tmp_path / "cache" / "snapshots").exists() def test_search_stops_when_streamed_output_reaches_its_byte_budget( monkeypatch, tmp_path: Path ) -> None: _, sha = _make_mirror( tmp_path, extra_files={ "pkg/search.go": "\n".join(f"const Needle{i} = 1" for i in range(200)) + "\n" }, ) service = LocalCodeContextService(tmp_path / "cache") context = service.prepare_context(repo=TIDB_REPO, commit_sha=sha) monkeypatch.setattr("investibook.core.code_context._MAX_SEARCH_STREAM_BYTES", 120) matches = service.search_context( context_id=context.context_id, expression="Needle", max_results=200 ) assert len(matches) < 200 def test_code_artifact_envelope_validates_and_writes_fixed_path(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) artifact = { "contract_version": 2, "repo": "pingcap/tidb", "commit_sha": sha, "question": "What does the code do?", "code_context": {"context_id": context.context_id, "profile": context.profile}, "confirmed_code_behavior": [ { "claim": "A bounded behavior", "mechanism": "A source mechanism", "evidence": ["pkg/logic_test.go:1"], } ], "limitations": ["runtime occurrence is unverified"], "confidence": "medium", } envelope = CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact) output = persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=envelope, expected_task_id="task-1", expected_session_id="session-1", cache_dir=cache, ) assert ( output.parent == tmp_path / "workspace" / "evidences" / "agent-runs" / "code-investigator" ) assert output.is_file() assert json.loads(output.read_text())["delivery"]["task_id"] == "task-1" latest = ( tmp_path / "workspace" / "evidences" / "agent-runs" / "code-investigator.json" ) manifest = ( tmp_path / "workspace" / "evidences" / "agent-runs" / "code-investigator.manifest.json" ) assert latest.is_file() assert ( json.loads(manifest.read_text())["runs"][0]["artifact"] == output.relative_to(tmp_path / "workspace").as_posix() ) with pytest.raises(CodeContextError, match="not valid JSON"): persist_code_investigator_envelope( workspace_root=tmp_path / "wrapped-workspace", envelope="{}", expected_task_id="task-wrapped-trailing", expected_session_id="session-1", cache_dir=cache, ) artifact["unknown"] = "not part of the contract" with pytest.raises(CodeContextError, match="unsupported fields: unknown"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task-unknown-field", expected_session_id="session-1", cache_dir=cache, ) artifact.pop("unknown") artifact["confirmed_code_behavior"][0]["evidence"] = [f"pkg/logic.go:1@{sha}"] with pytest.raises(CodeContextError, match="invalid source reference"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task-legacy-reference", expected_session_id="session-1", cache_dir=cache, ) artifact["confirmed_code_behavior"][0]["evidence"] = ["pkg/logic.go:2-1"] with pytest.raises(CodeContextError, match="source reference is out of range"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task-reversed-range", expected_session_id="session-1", cache_dir=cache, ) with pytest.raises(CodeContextError, match="trailing prose"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=envelope + "\nnot allowed", expected_task_id="task-2", expected_session_id="session-1", cache_dir=cache, ) artifact["confirmed_code_behavior"][0]["evidence"] = ["pkg/logic.go:1"] with pytest.raises(CodeContextError, match="authorized task SHA"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task-wrong-sha", expected_session_id="session-1", expected_commit_sha="a" * 40, cache_dir=cache, ) def test_code_artifact_requires_direct_answer_for_task_focus_terms( tmp_path: Path, ) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) artifact = { "contract_version": 2, "repo": "pingcap/tidb", "commit_sha": sha, "question": "What is others?", "focus_terms": ["others"], "code_context": {"context_id": context.context_id, "profile": context.profile}, "confirmed_code_behavior": [ {"claim": "bounded", "mechanism": "source", "evidence": ["pkg/logic.go:1"]} ], "limitations": ["source only"], "confidence": "high", } envelope = CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact) with pytest.raises(CodeContextError, match="direct_answers"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=envelope, expected_task_id="task", expected_session_id="session", expected_focus_terms=["others"], cache_dir=cache, ) artifact["direct_answers"] = [ { "focus_term": "others", "answer": "An aggregation bucket.", "evidence": ["pkg/logic.go:1"], } ] persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task", expected_session_id="session", expected_focus_terms=["others"], cache_dir=cache, ) artifact["direct_answers"] = [ { "focus_term": "tidb_slow_log_threshold", "answer": "Controls slow-log emission timing.", "evidence": ["pkg/logic.go:1"], }, { "focus_term": "tidb_enable_slow_log", "answer": "Enables slow-log output.", "evidence": ["pkg/logic.go:1"], }, ] output = persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task", expected_session_id="session", expected_focus_terms=["others"], cache_dir=cache, ) assert json.loads(output.read_text())["delivery"]["validation_warnings"] == [ { "code": "direct_answers_focus_coverage_incomplete", "missing_focus_terms": ["others"], "unexpected_focus_terms": [ "tidb_enable_slow_log", "tidb_slow_log_threshold", ], } ] artifact["direct_answers"] = [ { "focus_term": "others", "answer": "An aggregation bucket.", "evidence": ["pkg/logic.go:1"], } ] artifact["hypothesis_assessment"] = [ { "hypothesis": "the incident uses this path", "source_status": "unresolved", "source_observations": ["the source contains the path"], "evidence": ["pkg/logic.go:1"], "inference_boundary": "runtime occurrence is unverified", } ] persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task", expected_session_id="session", expected_focus_terms=["others"], cache_dir=cache, ) artifact["hypothesis_assessment"][0]["source_status"] = "maybe" with pytest.raises(CodeContextError, match="hypothesis source_status"): persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task", expected_session_id="session", expected_focus_terms=["others"], cache_dir=cache, ) def test_code_artifact_focus_terms_ignore_order_and_whitespace( tmp_path: Path, ) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) artifact = { "contract_version": 2, "repo": TIDB_REPO, "commit_sha": sha, "question": "What are the terms?", "focus_terms": [" second ", "FIRST"], "direct_answers": [ {"focus_term": "first", "answer": "one", "evidence": ["pkg/logic.go:1"]}, {"focus_term": "second", "answer": "two", "evidence": ["pkg/logic.go:1"]}, ], "code_context": {"context_id": context.context_id, "profile": context.profile}, "confirmed_code_behavior": [ {"claim": "bounded", "mechanism": "source", "evidence": ["pkg/logic.go:1"]} ], "limitations": ["source only"], "confidence": "high", } persist_code_investigator_envelope( workspace_root=tmp_path / "workspace", envelope=CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps(artifact), expected_task_id="task", expected_session_id="session", expected_focus_terms=["first", "second"], cache_dir=cache, ) def test_inspect_no_match_returns_recoverable_result(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) script_path = ( Path(__file__).parents[2] / "agentkit/skills/builtin/tidb-code-context/scripts/code_context.py" ) spec = importlib.util.spec_from_file_location("code_context_helper_miss", script_path) assert spec and spec.loader helper = importlib.util.module_from_spec(spec) spec.loader.exec_module(helper) helper.LocalCodeContextService = lambda: LocalCodeContextService(cache) result = helper._inspect( argparse.Namespace( context=context.context_id, anchor=["not_a_real_symbol"], path_hint=["pkg"], ) ) assert result["matched"] is False assert result["error"] == "no source matches for semantic anchors" assert result["ranges"] == [] def test_code_artifact_targeted_follow_up_is_append_only_and_budgeted( tmp_path: Path, ) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) def envelope(term: str) -> str: return ( CODE_INVESTIGATOR_ENVELOPE_MARKER + "\n" + json.dumps( { "contract_version": 2, "repo": "pingcap/tidb", "commit_sha": sha, "question": f"What is {term}?", "focus_terms": [term], "direct_answers": [ { "focus_term": term, "answer": "A bounded answer.", "evidence": ["pkg/logic.go:1"], } ], "code_context": { "context_id": context.context_id, "profile": context.profile, }, "confirmed_code_behavior": [ { "claim": "bounded", "mechanism": "source", "evidence": ["pkg/logic.go:1"], } ], "limitations": ["source only"], "confidence": "high", } ) ) root = tmp_path / "workspace" initial_id = "code-" + "1" * 32 follow_up_id = "code-" + "2" * 32 initial = persist_code_investigator_envelope( workspace_root=root, envelope=envelope("first"), expected_task_id="task-1", expected_session_id="session", expected_focus_terms=["first"], expected_request_id=initial_id, cache_dir=cache, ) with pytest.raises(CodeContextError, match="must use new focus terms"): persist_code_investigator_envelope( workspace_root=root, envelope=envelope("first"), expected_task_id="task-repeat", expected_session_id="session", expected_focus_terms=["first"], expected_request_id="code-" + "0" * 32, task_mode="targeted_follow_up", parent_request_id=initial_id, follow_up_reason="Repeat the initial question.", cache_dir=cache, ) follow_up = persist_code_investigator_envelope( workspace_root=root, envelope=envelope("second"), expected_task_id="task-2", expected_session_id="session", expected_focus_terms=["second"], expected_request_id=follow_up_id, task_mode="targeted_follow_up", parent_request_id=initial_id, follow_up_reason="User requested the unresolved second mechanism.", cache_dir=cache, ) assert initial != follow_up payload = json.loads(follow_up.read_text()) assert payload["delivery"]["root_request_id"] == initial_id assert payload["delivery"]["parent_request_id"] == initial_id manifest = json.loads( ( root / "evidences" / "agent-runs" / "code-investigator.manifest.json" ).read_text() ) assert [run["request_id"] for run in manifest["runs"]] == [initial_id, follow_up_id] assert ( json.loads( (root / "evidences" / "agent-runs" / "code-investigator.json").read_text() )["delivery"]["request_id"] == follow_up_id ) with pytest.raises(CodeContextError, match="budget is exhausted"): persist_code_investigator_envelope( workspace_root=root, envelope=envelope("third"), expected_task_id="task-3", expected_session_id="session", expected_focus_terms=["third"], expected_request_id="code-" + "3" * 32, task_mode="targeted_follow_up", parent_request_id=initial_id, follow_up_reason="One more.", cache_dir=cache, ) with pytest.raises(CodeContextError, match="authorized task SHA"): persist_code_investigator_envelope( workspace_root=root, envelope=envelope("fourth"), expected_task_id="task-4", expected_session_id="session", expected_commit_sha="a" * 40, expected_focus_terms=["fourth"], expected_request_id="code-" + "4" * 32, task_mode="targeted_follow_up", parent_request_id=initial_id, follow_up_reason="Wrong SHA.", cache_dir=cache, ) def test_inspect_batches_ranked_source_windows(tmp_path: Path) -> None: _, sha = _make_mirror(tmp_path) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) script_path = ( Path(__file__).parents[2] / "agentkit/skills/builtin/tidb-code-context/scripts/code_context.py" ) spec = importlib.util.spec_from_file_location("code_context_helper", script_path) assert spec and spec.loader helper = importlib.util.module_from_spec(spec) spec.loader.exec_module(helper) service = LocalCodeContextService(cache) helper.LocalCodeContextService = lambda: service result = helper._inspect( argparse.Namespace( context=context.context_id, anchor=["package", "pkg"], path_hint=["pkg"], ) ) assert result["context_id"] == context.context_id assert result["candidate_count"] >= 1 assert 1 <= len(result["ranges"]) <= 8 assert result["ranges"][0]["path"] == "pkg/logic.go" assert result["ranges"][0]["start_line"] == 1 assert result["ranges"][0]["content"].startswith("1: package pkg") assert len(json.dumps(result, ensure_ascii=False, indent=2).encode()) <= 40_000 def test_inspect_keeps_dense_matches_within_source_range_limits(tmp_path: Path) -> None: _, sha = _make_mirror( tmp_path, extra_files={"pkg/dense.go": "\n".join(["anchor" for _ in range(600)]) + "\n"}, ) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) script_path = ( Path(__file__).parents[2] / "agentkit/skills/builtin/tidb-code-context/scripts/code_context.py" ) spec = importlib.util.spec_from_file_location( "code_context_helper_dense", script_path ) assert spec and spec.loader helper = importlib.util.module_from_spec(spec) spec.loader.exec_module(helper) service = LocalCodeContextService(cache) helper.LocalCodeContextService = lambda: service result = helper._inspect( argparse.Namespace( context=context.context_id, anchor=["anchor"], path_hint=["pkg"], ) ) ranges = result["ranges"] assert ranges assert all(item["end_line"] - item["start_line"] < 501 for item in ranges) assert sum(item["end_line"] - item["start_line"] + 1 for item in ranges) <= 2_000 def test_inspect_truncates_transport_below_tool_output_limit(tmp_path: Path) -> None: _, sha = _make_mirror( tmp_path, extra_files={ "pkg/large.go": "\n".join(["anchor " + "x" * 1_000 for _ in range(200)]) + "\n" }, ) cache = tmp_path / "cache" context = LocalCodeContextService(cache).prepare_context( repo=TIDB_REPO, commit_sha=sha ) script_path = ( Path(__file__).parents[2] / "agentkit/skills/builtin/tidb-code-context/scripts/code_context.py" ) spec = importlib.util.spec_from_file_location( "code_context_helper_budget", script_path ) assert spec and spec.loader helper = importlib.util.module_from_spec(spec) spec.loader.exec_module(helper) service = LocalCodeContextService(cache) helper.LocalCodeContextService = lambda: service result = helper._inspect( argparse.Namespace( context=context.context_id, anchor=["anchor"], path_hint=["pkg"] ) ) assert result["truncated"] is True assert result["refinement_available"] is True assert len(json.dumps(result, ensure_ascii=False, indent=2).encode()) <= 40_000