generated from coulomb/repo-seed
API contract hardening
This commit is contained in:
@@ -52,6 +52,235 @@ def test_openapi_groups_agent_facing_endpoints():
|
||||
assert (
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/changes/approve"
|
||||
) in schema["paths"]
|
||||
assert components["ErrorResponse"]["examples"][0]["detail"]
|
||||
assert (
|
||||
schema["paths"]["/repos/{repository_id}"]["get"]["responses"]["404"][
|
||||
"content"
|
||||
]["application/json"]["schema"]["$ref"]
|
||||
).endswith("/ErrorResponse")
|
||||
assert (
|
||||
schema["paths"][
|
||||
"/repos/{repository_id}/analysis-runs/{base_analysis_run_id}/diff/"
|
||||
"{target_analysis_run_id}"
|
||||
]["get"]["responses"]["200"]["content"]["application/json"]["schema"]["$ref"]
|
||||
).endswith("/AnalysisRunDiffResponse")
|
||||
assert (
|
||||
components["RepositoryComparisonResponse"]["examples"][0][
|
||||
"unique_capabilities"
|
||||
][0]["capability_name"]
|
||||
== "Classify Incoming Email"
|
||||
)
|
||||
assert (
|
||||
components["CapabilityGapResponse"]["examples"][0]["missing_capabilities"][0]
|
||||
== "Route Email to Team"
|
||||
)
|
||||
|
||||
|
||||
def test_openapi_contract_snapshot_for_stable_agent_paths():
|
||||
client = TestClient(app)
|
||||
|
||||
schema = client.get("/openapi.json").json()
|
||||
|
||||
def success_schema(operation):
|
||||
responses = operation["responses"]
|
||||
status_code = "201" if "201" in responses else "200"
|
||||
content = responses.get(status_code, {}).get("content", {})
|
||||
if "application/json" in content:
|
||||
response_schema = content["application/json"]["schema"]
|
||||
if "$ref" in response_schema:
|
||||
return response_schema["$ref"].split("/")[-1]
|
||||
if response_schema.get("type") == "array":
|
||||
items = response_schema["items"]
|
||||
if "$ref" in items:
|
||||
return f"list[{items['$ref'].split('/')[-1]}]"
|
||||
return "list"
|
||||
return response_schema.get("type")
|
||||
if "application/x-yaml" in content:
|
||||
return "application/x-yaml"
|
||||
return None
|
||||
|
||||
stable_contract = {
|
||||
path: {
|
||||
method: {
|
||||
"tags": operation["tags"],
|
||||
"success_schema": success_schema(operation),
|
||||
}
|
||||
for method, operation in sorted(methods.items())
|
||||
}
|
||||
for path, methods in sorted(schema["paths"].items())
|
||||
if not path.startswith("/ui")
|
||||
}
|
||||
|
||||
assert stable_contract == {
|
||||
"/abilities": {
|
||||
"get": {"tags": ["search"], "success_schema": "list[AbilitySummaryResponse]"}
|
||||
},
|
||||
"/capabilities": {
|
||||
"get": {
|
||||
"tags": ["search"],
|
||||
"success_schema": "list[CapabilitySummaryResponse]",
|
||||
}
|
||||
},
|
||||
"/capability-gaps": {
|
||||
"post": {"tags": ["discovery"], "success_schema": "CapabilityGapResponse"}
|
||||
},
|
||||
"/health": {"get": {"tags": ["health"], "success_schema": "object"}},
|
||||
"/repos": {
|
||||
"get": {"tags": ["repositories"], "success_schema": "list[RepositoryResponse]"},
|
||||
"post": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||
},
|
||||
"/repos/{repository_id}": {
|
||||
"delete": {"tags": ["repositories"], "success_schema": None},
|
||||
"get": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||
"patch": {"tags": ["repositories"], "success_schema": "RepositoryResponse"},
|
||||
},
|
||||
"/repos/{repository_id}/abilities": {
|
||||
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/abilities/{ability_id}": {
|
||||
"delete": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
"patch": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
},
|
||||
"/repos/{repository_id}/ability-map": {
|
||||
"get": {"tags": ["registry"], "success_schema": "RepositoryAbilityMapResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs": {
|
||||
"get": {"tags": ["analysis"], "success_schema": "list[AnalysisRunResponse]"},
|
||||
"post": {"tags": ["analysis"], "success_schema": "ScanSummaryResponse"},
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}": {
|
||||
"get": {"tags": ["analysis"], "success_schema": "AnalysisRunResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph": {
|
||||
"get": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-graph/approve": {
|
||||
"post": {
|
||||
"tags": ["review"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/changes/approve": {
|
||||
"post": {
|
||||
"tags": ["review"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/content-chunks": {
|
||||
"get": {"tags": ["analysis"], "success_schema": "list[ContentChunkResponse]"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/review-decisions": {
|
||||
"get": {"tags": ["review"], "success_schema": "list[ReviewDecisionResponse]"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{base_analysis_run_id}/diff/{target_analysis_run_id}": {
|
||||
"get": {"tags": ["review"], "success_schema": "AnalysisRunDiffResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{candidate_ability_id}": {
|
||||
"patch": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{candidate_ability_id}/reject": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-abilities/{source_ability_id}/merge": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}": {
|
||||
"patch": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}/reject": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{candidate_capability_id}/relink": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-capabilities/{source_capability_id}/merge": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{candidate_evidence_id}/reject": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{candidate_evidence_id}/relink": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-evidence/{source_evidence_id}/merge": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{candidate_feature_id}/reject": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{candidate_feature_id}/relink": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/analysis-runs/{analysis_run_id}/candidate-features/{source_feature_id}/merge": {
|
||||
"post": {"tags": ["review"], "success_schema": "CandidateGraphResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/capabilities": {
|
||||
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/capabilities/{capability_id}": {
|
||||
"delete": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
"patch": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
},
|
||||
"/repos/{repository_id}/content-chunks": {
|
||||
"get": {"tags": ["analysis"], "success_schema": "list[ContentChunkResponse]"}
|
||||
},
|
||||
"/repos/{repository_id}/evidence": {
|
||||
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/evidence/{evidence_id}": {
|
||||
"delete": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
"patch": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
},
|
||||
"/repos/{repository_id}/export": {
|
||||
"get": {"tags": ["discovery"], "success_schema": "application/x-yaml"}
|
||||
},
|
||||
"/repos/{repository_id}/features": {
|
||||
"post": {"tags": ["registry"], "success_schema": "IdResponse"}
|
||||
},
|
||||
"/repos/{repository_id}/features/{feature_id}": {
|
||||
"delete": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
"patch": {
|
||||
"tags": ["registry"],
|
||||
"success_schema": "RepositoryAbilityMapResponse",
|
||||
},
|
||||
},
|
||||
"/repos/{repository_id}/observed-facts": {
|
||||
"get": {"tags": ["analysis"], "success_schema": "list[ObservedFactResponse]"}
|
||||
},
|
||||
"/repos/{repository_id}/review-decisions": {
|
||||
"get": {"tags": ["review"], "success_schema": "list[ReviewDecisionResponse]"}
|
||||
},
|
||||
"/repository-comparisons": {
|
||||
"get": {
|
||||
"tags": ["discovery"],
|
||||
"success_schema": "RepositoryComparisonResponse",
|
||||
}
|
||||
},
|
||||
"/search": {
|
||||
"get": {"tags": ["search"], "success_schema": "list[SearchResultResponse]"}
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def test_docs_endpoint_is_available():
|
||||
|
||||
Reference in New Issue
Block a user