/** * E2E Test: Log Ingestion and Metrics * * Tests for: * - Start a scenario * - Send test logs via API * - Verify metrics update * - Check PII detection */ import { test, expect } from '@playwright/test'; import { navigateTo, waitForLoading, createScenarioViaAPI, deleteScenarioViaAPI, startScenarioViaAPI, stopScenarioViaAPI, sendTestLogs, generateTestScenarioName, } from './utils/test-helpers'; import { testLogs, logsWithPII, highVolumeLogs } from './fixtures/test-logs'; import { newScenarioData } from './fixtures/test-scenarios'; const testScenarioName = generateTestScenarioName('Ingest Test'); let createdScenarioId: string | null = null; test.describe('Log Ingestion', () => { test.beforeEach(async ({ request }) => { // Create a fresh scenario for each test const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: testScenarioName, }); createdScenarioId = scenario.id; }); test.afterEach(async ({ request }) => { // Cleanup: Stop and delete scenario if (createdScenarioId) { try { await stopScenarioViaAPI(request, createdScenarioId); } catch { // Scenario might not be running } await deleteScenarioViaAPI(request, createdScenarioId); createdScenarioId = null; } }); test('should start scenario successfully', async ({ page }) => { // Navigate to scenario detail await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Verify initial state (draft) await expect(page.locator('span').filter({ hasText: 'draft' }).first()).toBeVisible(); }); test('should ingest logs and update metrics', async ({ page, request }) => { // Start the scenario await startScenarioViaAPI(request, createdScenarioId!); // Send test logs await sendTestLogs(request, createdScenarioId!, testLogs); // Wait a moment for logs to be processed await page.waitForTimeout(2000); // Navigate to scenario detail and verify metrics await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Verify metrics updated (should be greater than 0) const totalRequests = page.locator('div', { has: page.locator('text=Total Requests') }).locator('div.text-2xl'); // Wait for metrics to refresh await page.waitForTimeout(6000); // Wait for metrics polling await page.reload(); await waitForLoading(page); // Verify scenario is now running await expect(page.locator('span').filter({ hasText: 'running' }).first()).toBeVisible(); }); test('should detect PII in logs', async ({ page, request }) => { // Start the scenario await startScenarioViaAPI(request, createdScenarioId!); // Send logs containing PII await sendTestLogs(request, createdScenarioId!, logsWithPII); // Wait for processing await page.waitForTimeout(2000); // Navigate to dashboard to check PII violations await navigateTo(page, '/'); await waitForLoading(page); // Verify PII Violations card is visible await expect(page.getByText('PII Violations')).toBeVisible(); }); test('should handle high volume log ingestion', async ({ page, request }) => { // Start the scenario await startScenarioViaAPI(request, createdScenarioId!); // Send high volume of logs await sendTestLogs(request, createdScenarioId!, highVolumeLogs.slice(0, 50)); // Wait for processing await page.waitForTimeout(3000); // Navigate to scenario detail await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Verify metrics reflect high volume // The scenario should still be stable await expect(page.getByRole('heading', { name: testScenarioName })).toBeVisible(); }); test('should stop scenario and update status', async ({ page, request }) => { // Start the scenario await startScenarioViaAPI(request, createdScenarioId!); // Navigate to detail page await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Verify running status await expect(page.locator('span').filter({ hasText: 'running' }).first()).toBeVisible(); // Stop the scenario await stopScenarioViaAPI(request, createdScenarioId!); // Refresh and verify stopped status await page.reload(); await waitForLoading(page); // Status should be completed or stopped const statusElement = page.locator('span').filter({ hasText: /completed|stopped|archived/ }).first(); await expect(statusElement).toBeVisible(); }); test('should update cost breakdown with different services', async ({ page, request }) => { // Start the scenario await startScenarioViaAPI(request, createdScenarioId!); // Send logs for different services const serviceLogs = [ ...testLogs.filter(log => log.service === 'lambda'), ...testLogs.filter(log => log.service === 'sqs'), ...testLogs.filter(log => log.service === 'bedrock'), ]; await sendTestLogs(request, createdScenarioId!, serviceLogs); // Wait for processing await page.waitForTimeout(2000); // Navigate to scenario detail await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Wait for metrics refresh await page.waitForTimeout(6000); await page.reload(); await waitForLoading(page); // Verify cost is updated const totalCost = page.locator('div', { has: page.locator('text=Total Cost') }).locator('div.text-2xl'); await expect(totalCost).toBeVisible(); }); test('should handle log ingestion errors gracefully', async ({ page, request }) => { // Try to send logs to a non-existent scenario const response = await request.post( `http://localhost:8000/api/v1/scenarios/non-existent-id/ingest`, { data: { logs: testLogs.slice(0, 1) } } ); // Should return 404 expect(response.status()).toBe(404); }); test('should persist metrics after page refresh', async ({ page, request }) => { // Start scenario and ingest logs await startScenarioViaAPI(request, createdScenarioId!); await sendTestLogs(request, createdScenarioId!, testLogs); // Wait for processing await page.waitForTimeout(3000); // Navigate to scenario detail await navigateTo(page, `/scenarios/${createdScenarioId}`); await waitForLoading(page); // Wait for metrics await page.waitForTimeout(6000); // Refresh page await page.reload(); await waitForLoading(page); // Verify metrics are still displayed await expect(page.getByText('Total Requests')).toBeVisible(); await expect(page.getByText('Total Cost')).toBeVisible(); await expect(page.getByText('SQS Blocks')).toBeVisible(); await expect(page.getByText('LLM Tokens')).toBeVisible(); }); }); test.describe('Log Ingestion - Dashboard Metrics', () => { test('should update dashboard stats after log ingestion', async ({ page, request }) => { // Create and start a scenario const scenario = await createScenarioViaAPI(request, { ...newScenarioData, name: generateTestScenarioName('Dashboard Test'), }); createdScenarioId = scenario.id; await startScenarioViaAPI(request, createdScenarioId); // Navigate to dashboard before ingestion await navigateTo(page, '/'); await waitForLoading(page); // Get initial running count const runningCard = page.locator('div').filter({ hasText: 'Running' }).first(); await expect(runningCard).toBeVisible(); // Send logs await sendTestLogs(request, createdScenarioId, testLogs); // Refresh dashboard await page.reload(); await waitForLoading(page); // Verify dashboard still loads correctly await expect(page.getByText('Total Scenarios')).toBeVisible(); await expect(page.getByText('Running')).toBeVisible(); await expect(page.getByText('Total Cost')).toBeVisible(); await expect(page.getByText('PII Violations')).toBeVisible(); }); });