generated from coulomb/repo-seed
repository-scoped dependency graph view profile persistence and interactive exploration features
This commit is contained in:
@@ -263,6 +263,88 @@ def test_dependency_graph_flags_same_layer_edges(tmp_path):
|
||||
assert same_layer_edges[0].target_key == f"capability:{second_capability_id}"
|
||||
|
||||
|
||||
def test_dependency_graph_enriches_layers_and_filters_with_profiles(tmp_path):
|
||||
service = make_service(tmp_path)
|
||||
source = write_python_cli_repo(tmp_path)
|
||||
repository = service.register_repository(
|
||||
name="Graph Profile",
|
||||
url=str(source),
|
||||
description="Graph profile fixture.",
|
||||
)
|
||||
summary = service.analyze_repository(
|
||||
repository.id,
|
||||
source_path=str(source),
|
||||
use_llm_assistance=False,
|
||||
)
|
||||
fact = next(item for item in summary.facts if item.kind == "framework")
|
||||
source_ref = SourceReference(
|
||||
fact_id=fact.id,
|
||||
path=fact.path,
|
||||
kind=fact.kind,
|
||||
name=fact.name,
|
||||
)
|
||||
ability_id = service.add_ability(repository.id, name="Explore Graphs")
|
||||
capability_id = service.add_capability(
|
||||
repository.id,
|
||||
ability_id,
|
||||
name="Filter Dependency Graph",
|
||||
)
|
||||
feature_id = service.store.create_feature(
|
||||
repository.id,
|
||||
capability_id,
|
||||
name="Graph filter control",
|
||||
type="UI",
|
||||
location="src/ui.py",
|
||||
confidence=0.8,
|
||||
source_refs=[source_ref],
|
||||
)
|
||||
service.store.create_evidence(
|
||||
repository.id,
|
||||
capability_id,
|
||||
type="test",
|
||||
reference="tests/test_ui.py",
|
||||
strength="strong",
|
||||
target_kind="feature",
|
||||
target_id=feature_id,
|
||||
source_refs=[source_ref],
|
||||
)
|
||||
profile = service.create_dependency_graph_profile(
|
||||
repository.id,
|
||||
name="Evidence Audit",
|
||||
description="Blur non-evidence layers.",
|
||||
default_mode="full",
|
||||
filter_rules=[
|
||||
{"name": "blur facts", "action": "blur", "match": {"layer": "fact"}},
|
||||
{"name": "hide features", "action": "hide", "match": {"layer": "feature"}},
|
||||
],
|
||||
manual_overrides={f"feature:{feature_id}": "show", "missing:1": "hide"},
|
||||
)
|
||||
|
||||
payload = service.dependency_graph_elements(repository.id, profile_id=profile.id)
|
||||
|
||||
nodes = [
|
||||
element["data"]
|
||||
for element in payload["elements"]
|
||||
if "source" not in element["data"]
|
||||
]
|
||||
fact_node = next(node for node in nodes if node["kind"] == "fact")
|
||||
feature_node = next(node for node in nodes if node["id"] == f"feature:{feature_id}")
|
||||
evidence_node = next(node for node in nodes if node["kind"] == "evidence")
|
||||
assert fact_node["layer"] == "fact"
|
||||
assert fact_node["path"] == fact.path
|
||||
assert fact_node["displayState"] == "blur"
|
||||
assert feature_node["displayState"] == "show"
|
||||
assert feature_node["visibilitySource"] == "manual"
|
||||
assert evidence_node["layer"] == "evidence"
|
||||
assert payload["filter"]["orphaned_overrides"] == ["missing:1"]
|
||||
assert payload["metrics"]["hidden_count"] == 0
|
||||
assert any(
|
||||
element["data"].get("target") == f"feature:{feature_id}"
|
||||
and element["data"].get("sourceKind") == "evidence"
|
||||
for element in payload["elements"]
|
||||
)
|
||||
|
||||
|
||||
def test_manual_registry_updates_and_deletes_approved_entries(tmp_path):
|
||||
service = make_service(tmp_path)
|
||||
repository = service.register_repository(
|
||||
|
||||
@@ -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",
|
||||
|
||||
Reference in New Issue
Block a user