From bfe2052bb16976dbd372edda46b1225b7c88b3a1 Mon Sep 17 00:00:00 2001 From: vishal Date: Wed, 3 Sep 2025 16:29:06 +0530 Subject: [PATCH] feat: update unified api test --- sdk/tests/unified-api-test.js | 531 +++++++--------------------------- 1 file changed, 100 insertions(+), 431 deletions(-) diff --git a/sdk/tests/unified-api-test.js b/sdk/tests/unified-api-test.js index b213a4960..9efedded7 100755 --- a/sdk/tests/unified-api-test.js +++ b/sdk/tests/unified-api-test.js @@ -1,6 +1,6 @@ #!/usr/bin/env node -// Unified API test script for comparing TypeScript and Go API responses +// Simplified API comparison test for TypeScript vs Go APIs import fs from 'fs'; import path from 'path'; import { fileURLToPath } from 'url'; @@ -8,474 +8,143 @@ import { fileURLToPath } from 'url'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -// Configuration const TS_API_URL = "http://localhost:3000"; const GO_API_URL = "http://localhost:8080"; const VERIFY_ENDPOINT = "/api/verify"; -// Colors for console output -const colors = { - reset: '\x1b[0m', - red: '\x1b[31m', - green: '\x1b[32m', - yellow: '\x1b[33m', - blue: '\x1b[34m', - magenta: '\x1b[35m', - cyan: '\x1b[36m', - white: '\x1b[37m' -}; - -function colorize(text, color) { - return `${colors[color]}${text}${colors.reset}`; -} - -// Test case structure -class TestCase { - constructor(name, requestBody, expectedStatus = 200, expectedResult = true, expectedErrorMessage = null) { - this.name = name; - this.requestBody = requestBody; - this.expectedStatus = expectedStatus; - this.expectedResult = expectedResult; - this.expectedErrorMessage = expectedErrorMessage; - } -} - -// API response comparison utility -class APIComparison { - constructor() { - this.results = { - passed: 0, - failed: 0, - tests: [] - }; - } - - async makeAPICall(url, endpoint, requestBody) { - const fullUrl = `${url}${endpoint}`; +// Simple API call function +async function callAPI(url, requestBody) { + try { + const response = await fetch(`${url}${VERIFY_ENDPOINT}`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(requestBody) + }); + const data = await response.text(); + let parsedData; try { - const response = await fetch(fullUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(requestBody) - }); - - const responseText = await response.text(); - let responseData; - - try { - responseData = JSON.parse(responseText); - } catch (e) { - responseData = { rawResponse: responseText }; - } - - return { - status: response.status, - data: responseData, - success: true - }; - } catch (error) { - return { - status: 0, - data: { error: error.message }, - success: false, - error: error.message - }; - } - } - - async runTest(testCase) { - console.log(colorize(`\n๐Ÿงช Running test: ${testCase.name}`, 'cyan')); - console.log(colorize('=' + '='.repeat(50 + testCase.name.length), 'cyan')); - - // Make calls to both APIs - console.log(colorize('\n๐Ÿ“ก Making API calls...', 'blue')); - - const [tsResponse, goResponse] = await Promise.all([ - this.makeAPICall(TS_API_URL, VERIFY_ENDPOINT, testCase.requestBody), - this.makeAPICall(GO_API_URL, VERIFY_ENDPOINT, testCase.requestBody) - ]); - - // Display responses - console.log(colorize('\n๐Ÿ“Š TypeScript API Response:', 'magenta')); - console.log(`Status: ${tsResponse.status}`); - console.log(`Success: ${tsResponse.success}`); - if (tsResponse.error) { - console.log(colorize(`Error: ${tsResponse.error}`, 'red')); - } else { - console.log(JSON.stringify(tsResponse.data, null, 2)); - } - - console.log(colorize('\n๐Ÿ“Š Go API Response:', 'magenta')); - console.log(`Status: ${goResponse.status}`); - console.log(`Success: ${goResponse.success}`); - if (goResponse.error) { - console.log(colorize(`Error: ${goResponse.error}`, 'red')); - } else { - console.log(JSON.stringify(goResponse.data, null, 2)); - } - - // Compare responses - const comparison = this.compareResponses(testCase, tsResponse, goResponse); - - // Record results - if (comparison.passed) { - this.results.passed++; - console.log(colorize('\nโœ… Test PASSED', 'green')); - } else { - this.results.failed++; - console.log(colorize('\nโŒ Test FAILED', 'red')); - } - - // Display comparison details - console.log(colorize('\n๐Ÿ” Comparison Results:', 'yellow')); - comparison.details.forEach(detail => { - const color = detail.passed ? 'green' : 'red'; - const symbol = detail.passed ? 'โœ“' : 'โœ—'; - console.log(colorize(` ${symbol} ${detail.message}`, color)); - }); - - this.results.tests.push({ - name: testCase.name, - passed: comparison.passed, - details: comparison.details, - tsResponse, - goResponse - }); - - return comparison; - } - - compareResponses(testCase, tsResponse, goResponse) { - const details = []; - let passed = true; - - // Check if both APIs are reachable - if (!tsResponse.success && !goResponse.success) { - details.push({ - passed: false, - message: 'Both APIs are unreachable - make sure Docker services are running' - }); - return { passed: false, details }; - } - - if (!tsResponse.success) { - details.push({ - passed: false, - message: `TypeScript API unreachable: ${tsResponse.error}` - }); - passed = false; - } - - if (!goResponse.success) { - details.push({ - passed: false, - message: `Go API unreachable: ${goResponse.error}` - }); - passed = false; - } - - if (!tsResponse.success || !goResponse.success) { - return { passed, details }; - } - - // Compare HTTP status codes - if (tsResponse.status === goResponse.status) { - details.push({ - passed: true, - message: `Status codes match: ${tsResponse.status}` - }); - } else { - details.push({ - passed: false, - message: `Status codes differ - TS: ${tsResponse.status}, Go: ${goResponse.status}` - }); - passed = false; - } - - // Check expected status - if (tsResponse.status === testCase.expectedStatus && goResponse.status === testCase.expectedStatus) { - details.push({ - passed: true, - message: `Both APIs returned expected status: ${testCase.expectedStatus}` - }); - } else { - details.push({ - passed: false, - message: `Expected status ${testCase.expectedStatus}, got TS: ${tsResponse.status}, Go: ${goResponse.status}` - }); - passed = false; - } - - // Compare result field (if present) - const tsResult = tsResponse.data?.result; - const goResult = goResponse.data?.result; - - if (tsResult !== undefined && goResult !== undefined) { - if (tsResult === goResult) { - details.push({ - passed: true, - message: `Result fields match: ${tsResult}` - }); - } else { - details.push({ - passed: false, - message: `Result fields differ - TS: ${tsResult}, Go: ${goResult}` - }); - passed = false; - } - - // Check expected result - if (tsResult === testCase.expectedResult && goResult === testCase.expectedResult) { - details.push({ - passed: true, - message: `Both APIs returned expected result: ${testCase.expectedResult}` - }); - } else { - details.push({ - passed: false, - message: `Expected result ${testCase.expectedResult}, got TS: ${tsResult}, Go: ${goResult}` - }); - passed = false; - } - } - - // Compare error messages (if expected) - if (testCase.expectedErrorMessage) { - const tsMessage = tsResponse.data?.message || tsResponse.data?.error; - const goMessage = goResponse.data?.message || goResponse.data?.error; - - if (tsMessage && goMessage) { - // Check if both contain the expected error message - const tsContainsExpected = tsMessage.toLowerCase().includes(testCase.expectedErrorMessage.toLowerCase()); - const goContainsExpected = goMessage.toLowerCase().includes(testCase.expectedErrorMessage.toLowerCase()); - - if (tsContainsExpected && goContainsExpected) { - details.push({ - passed: true, - message: `Both APIs returned expected error message containing: "${testCase.expectedErrorMessage}"` - }); - } else { - details.push({ - passed: false, - message: `Error messages don't match expected. TS: "${tsMessage}", Go: "${goMessage}", Expected: "${testCase.expectedErrorMessage}"` - }); - passed = false; - } - } - } - - // Compare response structure similarity - const tsKeys = Object.keys(tsResponse.data || {}).sort(); - const goKeys = Object.keys(goResponse.data || {}).sort(); - - if (JSON.stringify(tsKeys) === JSON.stringify(goKeys)) { - details.push({ - passed: true, - message: `Response structures match (same fields)` - }); - } else { - details.push({ - passed: false, - message: `Response structures differ - TS fields: [${tsKeys.join(', ')}], Go fields: [${goKeys.join(', ')}]` - }); - // This is a warning, not a failure - } - - return { passed, details }; - } - - printSummary() { - console.log(colorize('\n' + '='.repeat(60), 'cyan')); - console.log(colorize('๐Ÿ“‹ TEST SUMMARY', 'cyan')); - console.log(colorize('='.repeat(60), 'cyan')); - - const total = this.results.passed + this.results.failed; - console.log(`\n๐Ÿ“Š Total Tests: ${total}`); - console.log(colorize(`โœ… Passed: ${this.results.passed}`, 'green')); - console.log(colorize(`โŒ Failed: ${this.results.failed}`, 'red')); - - if (this.results.failed === 0) { - console.log(colorize('\n๐ŸŽ‰ All tests passed! Both APIs are responding consistently.', 'green')); - } else { - console.log(colorize('\nโš ๏ธ Some tests failed. Check the details above.', 'yellow')); - } - - // Show failed tests - if (this.results.failed > 0) { - console.log(colorize('\nโŒ Failed Tests:', 'red')); - this.results.tests.filter(t => !t.passed).forEach(test => { - console.log(colorize(` โ€ข ${test.name}`, 'red')); - }); + parsedData = JSON.parse(data); + } catch { + parsedData = { rawResponse: data }; } + return { + status: response.status, + data: parsedData, + success: true + }; + } catch (error) { + return { status: 0, data: { error: error.message }, success: false }; } } -// Load test data +// Compare two API responses +function compareAPIs(testName, tsResponse, goResponse, expectedStatus = 200) { + const issues = []; + + // Check connectivity + if (!tsResponse.success) issues.push(`TS API unreachable: ${tsResponse.data.error}`); + if (!goResponse.success) issues.push(`Go API unreachable: ${goResponse.data.error}`); + if (issues.length) return { passed: false, issues }; + + // Compare status codes + if (tsResponse.status !== goResponse.status) { + issues.push(`Status mismatch: TS=${tsResponse.status}, Go=${goResponse.status}`); + } + if (tsResponse.status !== expectedStatus) { + issues.push(`Expected status ${expectedStatus}, got TS=${tsResponse.status}, Go=${goResponse.status}`); + } + + // Compare results + const tsResult = tsResponse.data?.result; + const goResult = goResponse.data?.result; + if (tsResult !== undefined && goResult !== undefined && tsResult !== goResult) { + issues.push(`Result mismatch: TS=${tsResult}, Go=${goResult}`); + } + + return { passed: issues.length === 0, issues }; +} + +// Run a single test +async function runTest(testName, requestBody, expectedStatus = 200) { + console.log(`\n๐Ÿงช ${testName}`); + + const [tsResponse, goResponse] = await Promise.all([ + callAPI(TS_API_URL, requestBody), + callAPI(GO_API_URL, requestBody) + ]); + + const result = compareAPIs(testName, tsResponse, goResponse, expectedStatus); + + if (result.passed) { + console.log(`โœ… PASS`); + } else { + console.log(`โŒ FAIL:`); + result.issues.forEach(issue => console.log(` - ${issue}`)); + } + + return result.passed; +} + +// Load test data and create test cases function loadTestData() { try { const proofDataPath = path.join(__dirname, 'ts-api', 'vc_and_disclose_proof.json'); - const proofData = JSON.parse(fs.readFileSync(proofDataPath, 'utf8')); - return proofData; + return JSON.parse(fs.readFileSync(proofDataPath, 'utf8')); } catch (error) { - console.error(colorize(`โŒ Error loading test data: ${error.message}`, 'red')); + console.error(`โŒ Error loading test data: ${error.message}`); process.exit(1); } } -// Create test cases function createTestCases() { const proofData = loadTestData(); + const validUserContext = "000000000000000000000000000000000000000000000000000000000000a4ec00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd27"; + const invalidUserContext = "000000000000000000000000000000000000000000000000000000000000a4ec00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd28"; - // Valid userContextData from working test - const validUserContextData = "000000000000000000000000000000000000000000000000000000000000a4ec00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd27"; - - // Create invalid userContextData by modifying the userIdentifier part (bytes 64-128) - // Original: "00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd27" - // Modified: "00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd28" (changed last byte) - const invalidUserContextData = "000000000000000000000000000000000000000000000000000000000000a4ec00000000000000000000000094ba0db8a9db66979905784a9d6b2d286e55bd28"; - - // Create modified publicSignals for different error scenarios - const invalidScopeSignals = [...proofData.publicSignals]; - invalidScopeSignals[19] = "17121382998761176299335602807450250650083579600718579431641003529012841023067"; // Changed scope (index 19) - - const invalidMerkleRootSignals = [...proofData.publicSignals]; - invalidMerkleRootSignals[9] = "9656656992379025128519272376477139373854042233370909906627112932049610896732"; // Changed merkle root (index 9) - - const invalidAttestationIdSignals = [...proofData.publicSignals]; - invalidAttestationIdSignals[8] = "2"; // Changed attestation ID from "1" to "2" (index 8) - - const testCases = [ - new TestCase( - 'Valid Proof Verification', - { + return [ + { + name: 'Valid Proof Verification', + body: { attestationId: 1, proof: proofData.proof, publicSignals: proofData.publicSignals, userContextData: validUserContext }, + expectedStatus: 200 + }, + { + name: 'Invalid User Context', + body: { attestationId: 1, proof: proofData.proof, publicSignals: proofData.publicSignals, userContextData: invalidUserContext }, + expectedStatus: 500 + }, + { + name: 'Invalid Scope', + body: { attestationId: 1, proof: proofData.proof, - publicSignals: proofData.publicSignals, - userContextData: validUserContextData + publicSignals: proofData.publicSignals.map((sig, i) => i === 19 ? "17121382998761176299335602807450250650083579600718579431641003529012841023067" : sig), + userContextData: validUserContext }, - 200, - true - ), - new TestCase( - 'UserContextHash Mismatch Should Fail', - { - attestationId: 1, - proof: proofData.proof, - publicSignals: proofData.publicSignals, - userContextData: invalidUserContextData - }, - 500, - false, - "User context hash does not match" - ), - new TestCase( - 'Invalid Scope Should Fail', - { - attestationId: 1, - proof: proofData.proof, - publicSignals: invalidScopeSignals, - userContextData: validUserContextData - }, - 500, - false, - "Scope does not match" - ), - new TestCase( - 'Invalid Merkle Root Should Fail', - { - attestationId: 1, - proof: proofData.proof, - publicSignals: invalidMerkleRootSignals, - userContextData: validUserContextData - }, - 500, - false, - "root does not exist" - ), - new TestCase( - 'Invalid Attestation ID Should Fail', - { - attestationId: 1, - proof: proofData.proof, - publicSignals: invalidAttestationIdSignals, - userContextData: validUserContextData - }, - 500, - false, - "Attestation ID does not match" - ) - ]; - - return testCases; -} - -// Check if APIs are running -async function checkAPIHealth() { - console.log(colorize('๐Ÿ” Checking API health...', 'blue')); - - const healthChecks = await Promise.all([ - fetch(`${TS_API_URL}/health`).then(r => ({ api: 'TypeScript', status: r.status, ok: r.ok })).catch(e => ({ api: 'TypeScript', error: e.message, ok: false })), - fetch(`${GO_API_URL}/health`).then(r => ({ api: 'Go', status: r.status, ok: r.ok })).catch(e => ({ api: 'Go', error: e.message, ok: false })) - ]); - - let allHealthy = true; - healthChecks.forEach(check => { - if (check.ok) { - console.log(colorize(`โœ… ${check.api} API is healthy (${check.status})`, 'green')); - } else { - console.log(colorize(`โŒ ${check.api} API is not responding: ${check.error || check.status}`, 'red')); - allHealthy = false; + expectedStatus: 500 } - }); - - if (!allHealthy) { - console.log(colorize('\nโš ๏ธ Some APIs are not responding. Make sure Docker services are running:', 'yellow')); - console.log(' cd sdk/tests && ./run-apis.sh up'); - console.log(''); - console.log('Continuing with tests anyway...'); - } - - return allHealthy; + ]; } // Main execution async function main() { - console.log(colorize('๐Ÿš€ Self SDK Unified API Test Suite', 'cyan')); - console.log(colorize('=====================================', 'cyan')); + console.log(' Self SDK API Comparison Test\n'); - // Check API health - await checkAPIHealth(); - - // Create test runner - const comparison = new APIComparison(); - - // Create and run test cases const testCases = createTestCases(); - - console.log(colorize(`\n๐Ÿ“‹ Running ${testCases.length} test case(s)...`, 'blue')); + let passed = 0, failed = 0; for (const testCase of testCases) { - await comparison.runTest(testCase); + const success = await runTest(testCase.name, testCase.body, testCase.expectedStatus); + success ? passed++ : failed++; } - // Print summary - comparison.printSummary(); + console.log(`\n Results: ${passed} passed, ${failed} failed`); + console.log(failed === 0 ? '๐ŸŽ‰ All tests passed!' : ' Some tests failed'); - // Exit with appropriate code - process.exit(comparison.results.failed > 0 ? 1 : 0); + process.exit(failed > 0 ? 1 : 0); } -// Handle errors -process.on('unhandledRejection', (error) => { - console.error(colorize('\n๐Ÿ’ฅ Unhandled error:', 'red'), error); - process.exit(1); -}); - -// Run the tests main().catch(error => { - console.error(colorize('\n๐Ÿ’ฅ Fatal error:', 'red'), error); + console.error(` error: ${error}`); process.exit(1); });