/** * Test Data Manager * Handles creation and cleanup of test data for E2E tests */ import { APIRequestContext, request } from '@playwright/test'; export interface TestUser { id?: string; email: string; password: string; fullName: string; } export interface TestScenario { id?: string; name: string; description?: string; region: string; tags: string[]; status?: string; } export interface TestReport { id?: string; scenarioId: string; format: 'pdf' | 'csv'; status?: string; } export interface TestScheduledReport { id?: string; scenarioId: string; name: string; frequency: 'daily' | 'weekly' | 'monthly'; format: 'pdf' | 'csv'; } export interface TestReportTemplate { id?: string; name: string; sections: string[]; } export class TestDataManager { private apiContext: APIRequestContext | null = null; private baseUrl: string; private authToken: string | null = null; // Track created entities for cleanup private users: string[] = []; private scenarios: string[] = []; private reports: string[] = []; private scheduledReports: string[] = []; private apiKeys: string[] = []; constructor(baseUrl: string = 'http://localhost:8000') { this.baseUrl = baseUrl; } async init() { this.apiContext = await request.newContext({ baseURL: this.baseUrl, }); } async cleanup() { // Clean up in reverse order of dependencies await this.cleanupReports(); await this.cleanupScheduledReports(); await this.cleanupScenarios(); await this.cleanupApiKeys(); await this.cleanupUsers(); await this.apiContext?.dispose(); } // ==================== USER MANAGEMENT ==================== async createTestUser(userData?: Partial): Promise { if (!this.apiContext) await this.init(); const user: TestUser = { email: userData?.email || `test_${Date.now()}_${Math.random().toString(36).substring(7)}@example.com`, password: userData?.password || 'TestPassword123!', fullName: userData?.fullName || 'Test User', }; const response = await this.apiContext!.post('/api/v1/auth/register', { data: { email: user.email, password: user.password, full_name: user.fullName, }, }); if (response.ok()) { const data = await response.json(); user.id = data.id; this.users.push(user.id!); // Login to get token await this.login(user.email, user.password); } return user; } async login(email: string, password: string): Promise { if (!this.apiContext) await this.init(); const response = await this.apiContext!.post('/api/v1/auth/login', { data: { username: email, password: password, }, }); if (response.ok()) { const data = await response.json(); this.authToken = data.access_token; return this.authToken; } return null; } private async cleanupUsers() { // Users are cleaned up at database level or left for reference // In production, you might want to actually delete them this.users = []; } // ==================== SCENARIO MANAGEMENT ==================== async createScenario(scenarioData: TestScenario): Promise { if (!this.apiContext) await this.init(); const response = await this.apiContext!.post('/api/v1/scenarios', { data: { name: scenarioData.name, description: scenarioData.description || '', region: scenarioData.region, tags: scenarioData.tags, }, headers: this.getAuthHeaders(), }); if (response.ok()) { const data = await response.json(); scenarioData.id = data.id; this.scenarios.push(data.id); } return scenarioData; } async addScenarioLogs(scenarioId: string, count: number = 10) { if (!this.apiContext) await this.init(); const logs = Array.from({ length: count }, (_, i) => ({ message: `Test log entry ${i + 1}`, source: 'e2e-test', level: ['INFO', 'WARN', 'ERROR'][Math.floor(Math.random() * 3)], timestamp: new Date().toISOString(), })); for (const log of logs) { await this.apiContext!.post('/ingest', { data: log, headers: { ...this.getAuthHeaders(), 'X-Scenario-ID': scenarioId, }, }); } } async addScenarioLogWithPII(scenarioId: string) { if (!this.apiContext) await this.init(); await this.apiContext!.post('/ingest', { data: { message: 'Contact us at test@example.com or call +1-555-123-4567', source: 'e2e-test', level: 'INFO', }, headers: { ...this.getAuthHeaders(), 'X-Scenario-ID': scenarioId, }, }); } async addScenarioMetrics(scenarioId: string, metrics: Record) { if (!this.apiContext) await this.init(); // Implementation depends on your metrics API await this.apiContext!.post(`/api/v1/scenarios/${scenarioId}/metrics`, { data: metrics, headers: this.getAuthHeaders(), }); } private async cleanupScenarios() { if (!this.apiContext) return; for (const scenarioId of this.scenarios) { await this.apiContext.delete(`/api/v1/scenarios/${scenarioId}`, { headers: this.getAuthHeaders(), failOnStatusCode: false, }); } this.scenarios = []; } // ==================== REPORT MANAGEMENT ==================== async createReport(scenarioId: string, format: 'pdf' | 'csv'): Promise { if (!this.apiContext) await this.init(); const response = await this.apiContext!.post(`/api/v1/scenarios/${scenarioId}/reports`, { data: { format, include_logs: true, }, headers: this.getAuthHeaders(), }); const report: TestReport = { id: response.ok() ? (await response.json()).id : undefined, scenarioId, format, status: 'pending', }; if (report.id) { this.reports.push(report.id); } return report; } async createScheduledReport(scenarioId: string, scheduleData: Partial): Promise { if (!this.apiContext) await this.init(); const schedule: TestScheduledReport = { id: undefined, scenarioId, name: scheduleData.name || 'Test Schedule', frequency: scheduleData.frequency || 'daily', format: scheduleData.format || 'pdf', }; const response = await this.apiContext!.post(`/api/v1/scenarios/${scenarioId}/reports/schedule`, { data: schedule, headers: this.getAuthHeaders(), }); if (response.ok()) { const data = await response.json(); schedule.id = data.id; this.scheduledReports.push(data.id); } return schedule; } async createReportTemplate(templateData: Partial): Promise { if (!this.apiContext) await this.init(); const template: TestReportTemplate = { id: undefined, name: templateData.name || 'Test Template', sections: templateData.sections || ['summary', 'charts'], }; const response = await this.apiContext!.post('/api/v1/reports/templates', { data: template, headers: this.getAuthHeaders(), }); if (response.ok()) { const data = await response.json(); template.id = data.id; } return template; } private async cleanupReports() { if (!this.apiContext) return; for (const reportId of this.reports) { await this.apiContext.delete(`/api/v1/reports/${reportId}`, { headers: this.getAuthHeaders(), failOnStatusCode: false, }); } this.reports = []; } private async cleanupScheduledReports() { if (!this.apiContext) return; for (const scheduleId of this.scheduledReports) { await this.apiContext.delete(`/api/v1/reports/schedule/${scheduleId}`, { headers: this.getAuthHeaders(), failOnStatusCode: false, }); } this.scheduledReports = []; } // ==================== API KEY MANAGEMENT ==================== async createApiKey(name: string, scopes: string[] = ['read']): Promise { if (!this.apiContext) await this.init(); const response = await this.apiContext!.post('/api/v1/api-keys', { data: { name, scopes, }, headers: this.getAuthHeaders(), }); if (response.ok()) { const data = await response.json(); this.apiKeys.push(data.id); return data.key; } return null; } private async cleanupApiKeys() { if (!this.apiContext) return; for (const keyId of this.apiKeys) { await this.apiContext.delete(`/api/v1/api-keys/${keyId}`, { headers: this.getAuthHeaders(), failOnStatusCode: false, }); } this.apiKeys = []; } // ==================== HELPERS ==================== private getAuthHeaders(): Record { const headers: Record = { 'Content-Type': 'application/json', }; if (this.authToken) { headers['Authorization'] = `Bearer ${this.authToken}`; } return headers; } }