import subprocess from repo_registry.core.service import RegistryService from repo_registry.repo_ingestion.git import GitIngestionService from repo_registry.storage.sqlite import NotFoundError, RegistryStore def make_service(tmp_path): store = RegistryStore(tmp_path / "registry.sqlite3") store.initialize() return RegistryService(store, ingestion=GitIngestionService(tmp_path / "checkouts")) def test_manual_registry_builds_ability_map(tmp_path): service = make_service(tmp_path) repository = service.register_repository( name="MailRouter", url="https://example.com/mail-router.git", description="Routes incoming customer email", ) ability_id = service.add_ability( repository.id, name="Business Email Routing", description="Route inbound messages to the right department.", confidence=0.92, ) capability_id = service.add_capability( repository.id, ability_id, name="Classify Incoming Email", description="Classify messages into intent categories.", inputs=["subject", "body"], outputs=["intent", "confidence"], confidence=0.88, ) service.add_feature( repository.id, capability_id, name="POST /api/classify-email", type="REST endpoint", location="src/routes/classify_email.py", confidence=0.84, ) service.add_evidence( repository.id, capability_id, type="unit_test", reference="tests/test_email_classification.py", strength="strong", ) ability_map = service.ability_map(repository.id) assert ability_map.repository.name == "MailRouter" assert ability_map.abilities[0].name == "Business Email Routing" capability = ability_map.abilities[0].capabilities[0] assert capability.name == "Classify Incoming Email" assert capability.inputs == ["subject", "body"] assert capability.features[0].location == "src/routes/classify_email.py" assert capability.evidence[0].strength == "strong" def test_search_matches_approved_abilities_and_capabilities(tmp_path): service = make_service(tmp_path) repository = service.register_repository( name="MailRouter", url="https://example.com/mail-router.git", description="Manual test repository.", ) ability_id = service.add_ability( repository.id, name="Business Email Routing", description="Route inbound messages.", ) service.add_capability( repository.id, ability_id, name="Classify Incoming Email", description="Classify messages into intent categories.", ) results = service.search("classify") assert len(results) == 1 assert results[0].repository_name == "MailRouter" assert results[0].match_type == "capability" assert results[0].match_name == "Classify Incoming Email" def test_register_repository_imports_metadata_when_name_is_omitted(tmp_path): source = tmp_path / "metadata-source" source.mkdir() (source / "pyproject.toml").write_text( '[project]\nname = "metadata-source"\ndescription = "Imported description."\n', encoding="utf-8", ) service = make_service(tmp_path) repository = service.register_repository(url=str(source)) assert repository.name == "metadata-source" assert repository.description == "Imported description." def test_capability_must_belong_to_repository(tmp_path): service = make_service(tmp_path) first = service.register_repository( name="First", url="https://example.com/first.git", description="Manual first repository.", ) second = service.register_repository( name="Second", url="https://example.com/second.git", description="Manual second repository.", ) ability_id = service.add_ability(first.id, name="Document Classification") try: service.add_capability(second.id, ability_id, name="Classify Document") except NotFoundError as exc: assert "ability" in str(exc) else: raise AssertionError("expected a NotFoundError") def test_analyze_repository_records_snapshot_and_observed_facts(tmp_path): source = tmp_path / "repo" source.mkdir() (source / "README.md").write_text("# Example\n", encoding="utf-8") (source / "requirements.txt").write_text("fastapi\n", encoding="utf-8") (source / "app.py").write_text( "from fastapi import FastAPI\n" "app = FastAPI()\n" '@app.get("/health")\n' "def health():\n" " return {}\n", encoding="utf-8", ) service = make_service(tmp_path) repository = service.register_repository( name="Example", url=str(source), description="A local fixture repository", ) summary = service.analyze_repository(repository.id) assert summary.analysis_run.status == "completed" assert summary.snapshot is not None assert summary.snapshot.file_count == 3 assert service.get_repository(repository.id).status == "analyzed" fact_names = {(fact.kind, fact.name, fact.path) for fact in summary.facts} assert ("documentation", "README", "README.md") in fact_names assert ("framework", "FastAPI", "requirements.txt") in fact_names assert ("interface", "python route decorator", "app.py") in fact_names candidate_graph = service.candidate_graph(repository.id, summary.analysis_run.id) assert candidate_graph.repository.name == "Example" assert candidate_graph.abilities capability_names = { capability.name for ability in candidate_graph.abilities for capability in ability.capabilities } assert "Expose Repository Interface" in capability_names def test_approve_candidate_graph_publishes_ability_map_once(tmp_path): source = tmp_path / "repo" source.mkdir() (source / "README.md").write_text("# Example\n", encoding="utf-8") (source / "app.py").write_text( "from fastapi import FastAPI\n" "app = FastAPI()\n" '@app.get("/health")\n' "def health():\n" " return {}\n", encoding="utf-8", ) service = make_service(tmp_path) repository = service.register_repository(name="Example", url=str(source)) summary = service.analyze_repository(repository.id) ability_map = service.approve_candidate_graph( repository.id, summary.analysis_run.id, notes="Looks good for the first pass.", ) second_approval = service.approve_candidate_graph( repository.id, summary.analysis_run.id, ) assert service.get_repository(repository.id).status == "indexed" assert len(ability_map.abilities) == 1 assert len(second_approval.abilities) == 1 assert ability_map.abilities[0].name == "Review Example Repository Usefulness" assert ability_map.abilities[0].capabilities[0].features[0].location == "app.py" candidate_graph = service.candidate_graph(repository.id, summary.analysis_run.id) assert candidate_graph.abilities[0].status == "approved" def test_analyze_repository_failure_is_recorded(tmp_path): service = make_service(tmp_path) repository = service.register_repository( name="Missing", url=str(tmp_path / "does-not-exist"), description="Manual missing repository.", ) summary = service.analyze_repository(repository.id) assert summary.analysis_run.status == "failed" assert summary.snapshot is None assert "does not exist" in (summary.analysis_run.error_message or "") assert service.get_repository(repository.id).status == "analysis_failed" def test_analyze_repository_clones_git_url_before_scanning(tmp_path): source = tmp_path / "git-source" source.mkdir() subprocess.run(["git", "init", "-b", "main"], cwd=source, check=True) subprocess.run( ["git", "config", "user.email", "tests@example.com"], cwd=source, check=True, ) subprocess.run( ["git", "config", "user.name", "Tests"], cwd=source, check=True, ) (source / "README.md").write_text("# Git Source\n", encoding="utf-8") (source / "requirements.txt").write_text("pytest\n", encoding="utf-8") subprocess.run(["git", "add", "."], cwd=source, check=True) subprocess.run(["git", "commit", "-m", "initial"], cwd=source, check=True) service = make_service(tmp_path) repository = service.register_repository(name="Git Source", url=source.as_uri()) summary = service.analyze_repository(repository.id) assert summary.analysis_run.status == "completed" assert summary.snapshot is not None assert str(tmp_path / "checkouts") in summary.snapshot.source_path fact_names = {(fact.kind, fact.name, fact.path) for fact in summary.facts} assert ("documentation", "README", "README.md") in fact_names assert ("framework", "pytest", "requirements.txt") in fact_names