repository-scoped dependency graph view profile persistence and interactive exploration features

This commit is contained in:
2026-05-04 11:26:25 +02:00
parent 98a65581ac
commit 4f04491734
11 changed files with 1365 additions and 32 deletions

View File

@@ -190,7 +190,37 @@ def test_openapi_contract_snapshot_for_stable_agent_paths():
"get": {"tags": ["registry"], "success_schema": "RepositoryAbilityMapResponse"}
},
"/repos/{repository_id}/dependency-graph": {
"get": {"tags": ["registry"], "success_schema": "object"}
"get": {"tags": ["visualization"], "success_schema": "object"}
},
"/repos/{repository_id}/dependency-graph/filter": {
"post": {"tags": ["visualization"], "success_schema": "object"}
},
"/repos/{repository_id}/dependency-graph/profiles": {
"get": {
"tags": ["visualization"],
"success_schema": "list[DependencyGraphProfileResponse]",
},
"post": {
"tags": ["visualization"],
"success_schema": "DependencyGraphProfileResponse",
},
},
"/repos/{repository_id}/dependency-graph/profiles/{profile_id}": {
"delete": {"tags": ["visualization"], "success_schema": None},
"get": {
"tags": ["visualization"],
"success_schema": "DependencyGraphProfileResponse",
},
"patch": {
"tags": ["visualization"],
"success_schema": "DependencyGraphProfileResponse",
},
},
"/repos/{repository_id}/dependency-graph/profiles/{profile_id}/duplicate": {
"post": {
"tags": ["visualization"],
"success_schema": "DependencyGraphProfileResponse",
}
},
"/repos/{repository_id}/analysis-runs": {
"get": {"tags": ["analysis"], "success_schema": "list[AnalysisRunResponse]"},
@@ -1504,17 +1534,58 @@ def test_ui_register_analyze_and_approve_loop(tmp_path):
assert graph_payload["mode"] == "full"
assert graph_payload["metrics"]["node_count"] >= 4
assert graph_payload["metrics"]["edge_count"] >= 3
assert graph_payload["filter"]["precedence"].startswith("later rules")
assert any(
element["data"].get("kind") == "scope"
for element in graph_payload["elements"]
if "source" not in element["data"]
)
assert all(
"layer" in element["data"]
for element in graph_payload["elements"]
)
profile_response = client.post(
f"/repos/{repository_id}/dependency-graph/profiles",
json={
"name": "Hide Facts",
"description": "Reduce deterministic fact noise.",
"default_mode": "full",
"filter_rules": [
{"name": "facts", "action": "hide", "match": {"layer": "fact"}}
],
"manual_overrides": {},
},
)
assert profile_response.status_code == 201
profile = profile_response.json()
filtered_response = client.get(
f"/repos/{repository_id}/dependency-graph",
params={"profile_id": profile["id"]},
)
assert filtered_response.status_code == 200
filtered_payload = filtered_response.json()
assert filtered_payload["profile"]["name"] == "Hide Facts"
assert filtered_payload["metrics"]["hidden_count"] >= 1
assert all(
element["data"].get("layer") != "fact"
for element in filtered_payload["elements"]
if "source" not in element["data"]
)
duplicate_response = client.post(
f"/repos/{repository_id}/dependency-graph/profiles/{profile['id']}/duplicate",
json={"name": "Hide Facts Copy"},
)
assert duplicate_response.status_code == 201
assert duplicate_response.json()["name"] == "Hide Facts Copy"
graph_page = client.get(f"/ui/repos/{repository_id}/dependency-graph")
assert graph_page.status_code == 200
assert "Dependency Graph" in graph_page.text
assert "cytoscape.min.js" in graph_page.text
assert 'data-graph-mode="impact"' in graph_page.text
assert 'id="profile-select"' in graph_page.text
assert 'data-override="blur"' in graph_page.text
scope_listing = client.get(
f"/ui/repos/{repository_id}/elements",