generated from coulomb/repo-seed
usecase e2e tests
This commit is contained in:
@@ -19,6 +19,7 @@ def test_openapi_groups_agent_facing_endpoints():
|
||||
"review",
|
||||
"registry",
|
||||
"search",
|
||||
"discovery",
|
||||
}
|
||||
search_operation = schema["paths"]["/search"]["get"]
|
||||
assert search_operation["tags"] == ["search"]
|
||||
@@ -190,6 +191,163 @@ def test_api_manual_registry_loop(tmp_path):
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_api_compare_gap_and_export_use_cases(tmp_path):
|
||||
def override_settings():
|
||||
return Settings(
|
||||
database_path=str(tmp_path / "discovery.sqlite3"),
|
||||
checkout_root=str(tmp_path / "discovery-checkouts"),
|
||||
)
|
||||
|
||||
app.dependency_overrides[get_settings] = override_settings
|
||||
client = TestClient(app)
|
||||
try:
|
||||
first = client.post(
|
||||
"/repos",
|
||||
json={
|
||||
"name": "MailRouter",
|
||||
"url": "https://example.com/mail-router.git",
|
||||
"description": "Routes customer email.",
|
||||
},
|
||||
).json()
|
||||
second = client.post(
|
||||
"/repos",
|
||||
json={
|
||||
"name": "SupportRouter",
|
||||
"url": "https://example.com/support-router.git",
|
||||
"description": "Routes support requests.",
|
||||
},
|
||||
).json()
|
||||
|
||||
first_ability = client.post(
|
||||
f"/repos/{first['id']}/abilities",
|
||||
json={
|
||||
"name": "Business Email Routing",
|
||||
"description": "Route inbound messages.",
|
||||
"confidence": 0.92,
|
||||
},
|
||||
).json()["id"]
|
||||
first_classify = client.post(
|
||||
f"/repos/{first['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": first_ability,
|
||||
"name": "Classify Incoming Email",
|
||||
"description": "Classify messages by intent.",
|
||||
"confidence": 0.88,
|
||||
},
|
||||
).json()["id"]
|
||||
client.post(
|
||||
f"/repos/{first['id']}/evidence",
|
||||
json={
|
||||
"capability_id": first_classify,
|
||||
"type": "unit_test",
|
||||
"reference": "tests/test_classify.py",
|
||||
"strength": "strong",
|
||||
},
|
||||
)
|
||||
client.post(
|
||||
f"/repos/{first['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": first_ability,
|
||||
"name": "Route Email to Team",
|
||||
"description": "Route messages to owning teams.",
|
||||
"confidence": 0.7,
|
||||
},
|
||||
)
|
||||
|
||||
second_ability = client.post(
|
||||
f"/repos/{second['id']}/abilities",
|
||||
json={
|
||||
"name": "Business Email Routing",
|
||||
"description": "Support routing workflows.",
|
||||
"confidence": 0.8,
|
||||
},
|
||||
).json()["id"]
|
||||
second_classify = client.post(
|
||||
f"/repos/{second['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": second_ability,
|
||||
"name": "Classify Incoming Email",
|
||||
"description": "Classify support requests.",
|
||||
"confidence": 0.6,
|
||||
},
|
||||
).json()["id"]
|
||||
client.post(
|
||||
f"/repos/{second['id']}/evidence",
|
||||
json={
|
||||
"capability_id": second_classify,
|
||||
"type": "documentation",
|
||||
"reference": "README.md",
|
||||
"strength": "medium",
|
||||
},
|
||||
)
|
||||
client.post(
|
||||
f"/repos/{second['id']}/capabilities",
|
||||
json={
|
||||
"ability_id": second_ability,
|
||||
"name": "Archive Email",
|
||||
"description": "Archive resolved messages.",
|
||||
"confidence": 0.75,
|
||||
},
|
||||
)
|
||||
|
||||
comparison_response = client.get(
|
||||
"/repository-comparisons",
|
||||
params=[
|
||||
("repository_ids", first["id"]),
|
||||
("repository_ids", second["id"]),
|
||||
],
|
||||
)
|
||||
assert comparison_response.status_code == 200
|
||||
comparison = comparison_response.json()
|
||||
assert {repo["name"] for repo in comparison["repositories"]} == {
|
||||
"MailRouter",
|
||||
"SupportRouter",
|
||||
}
|
||||
ability_entry = comparison["abilities"][0]
|
||||
assert ability_entry["name"] == "Business Email Routing"
|
||||
assert {repo["repository_name"] for repo in ability_entry["repositories"]} == {
|
||||
"MailRouter",
|
||||
"SupportRouter",
|
||||
}
|
||||
assert {
|
||||
item["capability_name"] for item in comparison["unique_capabilities"]
|
||||
} >= {"Route Email to Team", "Archive Email"}
|
||||
|
||||
gaps_response = client.post(
|
||||
"/capability-gaps",
|
||||
json={
|
||||
"desired_ability": "Business Email Routing",
|
||||
"desired_capabilities": [
|
||||
"Classify Incoming Email",
|
||||
"Route Email to Team",
|
||||
"German Benchmark Evaluation",
|
||||
],
|
||||
"repository_ids": [first["id"], second["id"]],
|
||||
},
|
||||
)
|
||||
assert gaps_response.status_code == 200
|
||||
gaps = gaps_response.json()
|
||||
assert gaps["missing_capabilities"] == ["German Benchmark Evaluation"]
|
||||
assert gaps["matched_capabilities"][0]["capability"] == (
|
||||
"Classify Incoming Email"
|
||||
)
|
||||
assert {
|
||||
weak["capability"] for weak in gaps["weakly_evidenced_capabilities"]
|
||||
} >= {"Classify Incoming Email", "Route Email to Team"}
|
||||
assert gaps["duplicate_capabilities"][0]["capability"] == (
|
||||
"classify incoming email"
|
||||
)
|
||||
|
||||
export_response = client.get(f"/repos/{first['id']}/export")
|
||||
assert export_response.status_code == 200
|
||||
assert export_response.headers["content-type"].startswith("application/x-yaml")
|
||||
assert 'name: "MailRouter"' in export_response.text
|
||||
assert 'name: "Classify Incoming Email"' in export_response.text
|
||||
assert 'reference: "tests/test_classify.py"' in export_response.text
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_api_registers_repository_from_url_metadata(tmp_path):
|
||||
source = tmp_path / "metadata-api"
|
||||
source.mkdir()
|
||||
@@ -439,6 +597,179 @@ def test_api_analysis_run_loop(tmp_path):
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_api_source_linked_candidate_and_repo_update_loop(tmp_path):
|
||||
source = tmp_path / "source-linked"
|
||||
source.mkdir()
|
||||
(source / "README.md").write_text(
|
||||
"# Source Linked\n\nProvides operational HTTP status checks.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "docs").mkdir()
|
||||
(source / "docs" / "usage.md").write_text(
|
||||
"# Usage\n\nCall the status endpoint before routing traffic.\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "examples").mkdir()
|
||||
(source / "examples" / "status_client.py").write_text(
|
||||
"print('GET /status')\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "tests").mkdir()
|
||||
(source / "tests" / "test_status.py").write_text(
|
||||
"def test_status(): pass\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
(source / "requirements.txt").write_text("fastapi\npytest\n", encoding="utf-8")
|
||||
app_file = source / "app.py"
|
||||
app_file.write_text(
|
||||
"from fastapi import FastAPI\n"
|
||||
"app = FastAPI()\n"
|
||||
'@app.get("/status")\n'
|
||||
"def status():\n"
|
||||
" return {'status': 'ok'}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
|
||||
def override_settings():
|
||||
return Settings(
|
||||
database_path=str(tmp_path / "source-linked.sqlite3"),
|
||||
checkout_root=str(tmp_path / "source-linked-checkouts"),
|
||||
)
|
||||
|
||||
app.dependency_overrides[get_settings] = override_settings
|
||||
client = TestClient(app)
|
||||
try:
|
||||
repository_response = client.post(
|
||||
"/repos",
|
||||
json={"name": "Source Linked", "url": str(source)},
|
||||
)
|
||||
assert repository_response.status_code == 201
|
||||
repository_id = repository_response.json()["id"]
|
||||
|
||||
first_run_response = client.post(
|
||||
f"/repos/{repository_id}/analysis-runs",
|
||||
json={},
|
||||
)
|
||||
assert first_run_response.status_code == 201
|
||||
first_run = first_run_response.json()
|
||||
first_run_id = first_run["analysis_run"]["id"]
|
||||
assert first_run["snapshot"]["file_count"] == 6
|
||||
|
||||
facts_response = client.get(f"/repos/{repository_id}/observed-facts")
|
||||
assert facts_response.status_code == 200
|
||||
facts = facts_response.json()
|
||||
fact_keys = {(fact["kind"], fact["name"], fact["path"]) for fact in facts}
|
||||
assert ("documentation", "README", "README.md") in fact_keys
|
||||
assert ("documentation", "usage.md", "docs/usage.md") in fact_keys
|
||||
assert ("example", "status_client.py", "examples/status_client.py") in fact_keys
|
||||
assert ("test", "test_status.py", "tests/test_status.py") in fact_keys
|
||||
assert ("framework", "FastAPI", "requirements.txt") in fact_keys
|
||||
assert ("interface", "python route decorator", "app.py") in fact_keys
|
||||
|
||||
candidate_response = client.get(
|
||||
f"/repos/{repository_id}/analysis-runs/{first_run_id}/candidate-graph"
|
||||
)
|
||||
assert candidate_response.status_code == 200
|
||||
candidate_graph = candidate_response.json()
|
||||
interface_capability = next(
|
||||
capability
|
||||
for capability in candidate_graph["abilities"][0]["capabilities"]
|
||||
if capability["name"] == "Expose Repository Interface"
|
||||
)
|
||||
feature = next(
|
||||
item
|
||||
for item in interface_capability["features"]
|
||||
if item["name"] == "GET /status"
|
||||
)
|
||||
assert feature["type"] == "API"
|
||||
assert feature["location"] == "app.py"
|
||||
assert feature["source_refs"][0]["path"] == "app.py"
|
||||
assert feature["source_refs"][0]["line"] == 3
|
||||
evidence_refs = {
|
||||
(evidence["type"], evidence["reference"], evidence["strength"])
|
||||
for evidence in interface_capability["evidence"]
|
||||
}
|
||||
assert ("test", "tests/test_status.py", "strong") in evidence_refs
|
||||
assert ("example", "examples/status_client.py", "strong") in evidence_refs
|
||||
assert ("documentation", "README.md", "medium") in evidence_refs
|
||||
|
||||
approve_response = client.post(
|
||||
f"/repos/{repository_id}/analysis-runs/{first_run_id}"
|
||||
"/candidate-graph/approve",
|
||||
json={"notes": "Approved source-linked e2e fixture"},
|
||||
)
|
||||
assert approve_response.status_code == 200
|
||||
approved_map = approve_response.json()
|
||||
approved_capability = next(
|
||||
capability
|
||||
for capability in approved_map["abilities"][0]["capabilities"]
|
||||
if capability["name"] == "Expose Repository Interface"
|
||||
)
|
||||
approved_feature = next(
|
||||
item
|
||||
for item in approved_capability["features"]
|
||||
if item["name"] == "GET /status"
|
||||
)
|
||||
assert approved_feature["source_refs"][0]["line"] == 3
|
||||
assert {
|
||||
evidence["reference"] for evidence in approved_capability["evidence"]
|
||||
} >= {"tests/test_status.py", "examples/status_client.py", "README.md"}
|
||||
|
||||
evidence_search = client.get(
|
||||
"/search",
|
||||
params={"q": "test_status.py", "status": "indexed"},
|
||||
)
|
||||
assert evidence_search.status_code == 200
|
||||
evidence_result = evidence_search.json()[0]
|
||||
assert evidence_result["match_type"] == "evidence"
|
||||
assert evidence_result["evidence_level"] == "strong"
|
||||
assert evidence_result["source_reference"] == "tests/test_status.py"
|
||||
|
||||
app_file.write_text(
|
||||
"from fastapi import FastAPI\n"
|
||||
"app = FastAPI()\n"
|
||||
'@app.get("/status")\n'
|
||||
"def status():\n"
|
||||
" return {'status': 'ok'}\n\n"
|
||||
'@app.get("/ready")\n'
|
||||
"def ready():\n"
|
||||
" return {'ready': True}\n",
|
||||
encoding="utf-8",
|
||||
)
|
||||
second_run_response = client.post(
|
||||
f"/repos/{repository_id}/analysis-runs",
|
||||
json={},
|
||||
)
|
||||
assert second_run_response.status_code == 201
|
||||
second_run_id = second_run_response.json()["analysis_run"]["id"]
|
||||
assert second_run_id != first_run_id
|
||||
|
||||
second_candidate_response = client.get(
|
||||
f"/repos/{repository_id}/analysis-runs/{second_run_id}/candidate-graph"
|
||||
)
|
||||
assert second_candidate_response.status_code == 200
|
||||
second_features = {
|
||||
feature["name"]
|
||||
for ability in second_candidate_response.json()["abilities"]
|
||||
for capability in ability["capabilities"]
|
||||
for feature in capability["features"]
|
||||
}
|
||||
assert {"GET /status", "GET /ready"} <= second_features
|
||||
|
||||
approved_after_reanalysis = client.get(f"/repos/{repository_id}/ability-map")
|
||||
assert approved_after_reanalysis.status_code == 200
|
||||
approved_features = {
|
||||
feature["name"]
|
||||
for ability in approved_after_reanalysis.json()["abilities"]
|
||||
for capability in ability["capabilities"]
|
||||
for feature in capability["features"]
|
||||
}
|
||||
assert "GET /status" in approved_features
|
||||
assert "GET /ready" not in approved_features
|
||||
finally:
|
||||
app.dependency_overrides.clear()
|
||||
|
||||
|
||||
def test_ui_register_analyze_and_approve_loop(tmp_path):
|
||||
source = tmp_path / "repo"
|
||||
source.mkdir()
|
||||
|
||||
Reference in New Issue
Block a user