test_workflow_designer_debugger.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. from __future__ import annotations
  2. from tests.conftest import build_fastapi_test_client, prepare_known_service_import
  3. prepare_known_service_import("workflow-service")
  4. from app.bootstrap.app import create_app
  5. from app.api.routes import get_workflow_application_service
  6. from app.application.designer import WorkflowDebugPlan, build_debug_plan
  7. def test_workflow_designer_validate_returns_graph_diagnostics() -> None:
  8. client = build_fastapi_test_client(create_app())
  9. response = client.post(
  10. "/workflows/designer/validate",
  11. json={
  12. "dsl_json": {
  13. "code": "order_flow",
  14. "name": "Order Flow",
  15. "nodes": [
  16. {"id": "start", "type": "llm", "name": "Start"},
  17. {"id": "answer", "type": "answer", "name": "Answer"},
  18. {"id": "orphan", "type": "tool", "name": "Orphan Tool"},
  19. ],
  20. "edges": [{"source": "start", "target": "answer"}],
  21. }
  22. },
  23. )
  24. assert response.status_code == 200
  25. payload = response.json()
  26. assert payload["valid"] is True
  27. assert payload["node_count"] == 3
  28. assert payload["edge_count"] == 1
  29. assert payload["entry_node_ids"] == ["start", "orphan"]
  30. assert payload["terminal_node_ids"] == ["answer", "orphan"]
  31. assert payload["isolated_node_ids"] == ["orphan"]
  32. assert payload["unreachable_node_ids"] == []
  33. assert payload["diagnostics"][0]["code"] == "workflow.multiple_entry_nodes"
  34. assert payload["normalized_dsl_json"]["nodes"][0]["config"] == {}
  35. def test_workflow_designer_validate_reports_blocking_errors() -> None:
  36. client = build_fastapi_test_client(create_app())
  37. response = client.post(
  38. "/workflows/designer/validate",
  39. json={
  40. "dsl_json": {
  41. "code": "broken_flow",
  42. "nodes": [{"id": "start", "type": "llm"}],
  43. "edges": [{"source": "start", "target": "missing"}],
  44. }
  45. },
  46. )
  47. assert response.status_code == 200
  48. payload = response.json()
  49. assert payload["valid"] is False
  50. assert payload["edges"][0]["valid_source"] is True
  51. assert payload["edges"][0]["valid_target"] is False
  52. assert any(item["code"] == "edge.target_missing" for item in payload["diagnostics"])
  53. def test_workflow_debugger_returns_execution_preview() -> None:
  54. client = build_fastapi_test_client(create_app())
  55. response = client.post(
  56. "/workflows/designer/debug",
  57. json={
  58. "dsl_json": {
  59. "code": "debug_flow",
  60. "nodes": [
  61. {"id": "start", "type": "llm"},
  62. {"id": "tool", "type": "tool"},
  63. {"id": "answer", "type": "answer"},
  64. ],
  65. "edges": [
  66. {"source": "start", "target": "tool"},
  67. {"source": "tool", "target": "answer"},
  68. ],
  69. },
  70. "max_preview_steps": 10,
  71. },
  72. )
  73. assert response.status_code == 200
  74. payload = response.json()
  75. assert payload["valid"] is True
  76. assert payload["truncated"] is False
  77. assert [item["node_id"] for item in payload["execution_preview"]] == [
  78. "start",
  79. "tool",
  80. "answer",
  81. ]
  82. assert payload["execution_preview"][0]["next_node_ids"] == ["tool"]
  83. class FakeWorkflowDebugService:
  84. def build_version_debug_plan(
  85. self,
  86. *,
  87. tenant_id: str,
  88. workflow_version_id: str,
  89. max_preview_steps: int,
  90. ) -> WorkflowDebugPlan | None:
  91. assert tenant_id == "t1"
  92. assert workflow_version_id == "version-1"
  93. return build_debug_plan(
  94. {
  95. "code": "persisted_flow",
  96. "nodes": [
  97. {"id": "start", "type": "llm"},
  98. {"id": "answer", "type": "answer"},
  99. ],
  100. "edges": [{"source": "start", "target": "answer"}],
  101. },
  102. max_preview_steps=max_preview_steps,
  103. )
  104. def test_workflow_version_debugger_api_uses_persisted_dsl_service() -> None:
  105. app = create_app()
  106. app.dependency_overrides[get_workflow_application_service] = lambda: FakeWorkflowDebugService()
  107. client = build_fastapi_test_client(app)
  108. debug_response = client.get(
  109. "/workflows/versions/version-1/debug",
  110. params={"tenant_id": "t1"},
  111. )
  112. assert debug_response.status_code == 200
  113. debug_payload = debug_response.json()
  114. assert debug_payload["valid"] is True
  115. assert [item["node_id"] for item in debug_payload["execution_preview"]] == [
  116. "start",
  117. "answer",
  118. ]