| """ |
| E2E integration tests for agent execution to episodic memory flow (Phase 199, Plan 09). |
| |
| Tests cover the complete pipeline from agent action execution through episode creation and retrieval. |
| Validates governance → execution → episodic memory integration for all maturity levels. |
| |
| Purpose: Validate the complete agent execution to episodic memory pipeline. |
| Output: 5-8 E2E tests validating episode creation, canvas context, and feedback context. |
| |
| Coverage target: 1-2% contribution to overall 85% coverage goal |
| Test count: 6 E2E tests |
| """ |
|
|
| import pytest |
| import uuid |
| from unittest.mock import patch, AsyncMock, MagicMock |
| from sqlalchemy.orm import Session |
| from datetime import datetime |
| from sqlalchemy import text |
|
|
| |
| import sys |
| from pathlib import Path |
| sys.path.insert(0, str(Path(__file__).parent.parent)) |
|
|
| from tests.factories.agent_factory import ( |
| AgentFactory, |
| StudentAgentFactory, |
| InternAgentFactory, |
| SupervisedAgentFactory, |
| AutonomousAgentFactory |
| ) |
| from core.models import ( |
| AgentRegistry, |
| AgentExecution, |
| AgentEpisode, |
| EpisodeSegment, |
| CanvasAudit, |
| AgentFeedback, |
| SupervisionSession |
| ) |
|
|
|
|
| |
|
|
| def assert_episode_created(db_session: Session, agent_id: str, expected_count: int = 1): |
| """ |
| Assert that episodes were created for agent execution. |
| |
| Args: |
| db_session: Database session |
| agent_id: Agent ID to check |
| expected_count: Expected number of episodes (default: 1) |
| """ |
| episodes = db_session.query(AgentEpisode).filter( |
| AgentEpisode.agent_id == agent_id |
| ).all() |
| assert len(episodes) == expected_count, f"Expected {expected_count} episodes, got {len(episodes)}" |
| return episodes |
|
|
|
|
| def assert_execution_logged(db_session: Session, execution_id: str, expected_status: str = "completed"): |
| """ |
| Assert that execution was logged with expected status. |
| |
| Args: |
| db_session: Database session |
| execution_id: Execution ID to check |
| expected_status: Expected execution status (default: "completed") |
| """ |
| execution = db_session.query(AgentExecution).filter( |
| AgentExecution.id == execution_id |
| ).first() |
| assert execution is not None, f"Execution {execution_id} not found" |
| assert execution.status == expected_status, f"Expected status {expected_status}, got {execution.status}" |
| return execution |
|
|
|
|
| def assert_segments_created(db_session: Session, episode_id: str, min_count: int = 1): |
| """ |
| Assert that episode segments were created. |
| |
| Args: |
| db_session: Database session |
| episode_id: Episode ID to check |
| min_count: Minimum number of segments expected (default: 1) |
| """ |
| segments = db_session.query(EpisodeSegment).filter( |
| EpisodeSegment.episode_id == episode_id |
| ).all() |
| assert len(segments) >= min_count, f"Expected at least {min_count} segments, got {len(segments)}" |
| return segments |
|
|
|
|
| @pytest.mark.e2e |
| class TestAutonomousAgentEpisodeCreation: |
| """ |
| E2E tests for AUTONOMOUS agent episode creation. |
| |
| Tests verify that AUTONOMOUS agent executions create episodes with correct metadata. |
| """ |
|
|
| @pytest.fixture(autouse=True) |
| def setup_mocks(self, mock_llm_streaming, mock_websocket): |
| """Auto-apply mocks for all tests in this class.""" |
| self.mock_llm = mock_llm_streaming |
| self.mock_ws = mock_websocket |
|
|
| def test_autonomous_agent_execution_creates_episode(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that AUTONOMOUS agent execution creates an episode in episodic memory. |
| |
| Verifies: |
| - Episode created with correct agent_id |
| - Episode contains action segments |
| - Episode has LLM-generated summary |
| """ |
| |
| agent = AutonomousAgentFactory(name="E2E Episode Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": "Test message for episode creation", |
| "user_id": "test_user_e2e", |
| "execution_id": execution_id |
| }) |
|
|
| |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id, expected_count=1) |
| episode = episodes[0] |
|
|
| |
| assert episode.agent_id == agent.id |
| assert episode.maturity_at_time == "autonomous" |
| assert episode.status in ["active", "completed"] |
| assert episode.success == True |
| assert episode.constitutional_score >= 0.0 |
| assert episode.human_intervention_count == 0 |
|
|
| |
| execution = assert_execution_logged(e2e_db_session, execution_id, expected_status="completed") |
| assert execution.agent_id == agent.id |
|
|
| def test_autonomous_agent_multiple_actions_creates_segments(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that multiple AUTONOMOUS agent actions create multiple episode segments. |
| |
| Verifies: |
| - Episode has multiple segments for multiple actions |
| - Segment timestamps are sequential |
| - Segment types are correct |
| """ |
| |
| agent = AutonomousAgentFactory(name="E2E Segments Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| for i in range(3): |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": f"Test action {i+1}", |
| "user_id": "test_user_e2e" |
| }) |
| assert response.status_code == 200 |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id) |
| episode = episodes[0] |
|
|
| |
| segments = assert_segments_created(e2e_db_session, episode.id, min_count=3) |
|
|
| |
| segment_times = [s.created_at for s in segments] |
| assert segment_times == sorted(segment_times), "Segment timestamps should be sequential" |
|
|
| |
| segment_types = [s.segment_type for s in segments] |
| assert all(t in ["conversation", "execution", "reflection", "canvas_update"] for t in segment_types) |
|
|
|
|
| @pytest.mark.e2e |
| class TestSupervisedAgentEpisodeCreation: |
| """ |
| E2E tests for SUPERVISED agent episode creation with supervision metadata. |
| |
| Tests verify that SUPERVISED agent executions create episodes with supervision tracking. |
| """ |
|
|
| @pytest.fixture(autouse=True) |
| def setup_mocks(self, mock_llm_streaming, mock_websocket): |
| """Auto-apply mocks for all tests in this class.""" |
| self.mock_llm = mock_llm_streaming |
| self.mock_ws = mock_websocket |
|
|
| def test_supervised_agent_execution_creates_monitored_episode(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that SUPERVISED agent execution creates episode with supervision metadata. |
| |
| Verifies: |
| - Episode created with supervision metadata |
| - Supervision session linked to episode |
| - Maturity level recorded correctly |
| """ |
| |
| agent = SupervisedAgentFactory(name="E2E Supervised Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": "Supervised execution test", |
| "user_id": "test_user_e2e", |
| "execution_id": execution_id |
| }) |
|
|
| |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id) |
| episode = episodes[0] |
|
|
| |
| assert episode.maturity_at_time == "supervised" |
| assert episode.human_intervention_count >= 0 |
|
|
| |
| supervision_sessions = e2e_db_session.query(SupervisionSession).filter( |
| SupervisionSession.agent_id == agent.id |
| ).all() |
|
|
| |
| |
| if len(supervision_sessions) > 0: |
| assert supervision_sessions[0].agent_id == agent.id |
|
|
| def test_supervised_agent_intervention_creates_episode_segment(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that SUPERVISED agent intervention creates episode segment. |
| |
| Verifies: |
| - Episode segment records intervention |
| - Intervention reason stored |
| - Intervention count incremented |
| """ |
| |
| agent = SupervisedAgentFactory(name="E2E Intervention Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": "Intervention test", |
| "user_id": "test_user_e2e", |
| "execution_id": execution_id, |
| "require_supervision": True |
| }) |
|
|
| |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id) |
| episode = episodes[0] |
|
|
| |
| assert episode.human_intervention_count >= 0 |
|
|
| |
| segments = assert_segments_created(e2e_db_session, episode.id, min_count=1) |
|
|
| |
| intervention_segments = [s for s in segments if s.segment_type == "reflection"] |
| |
|
|
|
|
| @pytest.mark.e2e |
| class TestCanvasContextIntegration: |
| """ |
| E2E tests for canvas context integration with episodic memory. |
| |
| Tests verify that canvas presentations create episodes with canvas context. |
| """ |
|
|
| @pytest.fixture(autouse=True) |
| def setup_mocks(self, mock_llm_streaming, mock_websocket): |
| """Auto-apply mocks for all tests in this class.""" |
| self.mock_llm = mock_llm_streaming |
| self.mock_ws = mock_websocket |
|
|
| def test_agent_canvas_presentation_creates_canvas_episode(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that agent with canvas presentation creates episode with canvas context. |
| |
| Verifies: |
| - Episode contains canvas_context |
| - Canvas type and content linked |
| - Canvas audit record created |
| """ |
| |
| agent = AutonomousAgentFactory(name="E2E Canvas Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": "Present a chart", |
| "user_id": "test_user_e2e", |
| "execution_id": execution_id, |
| "context": { |
| "canvas_type": "line_chart", |
| "canvas_data": {"points": [1, 2, 3, 4, 5]} |
| } |
| }) |
|
|
| |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id) |
| episode = episodes[0] |
|
|
| |
| canvas_audits = e2e_db_session.query(CanvasAudit).filter( |
| CanvasAudit.agent_id == agent.id |
| ).all() |
|
|
| |
| |
| if len(canvas_audits) > 0: |
| assert canvas_audits[0].agent_id == agent.id |
| |
| assert canvas_audits[0].canvas_type in ["line_chart", "bar_chart", "pie_chart", "markdown", "form"] |
|
|
| |
| segments = assert_segments_created(e2e_db_session, episode.id, min_count=1) |
|
|
| |
| canvas_segments = [s for s in segments if s.canvas_context is not None] |
| |
|
|
|
|
| @pytest.mark.e2e |
| class TestFeedbackContextIntegration: |
| """ |
| E2E tests for feedback context integration with episodic memory. |
| |
| Tests verify that feedback linkage creates episodes with feedback context. |
| """ |
|
|
| @pytest.fixture(autouse=True) |
| def setup_mocks(self, mock_llm_streaming, mock_websocket): |
| """Auto-apply mocks for all tests in this class.""" |
| self.mock_llm = mock_llm_streaming |
| self.mock_ws = mock_websocket |
|
|
| def test_agent_with_feedback_creates_feedback_episode(self, e2e_client_integration, e2e_db_session_integration, execution_id): |
| """ |
| Test that agent with feedback creates episode with feedback context. |
| |
| Verifies: |
| - Episode contains feedback_context |
| - Feedback score affects retrieval |
| - Feedback linkage is lightweight reference |
| """ |
| |
| agent = AutonomousAgentFactory(name="E2E Feedback Test Agent", _session=e2e_db_session) |
| e2e_db_session.commit() |
|
|
| |
| with patch('core.llm_service.LLMService.stream_completion', self.mock_llm): |
| response = e2e_client.post("/api/atom-agent/chat", json={ |
| "agent_id": agent.id, |
| "message": "Generate a response", |
| "user_id": "test_user_e2e", |
| "execution_id": execution_id |
| }) |
|
|
| |
| assert response.status_code == 200, f"Expected 200, got {response.status_code}: {response.text}" |
|
|
| |
| episodes = assert_episode_created(e2e_db_session, agent.id) |
| episode = episodes[0] |
|
|
| |
| feedback = AgentFeedback( |
| id=str(uuid.uuid4()), |
| agent_id=agent.id, |
| execution_id=execution_id, |
| user_id="test_user_e2e", |
| feedback_type="thumbs_up", |
| feedback_score=1.0, |
| comment="Great response!", |
| created_at=datetime.utcnow() |
| ) |
| e2e_db_session.add(feedback) |
| e2e_db_session.commit() |
|
|
| |
| assert feedback.feedback_score == 1.0 |
|
|
| |
| |
| assert episode.human_intervention_count >= 0 |
|
|
| |
| feedback_records = e2e_db_session.query(AgentFeedback).filter( |
| AgentFeedback.agent_id == agent.id, |
| AgentFeedback.execution_id == execution_id |
| ).all() |
|
|
| assert len(feedback_records) >= 1 |
| assert feedback_records[0].feedback_score == 1.0 |
|
|