| """ |
| Canvas component integration tests (INTG-09). |
| |
| Tests cover: |
| - Adding text components |
| - Adding form components |
| - Adding chart components |
| - Adding custom components |
| - Component update |
| - Component deletion |
| """ |
| import pytest |
| from fastapi.testclient import TestClient |
| from sqlalchemy.orm import Session |
| from tests.factories.canvas_factory import CanvasAuditFactory |
| from tests.factories.user_factory import UserFactory |
| from core.models import CanvasAudit |
| from unittest.mock import Mock, patch |
| import uuid |
|
|
|
|
| class TestTextComponents: |
| """Test text component operations.""" |
|
|
| def test_add_text_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding text component to canvas.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "text", |
| "content": "Hello, World!" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("type") == "text" |
|
|
| def test_add_markdown_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding markdown component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="docs") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "markdown", |
| "content": "# Heading\n\n**Bold** text" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
|
|
| class TestFormComponents: |
| """Test form component operations.""" |
|
|
| def test_add_form_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding form component to canvas.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "form", |
| "fields": [ |
| {"name": "email", "type": "email", "required": True}, |
| {"name": "name", "type": "text", "required": True} |
| ] |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("type") == "form" |
| assert "fields" in data |
| assert len(data.get("fields", [])) == 2 |
|
|
| def test_form_with_various_field_types(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test form with various field types.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "form", |
| "fields": [ |
| {"name": "name", "type": "text", "required": True}, |
| {"name": "email", "type": "email", "required": True}, |
| {"name": "age", "type": "number", "required": False}, |
| {"name": "bio", "type": "textarea", "required": False}, |
| {"name": "subscribe", "type": "checkbox", "required": False} |
| ] |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
| def test_form_field_validation(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test form field validation.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| |
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "form", |
| "fields": [] |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 400, 422, 404, 405] |
|
|
|
|
| class TestChartComponents: |
| """Test chart component operations.""" |
|
|
| def test_add_line_chart(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding line chart component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="orchestration") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "line_chart", |
| "data": { |
| "x": [1, 2, 3, 4, 5], |
| "y": [10, 20, 15, 25, 30] |
| } |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("type") in ["line_chart", "chart"] |
|
|
| def test_add_bar_chart(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding bar chart component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="orchestration") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "bar_chart", |
| "data": { |
| "labels": ["A", "B", "C"], |
| "values": [10, 20, 30] |
| } |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
| def test_add_pie_chart(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding pie chart component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="orchestration") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "pie_chart", |
| "data": { |
| "labels": ["Red", "Blue", "Green"], |
| "values": [30, 40, 30] |
| } |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
|
|
| class TestSheetComponents: |
| """Test sheet component operations.""" |
|
|
| def test_add_sheet_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding sheet component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="sheets") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "sheet", |
| "data": { |
| "headers": ["Name", "Age", "City"], |
| "rows": [ |
| ["Alice", 30, "NYC"], |
| ["Bob", 25, "LA"] |
| ] |
| } |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| assert response.status_code in [200, 201, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("type") in ["sheet", "data_table"] |
|
|
|
|
| class TestCustomComponents: |
| """Test custom component operations.""" |
|
|
| def test_add_custom_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test adding custom HTML/CSS/JS component.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "custom", |
| "html": "<div>Custom</div>", |
| "css": "div { color: red; }", |
| "javascript": "console.log('test');" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 403, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("type") == "custom" |
|
|
| def test_custom_component_html_only(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test custom component with HTML only (lower governance).""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "custom", |
| "html": "<div class='safe'>Safe HTML</div>", |
| "css": ".safe { padding: 10px; }" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 404, 405] |
|
|
| def test_custom_component_with_javascript_restricted(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test custom component with JavaScript requires AUTONOMOUS.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "custom", |
| "html": "<div>Test</div>", |
| "javascript": "console.log('test');" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 403, 404, 405] |
|
|
|
|
| class TestComponentUpdate: |
| """Test component update operations.""" |
|
|
| def test_update_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test updating existing component.""" |
| canvas_id = str(uuid.uuid4()) |
| component_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.put( |
| f"/api/canvas/{canvas_id}/components/{component_id}", |
| json={ |
| "content": "Updated content" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 201, 404, 405] |
|
|
| if response.status_code in [200, 201]: |
| data = response.json() |
| assert data.get("content") == "Updated content" |
|
|
| def test_update_nonexistent_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test updating non-existent component.""" |
| canvas_id = str(uuid.uuid4()) |
| fake_component_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.put( |
| f"/api/canvas/{canvas_id}/components/{fake_component_id}", |
| json={ |
| "content": "Should fail" |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [404, 405] |
|
|
|
|
| class TestComponentDeletion: |
| """Test component deletion operations.""" |
|
|
| def test_delete_component(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test deleting component from canvas.""" |
| canvas_id = str(uuid.uuid4()) |
| component_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.delete( |
| f"/api/canvas/{canvas_id}/components/{component_id}", |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [200, 204, 404, 405] |
|
|
| if response.status_code in [200, 204]: |
| |
| verify_response = client.get( |
| f"/api/canvas/{canvas_id}/components/{component_id}", |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
| if verify_response.status_code != 405: |
| assert verify_response.status_code == 404 |
|
|
| def test_delete_component_from_nonexistent_canvas(self, client: TestClient, auth_token: str): |
| """Test deleting component from non-existent canvas.""" |
| fake_canvas_id = str(uuid.uuid4()) |
| component_id = str(uuid.uuid4()) |
|
|
| response = client.delete( |
| f"/api/canvas/{fake_canvas_id}/components/{component_id}", |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [404, 405] |
|
|
|
|
| class TestComponentValidation: |
| """Test component validation.""" |
|
|
| def test_component_invalid_type(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test component with invalid type.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="generic") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "invalid_type", |
| "data": {} |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [400, 422, 404, 405] |
|
|
| def test_chart_missing_data(self, client: TestClient, auth_token: str, db_session: Session): |
| """Test chart component without required data.""" |
| canvas_id = str(uuid.uuid4()) |
| canvas = CanvasAuditFactory(id=canvas_id, canvas_type="orchestration") |
| db_session.add(canvas) |
| db_session.commit() |
|
|
| response = client.post( |
| f"/api/canvas/{canvas_id}/components", |
| json={ |
| "type": "line_chart", |
| "data": {} |
| }, |
| headers={"Authorization": f"Bearer {auth_token}"} |
| ) |
|
|
| |
| assert response.status_code in [400, 422, 200, 201, 404, 405] |
|
|