315 lines
8.6 KiB
TypeScript
315 lines
8.6 KiB
TypeScript
#!/usr/bin/env ts-node
|
|
|
|
import axios from 'axios';
|
|
import jwt from 'jsonwebtoken';
|
|
import * as fs from 'fs';
|
|
import * as path from 'path';
|
|
|
|
/**
|
|
* Comprehensive API endpoint tester
|
|
* Tests all major endpoints and reports results
|
|
*/
|
|
|
|
const BASE_URL = process.env.API_URL || 'http://localhost:4000';
|
|
|
|
// Generate test tokens
|
|
const freeToken = jwt.sign(
|
|
{
|
|
sub: 'test-free-user-api',
|
|
email: 'api-test-free@test.com',
|
|
preferred_username: 'apitestfree',
|
|
realm_access: { roles: [] },
|
|
},
|
|
'test-secret',
|
|
{ expiresIn: '1h' }
|
|
);
|
|
|
|
const premiumToken = jwt.sign(
|
|
{
|
|
sub: 'test-premium-user-api',
|
|
email: 'api-test-premium@test.com',
|
|
preferred_username: 'apitestpremium',
|
|
realm_access: { roles: ['premium-user'] },
|
|
},
|
|
'test-secret',
|
|
{ expiresIn: '1h' }
|
|
);
|
|
|
|
interface TestResult {
|
|
endpoint: string;
|
|
method: string;
|
|
status: number;
|
|
success: boolean;
|
|
message: string;
|
|
duration: number;
|
|
}
|
|
|
|
const results: TestResult[] = [];
|
|
|
|
async function testEndpoint(
|
|
name: string,
|
|
method: string,
|
|
endpoint: string,
|
|
options: {
|
|
token?: string;
|
|
data?: any;
|
|
expectedStatus?: number;
|
|
headers?: any;
|
|
} = {}
|
|
): Promise<void> {
|
|
const startTime = Date.now();
|
|
try {
|
|
const config: any = {
|
|
method,
|
|
url: `${BASE_URL}${endpoint}`,
|
|
headers: {
|
|
...options.headers,
|
|
},
|
|
validateStatus: () => true, // Don't throw on any status
|
|
};
|
|
|
|
if (options.token) {
|
|
config.headers['Authorization'] = `Bearer ${options.token}`;
|
|
}
|
|
|
|
if (options.data) {
|
|
config.data = options.data;
|
|
config.headers['Content-Type'] = 'application/json';
|
|
}
|
|
|
|
const response = await axios(config);
|
|
const duration = Date.now() - startTime;
|
|
const expectedStatus = options.expectedStatus || 200;
|
|
const success = response.status === expectedStatus ||
|
|
(response.status >= 200 && response.status < 300);
|
|
|
|
results.push({
|
|
endpoint: `${method} ${endpoint}`,
|
|
method,
|
|
status: response.status,
|
|
success,
|
|
message: success ? '✅ PASS' : `❌ FAIL (expected ${expectedStatus}, got ${response.status})`,
|
|
duration,
|
|
});
|
|
|
|
console.log(`${success ? '✅' : '❌'} ${name}: ${response.status} (${duration}ms)`);
|
|
} catch (error: any) {
|
|
const duration = Date.now() - startTime;
|
|
results.push({
|
|
endpoint: `${method} ${endpoint}`,
|
|
method,
|
|
status: 0,
|
|
success: false,
|
|
message: `❌ ERROR: ${error.message}`,
|
|
duration,
|
|
});
|
|
console.log(`❌ ${name}: ERROR - ${error.message}`);
|
|
}
|
|
}
|
|
|
|
async function runTests() {
|
|
console.log('\n=================================================');
|
|
console.log('🧪 Testing All API Endpoints');
|
|
console.log('=================================================\n');
|
|
console.log(`Base URL: ${BASE_URL}\n`);
|
|
|
|
// Health Endpoints
|
|
console.log('📊 Testing Health Endpoints...');
|
|
await testEndpoint('Basic Health Check', 'GET', '/health');
|
|
await testEndpoint('Detailed Health Check', 'GET', '/health/detailed');
|
|
console.log('');
|
|
|
|
// User Endpoints
|
|
console.log('👤 Testing User Endpoints...');
|
|
await testEndpoint('Get User Profile (FREE)', 'GET', '/api/v1/user/profile', {
|
|
token: freeToken,
|
|
});
|
|
await testEndpoint('Get User Limits (FREE)', 'GET', '/api/v1/user/limits', {
|
|
token: freeToken,
|
|
});
|
|
await testEndpoint('Get User Profile (PREMIUM)', 'GET', '/api/v1/user/profile', {
|
|
token: premiumToken,
|
|
});
|
|
await testEndpoint('Get User Limits (PREMIUM)', 'GET', '/api/v1/user/limits', {
|
|
token: premiumToken,
|
|
});
|
|
await testEndpoint('Get User Profile (No Auth)', 'GET', '/api/v1/user/profile', {
|
|
expectedStatus: 401,
|
|
});
|
|
console.log('');
|
|
|
|
// Job Endpoints
|
|
console.log('📋 Testing Job Endpoints...');
|
|
await testEndpoint('Get User Jobs (FREE)', 'GET', '/api/v1/jobs', {
|
|
token: freeToken,
|
|
});
|
|
await testEndpoint('Get User Jobs (PREMIUM)', 'GET', '/api/v1/jobs', {
|
|
token: premiumToken,
|
|
});
|
|
await testEndpoint('Get Job Status (Non-existent)', 'GET', '/api/v1/jobs/non-existent-id', {
|
|
token: freeToken,
|
|
expectedStatus: 404,
|
|
});
|
|
console.log('');
|
|
|
|
// PDF Tool Endpoints
|
|
console.log('📄 Testing PDF Tool Endpoints...');
|
|
|
|
// Test PDF Merge (Available to all)
|
|
await testEndpoint('PDF Merge (FREE)', 'POST', '/api/v1/tools/pdf/merge', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1', 'test-file-2'],
|
|
parameters: {},
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
await testEndpoint('PDF Merge (PREMIUM)', 'POST', '/api/v1/tools/pdf/merge', {
|
|
token: premiumToken,
|
|
data: {
|
|
fileIds: ['test-file-1', 'test-file-2'],
|
|
parameters: {},
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF Compress
|
|
await testEndpoint('PDF Compress (FREE)', 'POST', '/api/v1/tools/pdf/compress', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: { optimizeLevel: 3 },
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF Split
|
|
await testEndpoint('PDF Split (FREE)', 'POST', '/api/v1/tools/pdf/split', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: {},
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF Rotate
|
|
await testEndpoint('PDF Rotate (FREE)', 'POST', '/api/v1/tools/pdf/rotate', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: { angle: 90 },
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF OCR (Premium only - should fail for FREE)
|
|
await testEndpoint('PDF OCR (FREE - Should Fail)', 'POST', '/api/v1/tools/pdf/ocr', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: { languages: ['eng'] },
|
|
},
|
|
expectedStatus: 403,
|
|
});
|
|
|
|
await testEndpoint('PDF OCR (PREMIUM)', 'POST', '/api/v1/tools/pdf/ocr', {
|
|
token: premiumToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: { languages: ['eng'] },
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF Watermark
|
|
await testEndpoint('PDF Watermark (FREE)', 'POST', '/api/v1/tools/pdf/watermark', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: {
|
|
watermarkType: 'text',
|
|
watermarkText: 'TEST',
|
|
},
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test PDF to Images
|
|
await testEndpoint('PDF to Images (FREE)', 'POST', '/api/v1/tools/pdf/to-images', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['test-file-1'],
|
|
parameters: { imageFormat: 'png' },
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
// Test Images to PDF
|
|
await testEndpoint('Images to PDF (FREE)', 'POST', '/api/v1/tools/pdf/from-images', {
|
|
token: freeToken,
|
|
data: {
|
|
fileIds: ['image-1', 'image-2'],
|
|
parameters: {},
|
|
},
|
|
expectedStatus: 202,
|
|
});
|
|
|
|
console.log('');
|
|
|
|
// Authentication Tests
|
|
console.log('🔐 Testing Authentication...');
|
|
await testEndpoint('Protected Endpoint (No Token)', 'GET', '/api/v1/user/profile', {
|
|
expectedStatus: 401,
|
|
});
|
|
await testEndpoint('Protected Endpoint (Invalid Token)', 'GET', '/api/v1/user/profile', {
|
|
token: 'invalid-token',
|
|
expectedStatus: 401,
|
|
});
|
|
console.log('');
|
|
|
|
// Summary
|
|
console.log('\n=================================================');
|
|
console.log('📊 Test Summary');
|
|
console.log('=================================================\n');
|
|
|
|
const total = results.length;
|
|
const passed = results.filter(r => r.success).length;
|
|
const failed = total - passed;
|
|
const avgDuration = Math.round(results.reduce((sum, r) => sum + r.duration, 0) / total);
|
|
|
|
console.log(`Total Tests: ${total}`);
|
|
console.log(`Passed: ✅ ${passed} (${((passed/total)*100).toFixed(1)}%)`);
|
|
console.log(`Failed: ❌ ${failed} (${((failed/total)*100).toFixed(1)}%)`);
|
|
console.log(`Average Response Time: ${avgDuration}ms\n`);
|
|
|
|
// Failed tests details
|
|
if (failed > 0) {
|
|
console.log('Failed Tests:');
|
|
console.log('---------------------------------------------------');
|
|
results.filter(r => !r.success).forEach(r => {
|
|
console.log(`${r.message}`);
|
|
console.log(` ${r.endpoint} - Status: ${r.status}`);
|
|
});
|
|
console.log('');
|
|
}
|
|
|
|
// Performance stats
|
|
console.log('Performance Stats:');
|
|
console.log('---------------------------------------------------');
|
|
const sortedByDuration = [...results].sort((a, b) => b.duration - a.duration);
|
|
console.log(`Fastest: ${sortedByDuration[sortedByDuration.length - 1].endpoint} (${sortedByDuration[sortedByDuration.length - 1].duration}ms)`);
|
|
console.log(`Slowest: ${sortedByDuration[0].endpoint} (${sortedByDuration[0].duration}ms)`);
|
|
console.log('\n=================================================\n');
|
|
|
|
// Exit with error code if tests failed
|
|
process.exit(failed > 0 ? 1 : 0);
|
|
}
|
|
|
|
// Run tests
|
|
runTests().catch(error => {
|
|
console.error('Fatal error:', error);
|
|
process.exit(1);
|
|
});
|