Artifex/tests/test_control_plane_ui_v1.py
2026-08-15 20:56:56 +07:00

123 lines
8.5 KiB
Python

from __future__ import annotations
from django.test import Client
from django.urls import reverse
from agents.control_room import AgentControlRoomService
from control_plane.agents.management.commands.seed_core_agents import Command as SeedAgentsCommand
from control_plane.agents.models import AgentRole, AgentVersion, PromotionStatus, ProgenySignal
from control_plane.events.bus import EventBus
from control_plane.events.models import EventType
from control_plane.projects.models import Exploration, ExplorationOpportunity, Project, ProjectPlan, Milestone, RoadmapItem, Scenario, ScenarioFinding, ScenarioRun, ScenarioSuite, StewardFinding, Task, TaskAttempt, TaskStatus
from control_plane.resources.models import Resource, ResourceKind
from control_plane.verification.models import Review, TestRun, Verification, VerificationLevel, VerificationResult
from graph.bootstrap import champion_project_exploration_graph_v1, champion_task_execution_graph_v1
from graph.models import GraphApproval, GraphRun, GraphRunStatus
def fixture() -> dict[str, object]:
SeedAgentsCommand().handle()
AgentControlRoomService().bootstrap_frontend_agents()
team = AgentControlRoomService().create_software_feature_team()
Resource.objects.create(name="Qwen", kind=ResourceKind.MODEL, provider="local_inference", roles=["CODING"], health_status="AVAILABLE", config={"model": "qwen38"})
project = Project.objects.create(name="UI Dogfood", goal="Operate Artifex through the UI", repository_path="/tmp/ui")
plan = ProjectPlan.objects.create(project=project, version=1, goal=project.goal, scope="UI V1")
milestone = Milestone.objects.create(project=project, plan=plan, key="UI", title="UI", goal="Build UI")
task = Task.objects.create(project=project, milestone=milestone, task_type="implementation", status=TaskStatus.COMPLETE, goal="Render dashboard", acceptance_criteria=["loads"])
coder = AgentVersion.objects.get(agent__role=AgentRole.CODER, promotion_status=PromotionStatus.CHAMPION)
reviewer = AgentVersion.objects.get(agent__role=AgentRole.REVIEWER, promotion_status=PromotionStatus.CHAMPION)
judge = AgentVersion.objects.get(agent__role=AgentRole.PROJECT_JUDGE, promotion_status=PromotionStatus.CHAMPION)
TaskAttempt.objects.create(task=task, attempt_number=1, coder=coder, status="COMPLETE")
TestRun.objects.create(project=project, task=task, command="pytest", status="PASS")
Review.objects.create(task=task, reviewer=reviewer, status="PASS")
Verification.objects.create(project=project, task=task, judge=judge, level=VerificationLevel.TASK, result=VerificationResult.PASS)
graph_version = champion_task_execution_graph_v1()
graph_run = GraphRun.objects.create(project=project, task=task, execution_graph_version=graph_version, current_node="review", status=GraphRunStatus.PAUSED, metadata={"telemetry": {}})
node_run = graph_run.node_runs.create(node_id="review", node_type="review", status="PAUSED", visit_index=1, agent_version=reviewer, output_metadata={"review_status": "RUNNING"})
approval = GraphApproval.objects.create(graph_run=graph_run, node_run=node_run, reason="AWAITING_UI_APPROVAL")
exploration = Exploration.objects.create(project=project, status="COMPLETE")
opportunity = ExplorationOpportunity.objects.create(exploration=exploration, project=project, title="Add status filter", description="Filter dashboard cards", opportunity_type="UX", recommended_action="EXTEND", value_score=0.8, effort_score=0.2, risk_score=0.2, confidence=0.8, technical_fit=0.8, strategic_fit=0.8, composite_score=0.75, grouping_key="ui-opp")
roadmap_item = RoadmapItem.objects.create(project=project, title="Improve graph viewer", description="Expose node details", horizon="NOW", target_action="EXTEND", grouping_key="ui-roadmap")
suite = ScenarioSuite.objects.create(project=project, name="UI Suite")
scenario = Scenario.objects.create(project=project, suite=suite, name="Permission denial", title="Permission denial", scenario_type="PERMISSION", target_type="PROJECT", target_id=str(project.id), injected_condition={"mechanism": "permission_denial"}, expected_invariants=["safe"], success_criteria=["pass"], resource_budget={"max_seconds": 5})
run = ScenarioRun.objects.create(project=project, scenario=scenario, result="FAIL", status="COMPLETE", failure_evidence={"summary": "denied"})
finding = ScenarioFinding.objects.create(project=project, scenario=scenario, scenario_run=run, title="Scenario failed", summary="permission denied", failure_category="PERMISSION", recommended_action="EXTEND", recommended_route="RoadmapItem", grouping_key="ui-scenario")
StewardFinding.objects.create(project=project, finding_type="RELIABILITY", title="Open issue", summary="Needs attention", severity="HIGH", confidence=0.8, recommended_action="REPAIR", grouping_key="ui-steward")
ProgenySignal.objects.create(project=project, agent_version=coder, source="ui", severity="MEDIUM", failure_category="MALFORMED", summary="Signal", grouping_key="ui-signal")
EventBus().publish(EventType.TASK_COMPLETED, project=project, task=task, actor="test")
return {"project": project, "task": task, "graph_run": graph_run, "approval": approval, "opportunity": opportunity, "roadmap_item": roadmap_item, "finding": finding, "coder": coder, "team": team}
def assert_ok(client: Client, url: str, text: str) -> None:
response = client.get(url)
assert response.status_code == 200
assert text.encode() in response.content
def test_control_plane_core_pages_render_and_link() -> None:
data = fixture()
client = Client()
project = data["project"]
task = data["task"]
graph_run = data["graph_run"]
coder = data["coder"]
assert_ok(client, reverse("dashboard"), "Dashboard")
assert_ok(client, reverse("projects"), "UI Dogfood")
assert_ok(client, reverse("project_workspace", args=[project.id]), "Project DAG")
assert_ok(client, reverse("project_brain", args=[project.id]), "Project Brain")
assert_ok(client, reverse("project_archaeologist", args=[project.id]), "Archaeologist")
assert_ok(client, reverse("graph_run_detail", args=[graph_run.id]), "Live execution graph viewer")
assert_ok(client, reverse("task_detail", args=[task.id]), "Acceptance")
assert_ok(client, reverse("steward"), "Open issue")
assert_ok(client, reverse("explore"), "Add status filter")
assert_ok(client, reverse("roadmap"), "Improve graph viewer")
assert_ok(client, reverse("scenarios"), "Permission denial")
assert_ok(client, reverse("progeny"), "Signal")
assert_ok(client, reverse("agent_control_room"), "Agent Control Room")
assert_ok(client, reverse("agent_detail", args=[coder.id]), "Competencies")
assert_ok(client, reverse("resources"), "Qwen")
assert_ok(client, reverse("approvals"), "AWAITING_UI_APPROVAL")
assert_ok(client, reverse("activity"), "TASK_COMPLETED")
def test_json_endpoints_and_ui_actions_work() -> None:
data = fixture()
client = Client()
project = data["project"]
graph_run = data["graph_run"]
approval = data["approval"]
opportunity = data["opportunity"]
roadmap_item = data["roadmap_item"]
finding = data["finding"]
assert client.get(reverse("graph_run_json", args=[graph_run.id])).json()["status"] == "PAUSED"
assert client.get(reverse("project_dag_json", args=[project.id])).json()["tasks"]
assert client.get(reverse("agent_performance_json", args=[data["coder"].id])).json()["quality"]
response = client.post(reverse("opportunity_action", args=[opportunity.id]), {"action": "roadmap"})
assert response.status_code == 302
assert project.roadmap_items.filter(title="Add status filter").exists()
response = client.post(reverse("roadmap_action", args=[roadmap_item.id]), {"action": "NEXT"})
assert response.status_code == 302
roadmap_item.refresh_from_db()
assert roadmap_item.horizon == "NEXT"
response = client.post(reverse("scenario_finding_action", args=[finding.id]), {"action": "roadmap"})
assert response.status_code == 302
response = client.post(reverse("approval_action", args=[approval.id]), {"action": "approve"})
assert response.status_code == 302
approval.refresh_from_db()
graph_run.refresh_from_db()
assert approval.status == "APPROVED"
assert graph_run.status == "RUNNING"
def test_ui_can_start_safe_explore_graph() -> None:
data = fixture()
client = Client()
response = client.post(reverse("run_explore", args=[data["project"].id]))
assert response.status_code == 302
assert GraphRun.objects.filter(execution_graph_version__graph__name="project_exploration", project=data["project"]).exists()