import { Component, inject, OnInit } from '@angular/core'; import { CommonModule } from '@angular/common'; import { FormsModule } from '@angular/forms'; import { MatProgressBarModule } from '@angular/material/progress-bar'; import { MatCheckboxModule } from '@angular/material/checkbox'; import { MatButtonModule } from '@angular/material/button'; import { MatIconModule } from '@angular/material/icon'; import { MatExpansionModule } from '@angular/material/expansion'; import { MatListModule } from '@angular/material/list'; import { MatChipsModule } from '@angular/material/chips'; import { ApiService } from '../../services/api.service'; import { HttpClient } from '@angular/common/http'; interface TestResult { name: string; status: 'PASS' | 'FAIL' | 'RUNNING' | 'SKIPPED'; duration_ms?: number; error?: string; details?: string; } interface TestCategory { name: string; displayName: string; tests: TestCase[]; selected: boolean; expanded: boolean; } interface TestCase { name: string; category: string; selected: boolean; testFn: () => Promise; } @Component({ selector: 'app-test', standalone: true, imports: [ CommonModule, FormsModule, MatProgressBarModule, MatCheckboxModule, MatButtonModule, MatIconModule, MatExpansionModule, MatListModule, MatChipsModule ], templateUrl: './test.component.html', styleUrls: ['./test.component.scss'] }) export class TestComponent implements OnInit { private apiService = inject(ApiService); private http = inject(HttpClient); running = false; currentTest: string = ''; testResults: TestResult[] = []; categories: TestCategory[] = [ { name: 'auth', displayName: 'Authentication Tests', tests: [], selected: false, expanded: false }, { name: 'api', displayName: 'API Endpoint Tests', tests: [], selected: false, expanded: false }, { name: 'validation', displayName: 'Validation Tests', tests: [], selected: false, expanded: false }, { name: 'integration', displayName: 'Integration Tests', tests: [], selected: false, expanded: false } ]; get allSelected(): boolean { return this.categories.every(c => c.selected); } get selectedTests(): TestCase[] { return this.categories .filter(c => c.selected) .flatMap(c => c.tests); } get totalTests(): number { return this.categories.reduce((sum, c) => sum + c.tests.length, 0); } get passedTests(): number { return this.testResults.filter(r => r.status === 'PASS').length; } get failedTests(): number { return this.testResults.filter(r => r.status === 'FAIL').length; } get progress(): number { if (this.testResults.length === 0) return 0; return (this.testResults.length / this.selectedTests.length) * 100; } ngOnInit() { this.initializeTests(); } initializeTests() { // Authentication Tests this.addTest('auth', 'Login with valid credentials', async () => { try { const response = await this.http.post('/api/login', { username: 'admin', password: 'admin' }).toPromise() as any; return { name: 'Login with valid credentials', status: response?.token ? 'PASS' : 'FAIL', duration_ms: 120 }; } catch (error) { return { name: 'Login with valid credentials', status: 'FAIL', error: 'Login failed', duration_ms: 120 }; } }); this.addTest('auth', 'Login with invalid credentials', async () => { try { await this.http.post('/api/login', { username: 'admin', password: 'wrong' }).toPromise(); return { name: 'Login with invalid credentials', status: 'FAIL', error: 'Expected 401 but got success', duration_ms: 80 }; } catch (error: any) { return { name: 'Login with invalid credentials', status: error.status === 401 ? 'PASS' : 'FAIL', duration_ms: 80, details: 'Correctly rejected invalid credentials' }; } }); // API Endpoint Tests this.addTest('api', 'GET /api/environment', async () => { const start = Date.now(); try { const response = await this.apiService.getEnvironment().toPromise(); return { name: 'GET /api/environment', status: response?.work_mode ? 'PASS' : 'FAIL', duration_ms: Date.now() - start }; } catch (error) { return { name: 'GET /api/environment', status: 'FAIL', error: 'Failed to get environment', duration_ms: Date.now() - start }; } }); this.addTest('api', 'GET /api/projects', async () => { const start = Date.now(); try { const response = await this.apiService.getProjects().toPromise(); return { name: 'GET /api/projects', status: Array.isArray(response) ? 'PASS' : 'FAIL', duration_ms: Date.now() - start, details: `Retrieved ${response?.length || 0} projects` }; } catch (error) { return { name: 'GET /api/projects', status: 'FAIL', error: 'Failed to get projects', duration_ms: Date.now() - start }; } }); this.addTest('api', 'GET /api/apis', async () => { const start = Date.now(); try { const response = await this.apiService.getAPIs().toPromise(); return { name: 'GET /api/apis', status: Array.isArray(response) ? 'PASS' : 'FAIL', duration_ms: Date.now() - start, details: `Retrieved ${response?.length || 0} APIs` }; } catch (error) { return { name: 'GET /api/apis', status: 'FAIL', error: 'Failed to get APIs', duration_ms: Date.now() - start }; } }); // Integration Tests this.addTest('integration', 'Create and delete project', async () => { const start = Date.now(); let projectId: number | undefined; try { // Create project const createResponse = await this.apiService.createProject({ name: `test_project_${Date.now()}`, caption: 'Test Project' }).toPromise() as any; if (!createResponse?.id) { throw new Error('Project creation failed'); } projectId = createResponse.id; // Delete project await this.apiService.deleteProject(projectId).toPromise(); return { name: 'Create and delete project', status: 'PASS', duration_ms: Date.now() - start, details: 'Project lifecycle test passed' }; } catch (error: any) { // Try to clean up if project was created if (projectId !== undefined) { try { await this.apiService.deleteProject(projectId).toPromise(); } catch {} } return { name: 'Create and delete project', status: 'FAIL', error: error.message || 'Test failed', duration_ms: Date.now() - start }; } }); this.addTest('integration', 'API used in intent cannot be deleted', async () => { const start = Date.now(); try { // Try to delete an API that's used in intents await this.apiService.deleteAPI('book_flight_api').toPromise(); return { name: 'API used in intent cannot be deleted', status: 'FAIL', error: 'Expected error but API was deleted', duration_ms: Date.now() - start }; } catch (error: any) { return { name: 'API used in intent cannot be deleted', status: error.status === 400 ? 'PASS' : 'FAIL', duration_ms: Date.now() - start, details: 'Correctly prevented deletion of API in use' }; } }); // Validation Tests this.addTest('validation', 'Regex validation - valid pattern', async () => { const start = Date.now(); try { const response = await this.apiService.validateRegex('^[A-Z]{3}$', 'ABC').toPromise(); return { name: 'Regex validation - valid pattern', status: response.valid && response.matches ? 'PASS' : 'FAIL', duration_ms: Date.now() - start }; } catch (error) { return { name: 'Regex validation - valid pattern', status: 'FAIL', error: 'Validation failed', duration_ms: Date.now() - start }; } }); this.addTest('validation', 'Regex validation - invalid pattern', async () => { const start = Date.now(); try { const response = await this.apiService.validateRegex('[invalid', 'test').toPromise(); return { name: 'Regex validation - invalid pattern', status: !response.valid ? 'PASS' : 'FAIL', duration_ms: Date.now() - start, details: 'Correctly identified invalid regex' }; } catch (error) { return { name: 'Regex validation - invalid pattern', status: 'PASS', duration_ms: Date.now() - start, details: 'Correctly rejected invalid regex' }; } }); // Update test counts this.categories.forEach(cat => { cat.displayName = `${cat.displayName} (${cat.tests.length} tests)`; }); } private addTest(category: string, name: string, testFn: () => Promise) { const cat = this.categories.find(c => c.name === category); if (cat) { cat.tests.push({ name, category, selected: true, testFn }); } } toggleAll() { const newState = !this.allSelected; this.categories.forEach(c => c.selected = newState); } async runAllTests() { this.categories.forEach(c => c.selected = true); await this.runTests(); } async runSelectedTests() { await this.runTests(); } async runTests() { if (this.running) return; this.running = true; this.testResults = []; this.currentTest = ''; const testsToRun = this.selectedTests; for (const test of testsToRun) { this.currentTest = test.name; try { const result = await test.testFn(); this.testResults.push(result); } catch (error: any) { this.testResults.push({ name: test.name, status: 'FAIL', error: error.message || 'Unexpected error' }); } // Small delay between tests await new Promise(resolve => setTimeout(resolve, 100)); } this.running = false; this.currentTest = ''; } stopTests() { this.running = false; this.currentTest = ''; } getTestResult(testName: string): TestResult | undefined { return this.testResults.find(r => r.name === testName); } getCategoryResults(category: TestCategory): { passed: number; failed: number; total: number } { const categoryResults = this.testResults.filter(r => category.tests.some(t => t.name === r.name) ); return { passed: categoryResults.filter(r => r.status === 'PASS').length, failed: categoryResults.filter(r => r.status === 'FAIL').length, total: category.tests.length }; } }