| """ |
| Mobile API Workflow Execution Tests |
| |
| Tests for mobile API workflow execution endpoints: |
| - Workflow creation |
| - Workflow skill addition |
| - Workflow execution |
| - Workflow DAG validation |
| - Workflow execution history |
| |
| All tests use API-first approach with TestClient (no browser). |
| Response structure matches web API for consistency. |
| """ |
|
|
| import pytest |
| from fastapi.testclient import TestClient |
|
|
|
|
| class TestMobileWorkflowCreate: |
| """Test mobile workflow creation endpoint""" |
|
|
| def test_mobile_workflow_create(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test workflow creation""" |
| response = mobile_api_client.post( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Mobile Test Workflow", |
| "description": "Test workflow from mobile API", |
| "category": "testing" |
| } |
| ) |
|
|
| |
| if response.status_code in [200, 201]: |
| data = response.json() |
|
|
| |
| assert "workflow_id" in data or "id" in data |
|
|
| |
| workflow_id = data.get("workflow_id") or data.get("id") |
| assert workflow_id is not None |
|
|
| |
| assert "name" in data or data.get("workflow", {}).get("name") |
| elif response.status_code == 404: |
| pytest.skip("Workflow creation endpoint not implemented") |
|
|
| def test_mobile_workflow_create_invalid(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test workflow creation with invalid data""" |
| response = mobile_api_client.post( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "", |
| "description": "Test" |
| } |
| ) |
|
|
| |
| assert response.status_code in [400, 422] |
|
|
| def test_mobile_workflow_create_unauthorized(self, mobile_api_client: TestClient): |
| """Test workflow creation without authentication""" |
| response = mobile_api_client.post( |
| "/api/v1/workflows", |
| json={ |
| "name": "Unauthorized Workflow", |
| "description": "Should fail" |
| } |
| ) |
|
|
| |
| assert response.status_code == 401 |
|
|
|
|
| class TestMobileWorkflowAddSkill: |
| """Test mobile workflow skill management""" |
|
|
| def test_mobile_workflow_add_skill(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test adding skill to workflow""" |
| |
| create_response = mobile_api_client.post( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Test Workflow for Skills", |
| "description": "Testing skill addition" |
| } |
| ) |
|
|
| if create_response.status_code not in [200, 201]: |
| pytest.skip("Workflow creation not available") |
|
|
| create_data = create_response.json() |
| workflow_id = create_data.get("workflow_id") or create_data.get("id") |
|
|
| if not workflow_id: |
| pytest.skip("Workflow ID not returned") |
|
|
| |
| response = mobile_api_client.post( |
| f"/api/v1/workflows/{workflow_id}/skills", |
| headers=mobile_auth_headers, |
| json={ |
| "skill_id": "test_skill", |
| "order": 1, |
| "parameters": {"test": "value"} |
| } |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
| assert "success" in data or "skill_id" in data |
|
|
| |
| get_response = mobile_api_client.get( |
| f"/api/v1/workflows/{workflow_id}", |
| headers=mobile_auth_headers |
| ) |
|
|
| if get_response.status_code == 200: |
| workflow_data = get_response.json() |
| |
| assert "skills" in workflow_data or "nodes" in workflow_data |
| elif response.status_code == 404: |
| pytest.skip("Workflow skill endpoint not implemented") |
|
|
| def test_mobile_workflow_list_skills(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test listing workflow skills""" |
| |
| response = mobile_api_client.get( |
| "/api/v1/workflows/test_workflow_id/skills", |
| headers=mobile_auth_headers |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
| assert "skills" in data or isinstance(data, list) |
| elif response.status_code == 404: |
| |
| pass |
|
|
|
|
| class TestMobileWorkflowExecute: |
| """Test mobile workflow execution endpoint""" |
|
|
| def test_mobile_workflow_execute(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test workflow execution""" |
| |
| create_response = mobile_api_client.post( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Executable Test Workflow", |
| "description": "Testing workflow execution" |
| } |
| ) |
|
|
| if create_response.status_code not in [200, 201]: |
| pytest.skip("Workflow creation not available") |
|
|
| create_data = create_response.json() |
| workflow_id = create_data.get("workflow_id") or create_data.get("id") |
|
|
| if not workflow_id: |
| pytest.skip("Workflow ID not returned") |
|
|
| |
| response = mobile_api_client.post( |
| f"/api/v1/workflows/{workflow_id}/execute", |
| headers=mobile_auth_headers, |
| json={ |
| "input": "test input data" |
| } |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
|
|
| |
| assert "execution_id" in data or "id" in data |
|
|
| |
| assert "status" in data or data.get("success") is True |
| elif response.status_code == 404: |
| pytest.skip("Workflow execution endpoint not implemented") |
|
|
| def test_mobile_workflow_execute_with_parameters(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test workflow execution with custom parameters""" |
| |
| create_response = mobile_api_client.post( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Parameterized Workflow", |
| "description": "Testing parameter passing" |
| } |
| ) |
|
|
| if create_response.status_code not in [200, 201]: |
| pytest.skip("Workflow creation not available") |
|
|
| create_data = create_response.json() |
| workflow_id = create_data.get("workflow_id") or create_data.get("id") |
|
|
| if not workflow_id: |
| pytest.skip("Workflow ID not returned") |
|
|
| |
| response = mobile_api_client.post( |
| f"/api/v1/workflows/{workflow_id}/execute", |
| headers=mobile_auth_headers, |
| json={ |
| "input": "test", |
| "parameters": { |
| "param1": "value1", |
| "param2": 123, |
| "param3": True |
| } |
| } |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
| assert "execution_id" in data or "success" in data |
| elif response.status_code == 404: |
| pytest.skip("Workflow execution endpoint not implemented") |
|
|
| def test_mobile_workflow_execute_not_found(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test executing non-existent workflow""" |
| response = mobile_api_client.post( |
| "/api/v1/workflows/nonexistent_workflow_id/execute", |
| headers=mobile_auth_headers, |
| json={"input": "test"} |
| ) |
|
|
| |
| assert response.status_code in [404, 400] |
|
|
|
|
| class TestMobileWorkflowDAGValidation: |
| """Test mobile workflow DAG validation""" |
|
|
| def test_mobile_workflow_dag_validation(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test DAG validation detects cycles""" |
| |
| response = mobile_api_client.post( |
| "/api/v1/workflows/validate", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Cyclic Workflow", |
| "nodes": [ |
| {"id": "A", "type": "skill"}, |
| {"id": "B", "type": "skill"}, |
| {"id": "C", "type": "skill"} |
| ], |
| "edges": [ |
| {"from": "A", "to": "B"}, |
| {"from": "B", "to": "C"}, |
| {"from": "C", "to": "A"} |
| ] |
| } |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
|
|
| |
| if "valid" in data: |
| assert data["valid"] is False |
| assert "cycle" in str(data.get("error", "")).lower() |
| elif "errors" in data: |
| |
| assert len(data["errors"]) > 0 |
| elif response.status_code == 404: |
| pytest.skip("Workflow validation endpoint not implemented") |
|
|
| def test_mobile_workflow_dag_valid(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test DAG validation passes for valid DAG""" |
| |
| response = mobile_api_client.post( |
| "/api/v1/workflows/validate", |
| headers=mobile_auth_headers, |
| json={ |
| "name": "Valid DAG Workflow", |
| "nodes": [ |
| {"id": "A", "type": "skill"}, |
| {"id": "B", "type": "skill"}, |
| {"id": "C", "type": "skill"} |
| ], |
| "edges": [ |
| {"from": "A", "to": "B"}, |
| {"from": "B", "to": "C"} |
| |
| ] |
| } |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
|
|
| if "valid" in data: |
| assert data["valid"] is True |
| elif "errors" in data: |
| |
| assert len(data["errors"]) == 0 |
| elif response.status_code == 404: |
| pytest.skip("Workflow validation endpoint not implemented") |
|
|
|
|
| class TestMobileWorkflowExecutionHistory: |
| """Test mobile workflow execution history""" |
|
|
| def test_mobile_workflow_execution_history(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test listing workflow execution history""" |
| response = mobile_api_client.get( |
| "/api/v1/workflows/executions", |
| headers=mobile_auth_headers |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
|
|
| |
| assert "executions" in data or "items" in data or isinstance(data, list) |
|
|
| |
| if isinstance(data, dict) and "executions" in data: |
| executions = data["executions"] |
| if executions: |
| |
| execution = executions[0] |
| assert "status" in execution or "workflow_id" in execution |
| elif response.status_code == 404: |
| pytest.skip("Workflow executions history endpoint not implemented") |
|
|
| def test_mobile_workflow_execution_history_fields(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test execution history has required fields""" |
| response = mobile_api_client.get( |
| "/api/v1/workflows/executions?limit=5", |
| headers=mobile_auth_headers |
| ) |
|
|
| if response.status_code == 200: |
| data = response.json() |
| executions = data.get("executions", data.get("items", data)) |
|
|
| if isinstance(executions, list) and executions: |
| execution = executions[0] |
|
|
| |
| expected_fields = ["status", "workflow_id"] |
| for field in expected_fields: |
| if field in execution: |
| assert execution[field] is not None |
|
|
| |
| if "started_at" in execution or "start_time" in execution: |
| timestamp = execution.get("started_at") or execution.get("start_time") |
| assert timestamp is not None |
|
|
| if "completed_at" in execution or "end_time" in execution: |
| timestamp = execution.get("completed_at") or execution.get("end_time") |
| |
| assert timestamp is None or timestamp is not None |
| elif response.status_code == 404: |
| pytest.skip("Workflow executions history endpoint not implemented") |
|
|
| def test_mobile_workflow_execution_history_pagination(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test execution history pagination""" |
| response = mobile_api_client.get( |
| "/api/v1/workflows/executions?limit=10&offset=0", |
| headers=mobile_auth_headers |
| ) |
|
|
| if response.status_code == 200: |
| data = response.json() |
| executions = data.get("executions", data.get("items", [])) |
|
|
| if isinstance(executions, list): |
| |
| assert len(executions) <= 10 |
| elif response.status_code == 404: |
| pytest.skip("Workflow executions history endpoint not implemented") |
|
|
|
|
| class TestMobileWorkflowList: |
| """Test mobile workflow listing""" |
|
|
| def test_mobile_workflow_list(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test listing available workflows""" |
| response = mobile_api_client.get( |
| "/api/v1/workflows", |
| headers=mobile_auth_headers |
| ) |
|
|
| |
| if response.status_code == 200: |
| data = response.json() |
|
|
| |
| assert "workflows" in data or isinstance(data, list) |
|
|
| |
| workflows = data.get("workflows", data) |
|
|
| if isinstance(workflows, list) and workflows: |
| |
| workflow = workflows[0] |
| assert "id" in workflow or "workflow_id" in workflow |
| assert "name" in workflow |
| elif response.status_code == 404: |
| pytest.skip("Workflow list endpoint not implemented") |
|
|
| def test_mobile_workflow_list_filtering(self, mobile_api_client: TestClient, mobile_auth_headers: dict): |
| """Test workflow list filtering""" |
| |
| response = mobile_api_client.get( |
| "/api/v1/workflows?category=testing", |
| headers=mobile_auth_headers |
| ) |
|
|
| if response.status_code == 200: |
| data = response.json() |
| workflows = data.get("workflows", data) |
|
|
| if isinstance(workflows, list): |
| |
| |
| assert True |
| elif response.status_code == 404: |
| pytest.skip("Workflow list endpoint not implemented") |
|
|