/** * E2E Integration Tests — Remote Diagnostics Phase 4 * Tests the complete flow: Admin creates session → Client captures → Admin views */ import { test, expect } from '@playwright/test'; const API_BASE = process.env.TEST_API_URL || 'http://localhost:4003'; const ADMIN_TOKEN = process.env.TEST_ADMIN_TOKEN || 'test-admin-token'; const PRODUCT_ID = 'test-product'; test.describe('Remote Diagnostics E2E Flow', () => { let sessionId: string; test('[E2E-1] Admin can create debug session', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/sessions`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { targetDeviceId: 'test-device-123', collectionLevel: 'debug', captureLogs: true, captureNetwork: true, captureScreenshots: true, maxDurationMinutes: 30, }, }); expect(response.status()).toBe(201); const session = await response.json(); expect(session.id).toBeDefined(); expect(session.status).toBe('pending'); expect(session.productId).toBe(PRODUCT_ID); sessionId = session.id; }); test('[E2E-2] Client can poll for active session config', async ({ request }) => { // First, admin must activate the session const activateResponse = await request.patch(`${API_BASE}/api/diagnostics/sessions/${sessionId}`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { status: 'active', }, }); expect(activateResponse.status()).toBe(200); // Now client polls for config const configResponse = await request.get(`${API_BASE}/api/diagnostics/config`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, 'X-Device-Id': 'test-device-123', }, }); expect(configResponse.status()).toBe(200); const config = await configResponse.json(); expect(config.enabled).toBe(true); expect(config.sessionId).toBe(sessionId); expect(config.captureLogs).toBe(true); expect(config.captureNetwork).toBe(true); }); test('[E2E-3] Client can ingest trace spans', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/sessions/${sessionId}/traces`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, 'X-Device-Id': 'test-device-123', }, data: { sessionId, traces: [ { traceId: 'trace-001', spanId: 'span-001', name: 'UserLogin', startTime: new Date().toISOString(), durationMs: 150, attributes: { userId: 'user-123' }, status: 'ok', }, { traceId: 'trace-002', spanId: 'span-002', parentId: 'span-001', name: 'DatabaseQuery', startTime: new Date().toISOString(), durationMs: 50, attributes: { query: 'SELECT * FROM users' }, status: 'ok', }, ], }, }); expect(response.status()).toBe(200); const result = await response.json(); expect(result.accepted).toBe(2); }); test('[E2E-4] Client can ingest log entries', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/sessions/${sessionId}/logs`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, 'X-Device-Id': 'test-device-123', }, data: { sessionId, logs: [ { level: 'info', message: 'Application started successfully', timestamp: new Date().toISOString(), module: 'AppLifecycle', context: { version: '1.0.0' }, }, { level: 'debug', message: 'User authenticated', timestamp: new Date().toISOString(), module: 'AuthManager', context: { userId: 'user-123', method: 'token' }, }, { level: 'error', message: 'Failed to load configuration', timestamp: new Date().toISOString(), module: 'ConfigLoader', context: { error: 'File not found' }, }, ], }, }); expect(response.status()).toBe(200); const result = await response.json(); expect(result.accepted).toBe(3); }); test('[E2E-5] Admin can query traces for session', async ({ request }) => { const response = await request.get(`${API_BASE}/api/diagnostics/sessions/${sessionId}/traces`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, }); expect(response.status()).toBe(200); const result = await response.json(); expect(Array.isArray(result.traces)).toBe(true); expect(result.traces.length).toBeGreaterThanOrEqual(2); }); test('[E2E-6] Admin can query logs for session', async ({ request }) => { const response = await request.get(`${API_BASE}/api/diagnostics/sessions/${sessionId}/logs`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, }); expect(response.status()).toBe(200); const result = await response.json(); expect(Array.isArray(result.logs)).toBe(true); expect(result.logs.length).toBeGreaterThanOrEqual(3); // Verify log levels are preserved const errorLog = result.logs.find((l: { level: string }) => l.level === 'error'); expect(errorLog).toBeDefined(); expect(errorLog.message).toContain('Failed to load'); }); test('[E2E-7] Admin can complete debug session', async ({ request }) => { const response = await request.patch(`${API_BASE}/api/diagnostics/sessions/${sessionId}`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { status: 'completed', }, }); expect(response.status()).toBe(200); const session = await response.json(); expect(session.status).toBe('completed'); expect(session.endedAt).toBeDefined(); expect(session.traceCount).toBeGreaterThanOrEqual(2); expect(session.logCount).toBeGreaterThanOrEqual(3); }); }); test.describe('Auto-Trigger E2E Tests', () => { test('[E2E-AUTO-1] Admin can create error-threshold trigger', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/triggers`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { productId: PRODUCT_ID, name: 'High Error Rate Alert', enabled: true, condition: { type: 'error_rate', threshold: 0.1, // 10% windowMinutes: 5, minEvents: 50, }, sessionConfig: { collectionLevel: 'debug', captureLogs: true, captureNetwork: true, captureScreenshots: true, maxDurationMinutes: 60, }, notifications: { emailAdmins: true, }, cooldownMinutes: 30, }, }); expect(response.status()).toBe(201); const result = await response.json(); expect(result.trigger.id).toBeDefined(); expect(result.trigger.condition.type).toBe('error_rate'); }); test('[E2E-AUTO-2] Admin can manually run triggers', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/triggers/run`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { productId: PRODUCT_ID, }, }); expect(response.status()).toBe(200); const result = await response.json(); expect(Array.isArray(result.results)).toBe(true); }); test('[E2E-CRASH-1] Client can report crash and trigger auto-session', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/crash-report`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { productId: PRODUCT_ID, deviceId: 'test-crash-device', sessionId: 'test-session-456', timestamp: new Date().toISOString(), errorType: 'SIGSEGV', errorMessage: 'Segmentation fault at 0x00000000', stackTrace: 'Thread 0: 0x001 0x002 0x003', breadcrumbs: [ { timestamp: new Date().toISOString(), category: 'navigation', message: 'Opened settings' }, { timestamp: new Date().toISOString(), category: 'action', message: 'Clicked save' }, ], deviceState: { memoryUsage: 1024 * 1024 * 100, // 100MB batteryLevel: 85, osVersion: 'iOS 17.2', appVersion: '1.5.0', }, }, }); // Should accept the crash report (session may or may not be created due to cooldown) expect([201, 202]).toContain(response.status()); const result = await response.json(); expect(result.accepted).toBe(true); }); }); test.describe('Access Control E2E Tests', () => { test('[E2E-AC-1] Non-admin cannot create session', async ({ request }) => { const response = await request.post(`${API_BASE}/api/diagnostics/sessions`, { headers: { 'Authorization': 'Bearer invalid-token', 'X-Product-Id': PRODUCT_ID, }, data: { targetDeviceId: 'test-device', }, }); expect(response.status()).toBe(401); }); test('[E2E-AC-2] User cannot access another user\'s session data', async ({ request }) => { // Create a session for user A const createResponse = await request.post(`${API_BASE}/api/diagnostics/sessions`, { headers: { 'Authorization': `Bearer ${ADMIN_TOKEN}`, 'X-Product-Id': PRODUCT_ID, }, data: { targetUserId: 'user-a', collectionLevel: 'standard', }, }); const session = await createResponse.json(); // User B tries to access const getResponse = await request.get(`${API_BASE}/api/diagnostics/sessions/${session.id}`, { headers: { 'Authorization': 'Bearer user-b-token', 'X-Product-Id': PRODUCT_ID, }, }); expect(getResponse.status()).toBe(403); }); });