Files
cdsl-cad/backend/tests/test_feature_plan.py
T
2026-08-26 14:13:11 +08:00

69 lines
2.8 KiB
Python

from __future__ import annotations
import sys
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
sys.path.insert(0, str(ROOT / "backend"))
from app.services.feature_plan import FeaturePlanError, compute_node_statuses, validate_feature_plan # noqa: E402
class FeaturePlanTests(unittest.TestCase):
def plan(self) -> dict:
return {
"schema_version": "cad.feature-plan.v1",
"plan_id": "plan_test",
"task_id": "cad_test",
"nodes": [
{"id": "base", "atomic_id": "extrude_add_blind", "depends_on": []},
{"id": "round", "atomic_id": "fillet", "depends_on": ["base"], "requires_topology": True},
],
}
def test_rejects_cycles_and_missing_dependencies(self) -> None:
cyclic = self.plan()
cyclic["nodes"][0]["depends_on"] = ["round"]
with self.assertRaises(FeaturePlanError):
validate_feature_plan(cyclic, supported_atomic_ids={"extrude_add_blind", "fillet"})
missing = self.plan()
missing["nodes"][1]["depends_on"] = ["missing"]
with self.assertRaises(FeaturePlanError):
validate_feature_plan(missing, supported_atomic_ids={"extrude_add_blind", "fillet"})
def test_ready_prefix_waits_for_topology(self) -> None:
statuses = compute_node_statuses(self.plan(), cdsl={"features": []})
self.assertEqual(statuses["ready_nodes"], ["base"])
self.assertEqual(statuses["waiting_nodes"], [])
def test_built_prefix_unlocks_topology_dependent_node(self) -> None:
statuses = compute_node_statuses(
self.plan(),
cdsl={"features": [{"id": "base"}]},
topology={"records": [{"record_id": "body:base", "kind": "body", "executable": True}]},
)
self.assertEqual(statuses["completed_nodes"], ["base"])
self.assertEqual(statuses["waiting_nodes"], ["round"])
def test_inspected_snapshot_unlocks_topology_dependent_node(self) -> None:
plan = self.plan()
plan["topology_snapshot_id"] = "cad_test/rev_001"
statuses = compute_node_statuses(
plan,
cdsl={"features": [{"id": "base"}]},
topology={
"snapshot_id": "cad_test/rev_001",
"records": [{"record_id": "body:base", "kind": "body", "executable": True}],
},
)
self.assertEqual(statuses["ready_nodes"], ["round"])
self.assertEqual(statuses["waiting_nodes"], [])
def test_dependency_must_appear_before_dependent_node(self) -> None:
plan = self.plan()
plan["nodes"] = [plan["nodes"][1], plan["nodes"][0]]
with self.assertRaisesRegex(FeaturePlanError, "appear after dependency"):
validate_feature_plan(plan, supported_atomic_ids={"extrude_add_blind", "fillet"})