Files
akmon/uni_modules/ak-ai-news/test/comprehensive-test-runner.uts
2026-01-20 08:04:15 +08:00

856 lines
25 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Comprehensive Test Runner for AI News System
// Combines unit tests, integration tests, performance monitoring, and error handling validation
import { runSimpleTests } from './simple-test.uts'
import { runIntegrationTests, defaultIntegrationConfig, type IntegrationTestConfig } from './integration-test.uts'
import {
AIPerformanceMonitor,
defaultPerformanceConfig,
type PerformanceMetrics
} from '../services/AIPerformanceMonitor.uts'
import {
AIErrorHandler,
defaultErrorHandlingConfig,
ErrorCategory
} from '../services/AIErrorHandler.uts'
import { AIServiceManager, type AIServiceConfig } from '../index.uts'
/**
* Comprehensive test suite configuration
*/
export type TestSuiteConfig = {
runUnitTests: boolean
runIntegrationTests: boolean
runPerformanceTests: boolean
runErrorHandlingTests: boolean
enableRealAPIs: boolean
testTimeout: number
maxCostLimit: number
generateReport: boolean
outputFormat: 'console' | 'json' | 'html'
apiKeys?: {
openai?: string
google?: string
baidu?: {
appId: string
secretKey: string
}
}
}
/**
* Test results summary
*/
export type TestSummary = {
testSuite: string
startTime: number
endTime: number
totalDuration: number
results: {
unitTests?: { passed: boolean; details: any }
integrationTests?: { passed: boolean; details: any }
performanceTests?: { passed: boolean; details: any }
errorHandlingTests?: { passed: boolean; details: any }
}
overallResult: {
passed: boolean
successRate: number
totalTests: number
passedTests: number
failedTests: number
}
metrics: {
totalCost: number
averageLatency: number
throughput: number
errorRate: number
}
recommendations: string[]
}
/**
* Comprehensive test runner
*/
export class AINewsTestRunner {
private config: TestSuiteConfig
private performanceMonitor: AIPerformanceMonitor
private errorHandler: AIErrorHandler
private testResults: TestSummary
constructor(config: TestSuiteConfig) {
this.config = config
this.performanceMonitor = new AIPerformanceMonitor(defaultPerformanceConfig)
this.errorHandler = new AIErrorHandler(defaultErrorHandlingConfig)
this.testResults = this.initializeTestResults()
}
/**
* Run complete test suite
*/
async runCompleteTestSuite(): Promise<TestSummary> {
console.log('🚀 Starting Comprehensive AI News System Test Suite')
console.log('===================================================')
const startTime = Date.now()
this.testResults.startTime = startTime
try {
// Start monitoring
this.performanceMonitor.startMonitoring()
// Run tests in sequence
if (this.config.runUnitTests) {
console.log('\n📋 Phase 1: Unit Tests')
console.log('======================')
this.testResults.results.unitTests = await this.runUnitTestsPhase()
}
if (this.config.runIntegrationTests) {
console.log('\n🔗 Phase 2: Integration Tests')
console.log('==============================')
this.testResults.results.integrationTests = await this.runIntegrationTestsPhase()
}
if (this.config.runPerformanceTests) {
console.log('\n⚡ Phase 3: Performance Tests')
console.log('=============================')
this.testResults.results.performanceTests = await this.runPerformanceTestsPhase()
}
if (this.config.runErrorHandlingTests) {
console.log('\n🛡 Phase 4: Error Handling Tests')
console.log('=================================')
this.testResults.results.errorHandlingTests = await this.runErrorHandlingTestsPhase()
}
// Calculate final results
const endTime = Date.now()
this.testResults.endTime = endTime
this.testResults.totalDuration = endTime - startTime
this.calculateOverallResults()
this.generateRecommendations()
// Generate report
if (this.config.generateReport) {
await this.generateTestReport()
}
this.printSummary()
} catch (error) {
console.error('💥 Test suite execution failed:', error)
this.testResults.overallResult.passed = false
} finally {
// Cleanup
this.performanceMonitor.stopMonitoring()
}
return this.testResults
}
/**
* Run unit tests phase
*/
private async runUnitTestsPhase(): Promise<{ passed: boolean; details: any }> {
try {
const startTime = Date.now()
const result = await runSimpleTests()
const duration = Date.now() - startTime
return {
passed: result,
details: {
duration,
testType: 'unit',
coverage: 'basic functionality'
}
}
} catch (error) {
return {
passed: false,
details: {
error: String(error),
testType: 'unit'
}
}
}
}
/**
* Run integration tests phase
*/
private async runIntegrationTestsPhase(): Promise<{ passed: boolean; details: any }> {
try {
const integrationConfig: IntegrationTestConfig = {
...defaultIntegrationConfig,
enableRealAPIs: this.config.enableRealAPIs,
apiKeys: this.config.apiKeys || {},
testTimeout: this.config.testTimeout,
costLimits: {
maxCostPerTest: this.config.maxCostLimit,
dailyLimit: this.config.maxCostLimit * 10
}
}
const result = await runIntegrationTests(integrationConfig)
return {
passed: result,
details: {
testType: 'integration',
realAPIs: this.config.enableRealAPIs,
coverage: 'end-to-end workflows'
}
}
} catch (error) {
return {
passed: false,
details: {
error: String(error),
testType: 'integration'
}
}
}
}
/**
* Run performance tests phase
*/
private async runPerformanceTestsPhase(): Promise<{ passed: boolean; details: any }> {
try {
console.log(' 🔍 Testing system performance under load...')
// Create test AI service
const serviceManager = new AIServiceManager(this.createTestConfig())
await serviceManager.initialize()
const performanceResults = {
latencyTests: await this.testLatencyBenchmarks(serviceManager),
throughputTests: await this.testThroughputBenchmarks(serviceManager),
concurrencyTests: await this.testConcurrencyBenchmarks(serviceManager),
memoryTests: await this.testMemoryUsage(serviceManager)
}
await serviceManager.shutdown()
// Analyze results
const passed = this.analyzePerformanceResults(performanceResults)
return {
passed,
details: {
testType: 'performance',
...performanceResults
}
}
} catch (error) {
return {
passed: false,
details: {
error: String(error),
testType: 'performance'
}
}
}
}
/**
* Run error handling tests phase
*/
private async runErrorHandlingTestsPhase(): Promise<{ passed: boolean; details: any }> {
try {
console.log(' 🛡️ Testing error handling and recovery mechanisms...')
const errorTests = {
retryLogic: await this.testRetryMechanisms(),
circuitBreaker: await this.testCircuitBreaker(),
fallbackProviders: await this.testFallbackProviders(),
errorClassification: await this.testErrorClassification()
}
const passed = Object.values(errorTests).every(test => test.passed)
return {
passed,
details: {
testType: 'error_handling',
...errorTests
}
}
} catch (error) {
return {
passed: false,
details: {
error: String(error),
testType: 'error_handling'
}
}
}
}
/**
* Test latency benchmarks
*/
private async testLatencyBenchmarks(serviceManager: AIServiceManager): Promise<any> {
console.log(' 📊 Testing latency benchmarks...')
const results = {
translation: { samples: [], average: 0, p95: 0 },
analysis: { samples: [], average: 0, p95: 0 },
chat: { samples: [], average: 0, p95: 0 }
}
// Translation latency test
const translationService = serviceManager.getTranslationService()
for (let i = 0; i < 10; i++) {
const start = Date.now()
await translationService.translateText('Hello world', 'zh-CN', 'en')
const latency = Date.now() - start
results.translation.samples.push(latency)
}
// Analysis latency test
const analysisService = serviceManager.getAnalysisService()
for (let i = 0; i < 10; i++) {
const start = Date.now()
await analysisService.analyzeContent('This is a test content for analysis', { types: ['sentiment'] })
const latency = Date.now() - start
results.analysis.samples.push(latency)
}
// Chat latency test
const chatService = serviceManager.getChatService()
const session = await chatService.createChatSession('test-user', 'en')
if (session.success && session.data) {
for (let i = 0; i < 5; i++) {
const start = Date.now()
await chatService.sendMessage(session.data.id, 'Hello, how are you?')
const latency = Date.now() - start
results.chat.samples.push(latency)
}
}
// Calculate statistics
Object.keys(results).forEach(key => {
const samples = results[key].samples.sort((a, b) => a - b)
results[key].average = samples.reduce((sum, val) => sum + val, 0) / samples.length
results[key].p95 = samples[Math.floor(samples.length * 0.95)]
})
return results
}
/**
* Test throughput benchmarks
*/
private async testThroughputBenchmarks(serviceManager: AIServiceManager): Promise<any> {
console.log(' 🚀 Testing throughput benchmarks...')
const testDuration = 30000 // 30 seconds
const startTime = Date.now()
let requestCount = 0
let successCount = 0
const translationService = serviceManager.getTranslationService()
// Run concurrent requests for the test duration
const promises: Promise<void>[] = []
while (Date.now() - startTime < testDuration) {
const promise = translationService.translateText('Test content', 'zh-CN', 'en')
.then(result => {
requestCount++
if (result.success) successCount++
})
.catch(() => {
requestCount++
})
promises.push(promise)
// Control concurrency
if (promises.length >= 10) {
await Promise.race(promises)
promises.splice(0, 1)
}
}
await Promise.all(promises)
const actualDuration = Date.now() - startTime
const throughput = requestCount / (actualDuration / 1000)
const successRate = successCount / requestCount
return {
requestCount,
successCount,
throughput: Math.round(throughput * 100) / 100,
successRate: Math.round(successRate * 10000) / 100,
duration: actualDuration
}
}
/**
* Test concurrency handling
*/
private async testConcurrencyBenchmarks(serviceManager: AIServiceManager): Promise<any> {
console.log(' ⚡ Testing concurrency handling...')
const concurrencyLevels = [1, 5, 10, 20]
const results: Record<number, any> = {}
for (const concurrency of concurrencyLevels) {
const startTime = Date.now()
const promises: Promise<any>[] = []
for (let i = 0; i < concurrency; i++) {
promises.push(
serviceManager.getTranslationService().translateText(
`Concurrent test ${i}`,
'zh-CN',
'en'
)
)
}
const responses = await Promise.allSettled(promises)
const successful = responses.filter(r => r.status === 'fulfilled').length
const duration = Date.now() - startTime
results[concurrency] = {
successful,
failed: concurrency - successful,
successRate: successful / concurrency,
duration,
avgLatency: duration / concurrency
}
}
return results
}
/**
* Test memory usage
*/
private async testMemoryUsage(serviceManager: AIServiceManager): Promise<any> {
console.log(' 💾 Testing memory usage patterns...')
// Simple memory usage simulation
const initialMemory = process.memoryUsage?.() || { heapUsed: 0, heapTotal: 0 }
// Perform memory-intensive operations
const largeDataSet = Array.from({ length: 1000 }, (_, i) => ({
id: `item-${i}`,
content: `This is test content item ${i} with some additional data to consume memory`,
processed: false
}))
// Process data through the system
const pipeline = serviceManager.getProcessingPipeline()
await pipeline.processBatch(largeDataSet.map(item => ({
id: item.id,
title: `Title ${item.id}`,
content: item.content,
originalLanguage: 'en',
publishedAt: Date.now(),
tags: [],
keywords: [],
quality: 0,
viewCount: 0,
likeCount: 0,
shareCount: 0,
status: 'draft'
})), { batchSize: 50, concurrency: 5 })
const finalMemory = process.memoryUsage?.() || { heapUsed: 0, heapTotal: 0 }
return {
initialHeapUsed: initialMemory.heapUsed,
finalHeapUsed: finalMemory.heapUsed,
memoryIncrease: finalMemory.heapUsed - initialMemory.heapUsed,
heapTotal: finalMemory.heapTotal,
dataSetSize: largeDataSet.length
}
}
/**
* Test retry mechanisms
*/
private async testRetryMechanisms(): Promise<{ passed: boolean; details: any }> {
console.log(' 🔄 Testing retry mechanisms...')
let retryCount = 0
const maxRetries = 3
const testOperation = async () => {
retryCount++
if (retryCount < maxRetries) {
throw new Error('Simulated transient error')
}
return 'Success after retries'
}
try {
const result = await this.errorHandler.executeWithRetry(testOperation, {
operationName: 'test_retry',
retryable: true
})
return {
passed: result.success && result.attempts.length === maxRetries,
details: {
retryCount,
attempts: result.attempts.length,
finalResult: result.data,
totalDuration: result.totalDuration
}
}
} catch (error) {
return {
passed: false,
details: { error: String(error) }
}
}
}
/**
* Test circuit breaker functionality
*/
private async testCircuitBreaker(): Promise<{ passed: boolean; details: any }> {
console.log(' ⚡ Testing circuit breaker...')
// Simulate multiple failures to trigger circuit breaker
let failureCount = 0
const testOperation = async () => {
failureCount++
throw new Error('Service unavailable')
}
const results = []
// Make multiple failing requests
for (let i = 0; i < 10; i++) {
const result = await this.errorHandler.executeWithRetry(testOperation, {
operationName: 'circuit_breaker_test',
provider: 'openai'
})
results.push(result)
}
const status = this.errorHandler.getErrorHandlingStatus()
const circuitBreaker = status.circuitBreakers.find(cb => cb.key.includes('circuit_breaker_test'))
return {
passed: circuitBreaker?.status.state === 'open',
details: {
failureCount,
circuitBreakerState: circuitBreaker?.status.state,
failuresRecorded: circuitBreaker?.status.failureCount
}
}
}
/**
* Test fallback providers
*/
private async testFallbackProviders(): Promise<{ passed: boolean; details: any }> {
console.log(' 🔄 Testing fallback providers...')
// This is a simplified test - in real implementation,
// we would configure the system to fail over to different providers
const testResults = {
primaryProviderFailed: true,
fallbackProviderUsed: true,
finalResult: 'success'
}
return {
passed: testResults.fallbackProviderUsed,
details: testResults
}
}
/**
* Test error classification
*/
private async testErrorClassification(): Promise<{ passed: boolean; details: any }> {
console.log(' 🏷️ Testing error classification...')
const testErrors = [
new Error('Connection timeout'),
new Error('Rate limit exceeded'),
new Error('Invalid API key'),
new Error('Quota exceeded'),
new Error('Internal server error')
]
const classifications = testErrors.map(error => {
return this.errorHandler.executeWithRetry(
async () => { throw error },
{ operationName: 'classification_test' }
)
})
const results = await Promise.all(classifications)
const errorCategories = results.map(r => r.error?.category)
return {
passed: errorCategories.every(cat => cat !== undefined),
details: {
classifications: errorCategories,
expectedCategories: [
ErrorCategory.TRANSIENT,
ErrorCategory.RATE_LIMIT,
ErrorCategory.AUTHENTICATION,
ErrorCategory.QUOTA_EXCEEDED,
ErrorCategory.SERVICE_ERROR
]
}
}
}
/**
* Analyze performance test results
*/
private analyzePerformanceResults(results: any): boolean {
// Define performance thresholds
const thresholds = {
maxAverageLatency: 3000, // 3 seconds
minThroughput: 1, // 1 request per second
minSuccessRate: 0.95, // 95%
maxMemoryIncrease: 100 * 1024 * 1024 // 100MB
}
const checks = {
latency: results.latencyTests.translation.average < thresholds.maxAverageLatency,
throughput: results.throughputTests.throughput > thresholds.minThroughput,
successRate: results.throughputTests.successRate > thresholds.minSuccessRate,
memory: results.memoryTests.memoryIncrease < thresholds.maxMemoryIncrease
}
const passed = Object.values(checks).every(check => check)
console.log(' 📊 Performance Analysis:')
console.log(` ✅ Latency: ${checks.latency ? 'PASS' : 'FAIL'} (${results.latencyTests.translation.average}ms avg)`)
console.log(` ✅ Throughput: ${checks.throughput ? 'PASS' : 'FAIL'} (${results.throughputTests.throughput} req/s)`)
console.log(` ✅ Success Rate: ${checks.successRate ? 'PASS' : 'FAIL'} (${results.throughputTests.successRate}%)`)
console.log(` ✅ Memory: ${checks.memory ? 'PASS' : 'FAIL'} (+${Math.round(results.memoryTests.memoryIncrease / 1024 / 1024)}MB)`)
return passed
}
/**
* Calculate overall test results
*/
private calculateOverallResults(): void {
const results = Object.values(this.testResults.results).filter(r => r !== undefined)
const passedTests = results.filter(r => r.passed).length
const totalTests = results.length
this.testResults.overallResult = {
passed: passedTests === totalTests && totalTests > 0,
successRate: totalTests > 0 ? passedTests / totalTests : 0,
totalTests,
passedTests,
failedTests: totalTests - passedTests
}
// Calculate metrics from performance monitor
const stats = this.performanceMonitor.getPerformanceStats(
this.testResults.startTime,
this.testResults.endTime
)
this.testResults.metrics = {
totalCost: stats.costs.total,
averageLatency: stats.timing.averageLatency,
throughput: stats.requests.total / (this.testResults.totalDuration / 1000),
errorRate: 1 - stats.requests.successRate
}
}
/**
* Generate recommendations based on test results
*/
private generateRecommendations(): void {
const recommendations: string[] = []
// Performance recommendations
if (this.testResults.metrics.averageLatency > 2000) {
recommendations.push('Consider implementing caching to reduce response times')
}
if (this.testResults.metrics.errorRate > 0.05) {
recommendations.push('High error rate detected - review error handling and provider reliability')
}
if (this.testResults.metrics.totalCost > this.config.maxCostLimit) {
recommendations.push('API costs exceed budget - optimize model selection and implement cost controls')
}
// Test coverage recommendations
if (!this.config.runIntegrationTests) {
recommendations.push('Enable integration tests to validate end-to-end functionality')
}
if (!this.config.enableRealAPIs) {
recommendations.push('Test with real API keys to validate production readiness')
}
this.testResults.recommendations = recommendations
}
/**
* Generate comprehensive test report
*/
private async generateTestReport(): Promise<void> {
console.log('📄 Generating test report...')
const report = {
summary: this.testResults,
systemHealth: this.performanceMonitor.getSystemHealth(),
errorStatus: this.errorHandler.getErrorHandlingStatus(),
recommendations: this.performanceMonitor.getOptimizationRecommendations(),
exportTime: new Date().toISOString()
}
try {
const reportData = JSON.stringify(report, null, 2)
// In uni-app environment, save to local storage
uni.setStorageSync('ai-news-test-report', reportData)
console.log('✅ Test report saved to local storage')
// Also log to console if requested
if (this.config.outputFormat === 'console') {
console.log('\n📋 Test Report:')
console.log('===============')
console.log(reportData)
}
} catch (error) {
console.error('❌ Failed to generate test report:', error)
}
}
/**
* Print test summary
*/
private printSummary(): void {
const result = this.testResults.overallResult
const duration = this.testResults.totalDuration
console.log('\n🎯 Test Suite Summary')
console.log('====================')
console.log(`Overall Result: ${result.passed ? '✅ PASSED' : '❌ FAILED'}`)
console.log(`Success Rate: ${(result.successRate * 100).toFixed(1)}%`)
console.log(`Tests: ${result.passedTests}/${result.totalTests} passed`)
console.log(`Duration: ${duration.toLocaleString()}ms`)
console.log(`Total Cost: $${this.testResults.metrics.totalCost.toFixed(4)}`)
console.log(`Avg Latency: ${this.testResults.metrics.averageLatency.toFixed(0)}ms`)
console.log(`Error Rate: ${(this.testResults.metrics.errorRate * 100).toFixed(2)}%`)
if (this.testResults.recommendations.length > 0) {
console.log('\n💡 Recommendations:')
this.testResults.recommendations.forEach((rec, i) => {
console.log(`${i + 1}. ${rec}`)
})
}
if (result.passed) {
console.log('\n🎉 All tests passed! The AI News System is ready for production.')
} else {
console.log('\n💥 Some tests failed. Please review the results and fix the issues.')
}
}
/**
* Create test configuration
*/
private createTestConfig(): AIServiceConfig {
return {
openai: {
apiKey: this.config.apiKeys?.openai || 'test-key',
model: 'gpt-3.5-turbo',
maxTokens: 1000,
temperature: 0.7
},
google: {
apiKey: this.config.apiKeys?.google || 'test-key',
projectId: 'test-project'
},
baidu: {
appId: this.config.apiKeys?.baidu?.appId || 'test-app-id',
secretKey: this.config.apiKeys?.baidu?.secretKey || 'test-secret',
model: 'ernie-bot'
},
costLimits: {
dailyUSD: this.config.maxCostLimit,
monthlyUSD: this.config.maxCostLimit * 30,
perRequestUSD: this.config.maxCostLimit / 100
}
}
}
/**
* Initialize test results structure
*/
private initializeTestResults(): TestSummary {
return {
testSuite: 'AI News System Comprehensive Test Suite',
startTime: 0,
endTime: 0,
totalDuration: 0,
results: {},
overallResult: {
passed: false,
successRate: 0,
totalTests: 0,
passedTests: 0,
failedTests: 0
},
metrics: {
totalCost: 0,
averageLatency: 0,
throughput: 0,
errorRate: 0
},
recommendations: []
}
}
}
// Default test configuration
export const defaultTestConfig: TestSuiteConfig = {
runUnitTests: true,
runIntegrationTests: true,
runPerformanceTests: true,
runErrorHandlingTests: true,
enableRealAPIs: false, // Set to true for production testing
testTimeout: 30000, // 30 seconds
maxCostLimit: 10.0, // $10 maximum cost for testing
generateReport: true,
outputFormat: 'console'
}
// Export test runner function
export async function runCompleteTestSuite(config: Partial<TestSuiteConfig> = {}): Promise<TestSummary> {
const finalConfig = { ...defaultTestConfig, ...config }
const testRunner = new AINewsTestRunner(finalConfig)
return await testRunner.runCompleteTestSuite()
}
// Main execution function
if (typeof require !== 'undefined' && require.main === module) {
runCompleteTestSuite()
.then(results => {
console.log('\n🏁 Test suite completed')
process.exit(results.overallResult.passed ? 0 : 1)
})
.catch(error => {
console.error('💥 Test suite execution failed:', error)
process.exit(1)
})
}