""" Test Condition Parsing - Alle Formate und Verschachtelungen Testet ob Pydantic die verschiedenen Condition-Formate korrekt deserialisiert. """ import sys import os # Force UTF-8 encoding on Windows if sys.platform == 'win32': import io sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8', errors='replace') sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding='utf-8', errors='replace') sys.path.insert(0, 'backend') from workflow_models import LogicExpression, Condition, WorkflowNode, LogicOperator from pydantic import ValidationError import json def test_case(name, data, expected_type, should_fail=False): """Test a single condition format""" print(f"\n{'='*60}") print(f"TEST: {name}") print(f"{'='*60}") print(f"Input: {json.dumps(data, indent=2)}") try: # Test 1: Direct deserialization if expected_type == LogicExpression: result = LogicExpression(**data) elif expected_type == Condition: result = Condition(**data) if should_fail: print("❌ FAILED: Should have raised ValidationError but didn't") return False print(f"✅ PASSED: Deserialized as {type(result).__name__}") print(f"Result: {result.model_dump()}") # Test 2: As part of WorkflowNode node_data = { "id": "test_node", "type": "logic", "condition": data } node = WorkflowNode(**node_data) print(f"✅ PASSED: WorkflowNode.condition type: {type(node.condition).__name__}") return True except ValidationError as e: if should_fail: print(f"✅ PASSED: Correctly raised ValidationError") return True else: print(f"❌ FAILED: {e}") return False except Exception as e: print(f"❌ FAILED: Unexpected error: {e}") import traceback traceback.print_exc() return False # ============================================================================ # Test Cases # ============================================================================ test_results = [] # Test 1: Simple comparison (UI format - einfachster Fall) test_results.append(test_case( "Simple comparison (eq)", { "operator": "eq", "ref": "node_1.q1", "value": "ja" }, LogicExpression )) # Test 2: Simple AND (UI format - wie im Workflow) test_results.append(test_case( "Simple AND with 2 operands", { "operator": "and", "operands": [ {"operator": "eq", "ref": "node_5.qTiefananalyseRecovery", "value": "ja"}, {"operator": "neq", "ref": "node_6.qKonsistenz", "value": "nein"} ] }, LogicExpression )) # Test 3: Simple OR test_results.append(test_case( "Simple OR with 3 operands", { "operator": "or", "operands": [ {"operator": "eq", "ref": "node_1.q1", "value": "ja"}, {"operator": "eq", "ref": "node_1.q2", "value": "nein"}, {"operator": "eq", "ref": "node_1.q3", "value": "unklar"} ] }, LogicExpression )) # Test 4: Nested AND/OR test_results.append(test_case( "Nested: OR with nested AND", { "operator": "or", "operands": [ { "operator": "and", "operands": [ {"operator": "eq", "ref": "node_1.q1", "value": "ja"}, {"operator": "neq", "ref": "node_1.q2", "value": "nein"} ] }, {"operator": "eq", "ref": "node_2.q1", "value": "hoch"} ] }, LogicExpression )) # Test 5: Deep nesting (3 levels) test_results.append(test_case( "Deep nesting (3 levels)", { "operator": "and", "operands": [ { "operator": "or", "operands": [ {"operator": "eq", "ref": "node_1.q1", "value": "ja"}, { "operator": "and", "operands": [ {"operator": "eq", "ref": "node_2.q1", "value": "hoch"}, {"operator": "neq", "ref": "node_2.q2", "value": "niedrig"} ] } ] }, {"operator": "eq", "ref": "node_3.q1", "value": "aktiv"} ] }, LogicExpression )) # Test 6: Different operators test_results.append(test_case( "Different comparison operators (gt, lt, in)", { "operator": "and", "operands": [ {"operator": "gt", "ref": "node_1.score", "value": 50}, {"operator": "lt", "ref": "node_1.score", "value": 100}, {"operator": "in", "ref": "node_1.category", "value": ["high", "medium"]} ] }, LogicExpression )) # Test 7: Legacy format (wrapped in Condition) test_results.append(test_case( "Legacy format: Condition with expression", { "type": "if", "expression": { "operator": "eq", "ref": "node_1.q1", "value": "ja" }, "then_path": "edge_1", "else_path": "edge_2" }, Condition )) # Test 8: NOT operator test_results.append(test_case( "NOT operator", { "operator": "not", "operands": [ {"operator": "eq", "ref": "node_1.q1", "value": "nein"} ] }, LogicExpression )) # Test 9: Complex real-world scenario test_results.append(test_case( "Complex real-world: Multiple nested conditions", { "operator": "and", "operands": [ { "operator": "or", "operands": [ {"operator": "eq", "ref": "node_1.relevance", "value": "high"}, { "operator": "and", "operands": [ {"operator": "eq", "ref": "node_1.relevance", "value": "medium"}, {"operator": "gt", "ref": "node_1.priority", "value": 5} ] } ] }, {"operator": "neq", "ref": "node_2.status", "value": "blocked"}, { "operator": "in", "ref": "node_3.category", "value": ["training", "nutrition", "recovery"] } ] }, LogicExpression )) # ============================================================================ # Results Summary # ============================================================================ print("\n" + "="*60) print("TEST RESULTS SUMMARY") print("="*60) passed = sum(test_results) total = len(test_results) failed = total - passed print(f"\n✅ Passed: {passed}/{total}") if failed > 0: print(f"❌ Failed: {failed}/{total}") print(f"\n⚠️ CRITICAL: Some tests failed! Do NOT deploy until fixed.") sys.exit(1) else: print(f"\n🎉 All tests passed! Safe to deploy.") sys.exit(0)