Spaces:
Running
Running
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<TestResult>; | |
} | |
({ | |
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<TestResult>) { | |
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 | |
}; | |
} | |
} |