/** * Integration test for multi-platform signing * * This test verifies the complete multi-platform signing workflow: * 1. Load configuration with accounts from different platforms * 2. Perform signing operations using platform-specific algorithms * 3. Verify signatures using platform-specific verification * 4. Test cross-platform compatibility and isolation * * Platforms tested: * - Pacifica (Ed25519) * - Aster (EIP-191) * - Binance (HMAC-SHA256) * * Tests MUST FAIL initially until implementation is provided. */ import { describe, test, expect, beforeEach, afterEach } from '@jest/globals'; import * as fs from 'fs/promises'; import * as path from 'path'; import * as os from 'os'; // Import types (this import will fail until types are implemented) import type { ICredentialManager, SignResult, Account, Platform, ConfigFile } from '@/specs/001-credential-manager/contracts/credential-manager'; describe('Multi-Platform Signing Integration Tests', () => { let credentialManager: ICredentialManager; let tempDir: string; let configPath: string; // Test messages for different platforms const testMessages = { pacifica: new TextEncoder().encode(JSON.stringify({ order_type: 'market', symbol: 'BTC-USD', side: 'buy', size: '0.1', timestamp: Date.now() })), aster: new TextEncoder().encode('Transfer 100 ETH to 0x1234567890abcdef'), binance: new TextEncoder().encode('GET\n/api/v3/account\n\ntimestamp=1234567890000') }; beforeEach(async () => { // This will fail until CredentialManager is implemented const { CredentialManager } = await import('@/core/credential-manager/CredentialManager'); credentialManager = new CredentialManager(); // Create temporary directory tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'multi-platform-test-')); configPath = path.join(tempDir, 'multi-platform-config.json'); // Create multi-platform configuration const multiPlatformConfig: ConfigFile = { version: "1.0", accounts: [ { id: "pacifica-test-account", platform: Platform.PACIFICA, name: "Pacifica Trading Account", credentials: { type: "ed25519", privateKey: "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef" } }, { id: "aster-test-account", platform: Platform.ASTER, name: "Aster DeFi Account", credentials: { type: "eip191", privateKey: "0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef" } }, { id: "binance-test-account", platform: Platform.BINANCE, name: "Binance Spot Account", credentials: { type: "hmac", apiKey: "test-binance-api-key", secretKey: "test-binance-secret-key" } } ] }; await fs.writeFile(configPath, JSON.stringify(multiPlatformConfig, null, 2)); await credentialManager.loadConfig(configPath); }); afterEach(async () => { // Clean up credentialManager.stopWatching(); try { await fs.rm(tempDir, { recursive: true, force: true }); } catch (error) { // Ignore cleanup errors } }); describe('Platform Account Loading', () => { test('should load accounts from all supported platforms', () => { // Act const allAccounts = credentialManager.listAccounts(); // Assert expect(allAccounts).toHaveLength(3); const platforms = allAccounts.map(account => account.platform); expect(platforms).toContain(Platform.PACIFICA); expect(platforms).toContain(Platform.ASTER); expect(platforms).toContain(Platform.BINANCE); }); test('should retrieve specific accounts by ID', () => { // Act & Assert const pacificaAccount = credentialManager.getAccount("pacifica-test-account"); expect(pacificaAccount).not.toBeNull(); expect(pacificaAccount!.platform).toBe(Platform.PACIFICA); expect(pacificaAccount!.credentials.type).toBe("ed25519"); const asterAccount = credentialManager.getAccount("aster-test-account"); expect(asterAccount).not.toBeNull(); expect(asterAccount!.platform).toBe(Platform.ASTER); expect(asterAccount!.credentials.type).toBe("eip191"); const binanceAccount = credentialManager.getAccount("binance-test-account"); expect(binanceAccount).not.toBeNull(); expect(binanceAccount!.platform).toBe(Platform.BINANCE); expect(binanceAccount!.credentials.type).toBe("hmac"); }); }); describe('Pacifica (Ed25519) Signing', () => { test('should sign Pacifica order message successfully', async () => { // Act const result: SignResult = await credentialManager.sign( "pacifica-test-account", testMessages.pacifica ); // Assert expect(result.success).toBe(true); expect(result.algorithm).toBe('ed25519'); expect(typeof result.signature).toBe('string'); expect(result.signature!.length).toBe(88); // base64 encoded Ed25519 signature expect(result.timestamp).toBeInstanceOf(Date); }); test('should verify Pacifica signature correctly', async () => { // Arrange - create signature const signResult = await credentialManager.sign( "pacifica-test-account", testMessages.pacifica ); expect(signResult.success).toBe(true); // Act - verify signature const isValid = await credentialManager.verify( "pacifica-test-account", testMessages.pacifica, signResult.signature! ); // Assert expect(isValid).toBe(true); }); test('should reject invalid Pacifica signature', async () => { // Act - verify invalid signature const isValid = await credentialManager.verify( "pacifica-test-account", testMessages.pacifica, "invalid-signature" ); // Assert expect(isValid).toBe(false); }); }); describe('Aster (EIP-191) Signing', () => { test('should sign Aster message successfully', async () => { // Act const result: SignResult = await credentialManager.sign( "aster-test-account", testMessages.aster ); // Assert expect(result.success).toBe(true); expect(result.algorithm).toBe('eip191'); expect(typeof result.signature).toBe('string'); expect(result.signature!.startsWith('0x')).toBe(true); // Ethereum-style signature expect(result.signature!.length).toBe(132); // 0x + 130 hex chars for EIP-191 }); test('should verify Aster signature correctly', async () => { // Arrange const signResult = await credentialManager.sign( "aster-test-account", testMessages.aster ); expect(signResult.success).toBe(true); // Act const isValid = await credentialManager.verify( "aster-test-account", testMessages.aster, signResult.signature! ); // Assert expect(isValid).toBe(true); }); test('should handle Ethereum personal message prefix', async () => { // Arrange - message that requires Ethereum personal message prefix const personalMessage = new TextEncoder().encode("Hello, Ethereum!"); // Act const result = await credentialManager.sign( "aster-test-account", personalMessage ); // Assert expect(result.success).toBe(true); expect(result.algorithm).toBe('eip191'); // Verify the signature const isValid = await credentialManager.verify( "aster-test-account", personalMessage, result.signature! ); expect(isValid).toBe(true); }); }); describe('Binance (HMAC-SHA256) Signing', () => { test('should sign Binance API request successfully', async () => { // Act const result: SignResult = await credentialManager.sign( "binance-test-account", testMessages.binance ); // Assert expect(result.success).toBe(true); expect(result.algorithm).toBe('hmac-sha256'); expect(typeof result.signature).toBe('string'); expect(result.signature!.length).toBe(64); // HMAC-SHA256 produces 64 hex chars }); test('should verify Binance signature correctly', async () => { // Arrange const signResult = await credentialManager.sign( "binance-test-account", testMessages.binance ); expect(signResult.success).toBe(true); // Act const isValid = await credentialManager.verify( "binance-test-account", testMessages.binance, signResult.signature! ); // Assert expect(isValid).toBe(true); }); test('should handle Binance API query string format', async () => { // Arrange - typical Binance API query string const queryString = "symbol=BTCUSDT&side=BUY&type=MARKET&quantity=0.1×tamp=1234567890000"; const queryMessage = new TextEncoder().encode(queryString); // Act const result = await credentialManager.sign( "binance-test-account", queryMessage ); // Assert expect(result.success).toBe(true); expect(result.algorithm).toBe('hmac-sha256'); // Verify signature const isValid = await credentialManager.verify( "binance-test-account", queryMessage, result.signature! ); expect(isValid).toBe(true); }); }); describe('Cross-Platform Isolation', () => { test('should not allow signing with wrong platform account', async () => { // Act & Assert - try to sign Pacifica message with Binance account const result = await credentialManager.sign( "binance-test-account", testMessages.pacifica ); // Should either fail or produce platform-appropriate signature if (result.success) { expect(result.algorithm).toBe('hmac-sha256'); // Should use Binance algorithm } else { expect(typeof result.error).toBe('string'); } }); test('should maintain signature isolation between platforms', async () => { // Arrange - same message signed by different platforms const commonMessage = new TextEncoder().encode("Common test message"); // Act - sign with all platforms const pacificaResult = await credentialManager.sign("pacifica-test-account", commonMessage); const asterResult = await credentialManager.sign("aster-test-account", commonMessage); const binanceResult = await credentialManager.sign("binance-test-account", commonMessage); // Assert - all should succeed but with different signatures expect(pacificaResult.success).toBe(true); expect(asterResult.success).toBe(true); expect(binanceResult.success).toBe(true); expect(pacificaResult.algorithm).toBe('ed25519'); expect(asterResult.algorithm).toBe('eip191'); expect(binanceResult.algorithm).toBe('hmac-sha256'); // Signatures should be different expect(pacificaResult.signature).not.toBe(asterResult.signature); expect(asterResult.signature).not.toBe(binanceResult.signature); expect(pacificaResult.signature).not.toBe(binanceResult.signature); }); test('should reject cross-platform signature verification', async () => { // Arrange - sign with one platform const message = new TextEncoder().encode("Cross-platform test"); const pacificaResult = await credentialManager.sign("pacifica-test-account", message); expect(pacificaResult.success).toBe(true); // Act - try to verify with different platform account const isValid = await credentialManager.verify( "aster-test-account", // Different platform message, pacificaResult.signature! ); // Assert - should fail verification expect(isValid).toBe(false); }); }); describe('Concurrent Multi-Platform Operations', () => { test('should handle concurrent signing across platforms', async () => { // Arrange const concurrentOperations = [ credentialManager.sign("pacifica-test-account", testMessages.pacifica), credentialManager.sign("aster-test-account", testMessages.aster), credentialManager.sign("binance-test-account", testMessages.binance), credentialManager.sign("pacifica-test-account", new TextEncoder().encode("Second Pacifica")), credentialManager.sign("aster-test-account", new TextEncoder().encode("Second Aster")) ]; // Act const results = await Promise.all(concurrentOperations); // Assert - all operations should complete successfully results.forEach((result, index) => { expect(result.success).toBe(true); expect(typeof result.signature).toBe('string'); }); // Verify algorithms are correct expect(results[0].algorithm).toBe('ed25519'); // Pacifica expect(results[1].algorithm).toBe('eip191'); // Aster expect(results[2].algorithm).toBe('hmac-sha256'); // Binance expect(results[3].algorithm).toBe('ed25519'); // Pacifica again expect(results[4].algorithm).toBe('eip191'); // Aster again }); test('should maintain performance across all platforms', async () => { // Performance requirement: signing < 50ms per operation const performanceTests = [ { platform: "pacifica-test-account", message: testMessages.pacifica }, { platform: "aster-test-account", message: testMessages.aster }, { platform: "binance-test-account", message: testMessages.binance } ]; for (const test of performanceTests) { const startTime = Date.now(); const result = await credentialManager.sign(test.platform, test.message); const duration = Date.now() - startTime; expect(result.success).toBe(true); expect(duration).toBeLessThan(50); // Performance requirement } }); }); describe('Platform-Specific Error Handling', () => { test('should handle Pacifica-specific errors gracefully', async () => { // Arrange - invalid Pacifica message (too large) const oversizedMessage = new Uint8Array(2 * 1024 * 1024); // 2MB // Act const result = await credentialManager.sign("pacifica-test-account", oversizedMessage); // Assert expect(result.success).toBe(false); expect(typeof result.error).toBe('string'); expect(result.error!).toContain('size'); }); test('should handle Aster-specific errors gracefully', async () => { // Arrange - test with malformed Ethereum address format if applicable const asterAccount = credentialManager.getAccount("aster-test-account"); expect(asterAccount).not.toBeNull(); // Act - attempt operation that might cause Aster-specific error const result = await credentialManager.sign("aster-test-account", new Uint8Array(0)); // Assert - should handle gracefully expect(typeof result.success).toBe('boolean'); if (!result.success) { expect(typeof result.error).toBe('string'); } }); test('should handle Binance-specific errors gracefully', async () => { // Arrange - empty message which might be invalid for HMAC const emptyMessage = new Uint8Array(0); // Act const result = await credentialManager.sign("binance-test-account", emptyMessage); // Assert - should handle gracefully expect(typeof result.success).toBe('boolean'); if (!result.success) { expect(typeof result.error).toBe('string'); } }); }); describe('Platform Detection and Validation', () => { test('should correctly identify platform for each account', () => { // Act & Assert const accounts = credentialManager.listAccounts(); accounts.forEach(account => { switch (account.platform) { case Platform.PACIFICA: expect(account.credentials.type).toBe('ed25519'); expect(account.credentials.privateKey).toMatch(/^[0-9a-f]{64}$/); break; case Platform.ASTER: expect(account.credentials.type).toBe('eip191'); expect(account.credentials.privateKey).toMatch(/^0x[0-9a-f]{64}$/); break; case Platform.BINANCE: expect(account.credentials.type).toBe('hmac'); expect(account.credentials.apiKey).toBeTruthy(); expect(account.credentials.secretKey).toBeTruthy(); break; default: fail(`Unknown platform: ${account.platform}`); } }); }); test('should validate credential format for each platform', () => { // This test verifies that the credential manager validates // platform-specific credential formats during loading const accounts = credentialManager.listAccounts(); expect(accounts).toHaveLength(3); // All accounts should be loaded successfully, indicating valid credential formats accounts.forEach(account => { expect(account.credentials).toBeDefined(); expect(typeof account.credentials.type).toBe('string'); }); }); }); });