fix: update tests to match action node schema (next_node_id, not children)

- Update _make_valid_tree() in test_ai_tree_validator to use next_node_id
  on action nodes (solution is a sibling, not a child)
- Fix test_dead_end_action_node → test_dead_end_decision_node (action nodes
  don't have child-based dead ends; dead ends are decision nodes with no children)
- Add test_action_missing_next_node_id for the new validation rule
- Update BRANCH_DETAIL_JSON in test_ai_endpoints to use next_node_id pattern
- Update test_draft_trees.py to use "title" field for action/solution nodes
  (tree_validation.py was updated this branch to require "title" not "action"/"solution")

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
chihlasm
2026-02-22 23:18:10 -05:00
parent 339486f555
commit 5acf94b6c2
3 changed files with 59 additions and 52 deletions

View File

@@ -5,7 +5,11 @@ from app.core.ai_tree_validator import validate_generated_tree, count_tree_stats
def _make_valid_tree():
"""Helper: minimal valid tree for testing."""
"""Helper: minimal valid tree for testing.
Action nodes use next_node_id to point to a sibling (not children).
The solution following an action is a sibling under the parent decision.
"""
return {
"id": "root",
"type": "decision",
@@ -44,14 +48,13 @@ def _make_valid_tree():
"title": "Restart the Service",
"description": "Restart the service and verify.",
"commands": ["Restart-Service -Name 'TestService'"],
"children": [
{
"id": "service-resolved",
"type": "solution",
"title": "Service Restored",
"description": "Service is running after restart.",
},
],
"next_node_id": "service-resolved",
},
{
"id": "service-resolved",
"type": "solution",
"title": "Service Restored",
"description": "Service is running after restart.",
},
],
}
@@ -107,6 +110,12 @@ class TestNodeValidation:
errors = validate_generated_tree(tree)
assert any("at least 2 options" in e for e in errors)
def test_action_missing_next_node_id(self):
tree = _make_valid_tree()
del tree["children"][1]["next_node_id"]
errors = validate_generated_tree(tree)
assert any("missing 'next_node_id'" in e for e in errors)
class TestReferenceIntegrity:
def test_option_references_nonexistent_child(self):
@@ -156,18 +165,18 @@ class TestGlobalChecks:
{"id": "o1", "label": "A", "next_node_id": "only-solution"},
{"id": "o2", "label": "B", "next_node_id": "only-solution"},
]
# Now restart-service branch has 1 solution, check-logs has 1 = total 2
# Remove one more to get to 1
tree["children"][1]["children"] = []
# Remove the solution that restart-service points to
tree["children"].pop(2) # remove service-resolved
errors = validate_generated_tree(tree)
assert any("solution" in e.lower() for e in errors)
class TestDeadEndDetection:
def test_dead_end_action_node(self):
def test_dead_end_decision_node(self):
"""A decision node with no children is a dead end."""
tree = _make_valid_tree()
# Remove restart-service's children — becomes dead end
tree["children"][1]["children"] = []
# Remove children from check-logs decision node — becomes dead end
tree["children"][0]["children"] = []
errors = validate_generated_tree(tree)
assert any("dead end" in e for e in errors)