123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652 |
- /**
- * Unit Tests for UnifiedSigner
- *
- * Tests the UnifiedSigner service implementation in isolation.
- */
- import { UnifiedSigner, BatchSignRequest } from '@/core/credential-manager/UnifiedSigner'
- import { PacificaSigner } from '@/core/credential-manager/signers/PacificaSigner'
- import { AsterSigner } from '@/core/credential-manager/signers/AsterSigner'
- import { BinanceSigner } from '@/core/credential-manager/signers/BinanceSigner'
- import { Platform } from '@/types/credential'
- describe('UnifiedSigner Unit Tests', () => {
- let unifiedSigner: UnifiedSigner
- let mockGetAccount: jest.Mock
- beforeEach(() => {
- unifiedSigner = new UnifiedSigner()
- mockGetAccount = jest.fn()
- unifiedSigner.setAccountGetter(mockGetAccount)
- })
- afterEach(() => {
- jest.clearAllMocks()
- })
- describe('Initialization and Configuration', () => {
- test('should initialize with default strategies', () => {
- const supportedPlatforms = unifiedSigner.getSupportedPlatforms()
- expect(supportedPlatforms).toContain(Platform.PACIFICA)
- expect(supportedPlatforms).toContain(Platform.ASTER)
- expect(supportedPlatforms).toContain(Platform.BINANCE)
- expect(supportedPlatforms).toHaveLength(3)
- })
- test('should allow registering custom strategies', () => {
- const customSigner = new PacificaSigner()
- const customPlatform = 'CUSTOM' as Platform
- // Mock the platform property for testing
- Object.defineProperty(customSigner, 'platform', {
- value: customPlatform,
- writable: false
- })
- unifiedSigner.registerStrategy(customPlatform, customSigner)
- const supportedPlatforms = unifiedSigner.getSupportedPlatforms()
- expect(supportedPlatforms).toContain(customPlatform)
- })
- test('should throw error for mismatched platform in strategy registration', () => {
- const pacificaSigner = new PacificaSigner()
- expect(() => {
- unifiedSigner.registerStrategy(Platform.ASTER, pacificaSigner)
- }).toThrow('Strategy platform mismatch')
- })
- test('should throw error for invalid strategy registration parameters', () => {
- expect(() => {
- unifiedSigner.registerStrategy(null as any, null as any)
- }).toThrow('Platform and strategy are required')
- })
- test('should require account getter to be set', async () => {
- const signerWithoutGetter = new UnifiedSigner()
- const message = new TextEncoder().encode('test')
- await expect(signerWithoutGetter.sign('test-account', message))
- .rejects.toThrow('Account getter function not configured')
- })
- })
- describe('Signing Operations', () => {
- test('should sign with Pacifica account', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(pacificaAccount)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('pac-account', message)
- expect(result.success).toBe(true)
- expect(result.algorithm).toBe('ed25519')
- expect(result.metadata?.platform).toBe(Platform.PACIFICA)
- expect(mockGetAccount).toHaveBeenCalledWith('pac-account')
- })
- test('should sign with Aster account', async () => {
- const asterAccount = {
- id: 'ast-account',
- platform: Platform.ASTER,
- credentials: {
- type: 'secp256k1' as const,
- privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
- }
- }
- mockGetAccount.mockResolvedValue(asterAccount)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('ast-account', message)
- expect(result.success).toBe(true)
- expect(result.algorithm).toBe('eip191')
- expect(result.metadata?.platform).toBe(Platform.ASTER)
- })
- test('should sign with Binance account', async () => {
- const binanceAccount = {
- id: 'bnb-account',
- platform: Platform.BINANCE,
- credentials: {
- type: 'hmac' as const,
- apiKey: 'test-api-key',
- secretKey: 'test-secret-key'
- }
- }
- mockGetAccount.mockResolvedValue(binanceAccount)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('bnb-account', message)
- expect(result.success).toBe(true)
- expect(result.algorithm).toBe('hmac-sha256')
- expect(result.metadata?.platform).toBe(Platform.BINANCE)
- })
- test('should handle account not found error', async () => {
- mockGetAccount.mockResolvedValue(null)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('non-existent', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain('Account not found')
- })
- test('should handle platform detection failure', async () => {
- const accountWithoutPlatform = {
- id: 'invalid-account',
- credentials: {
- type: 'unknown' as any
- }
- }
- mockGetAccount.mockResolvedValue(accountWithoutPlatform)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('invalid-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain('Cannot determine platform')
- })
- test('should handle unsupported platform', async () => {
- const accountWithUnsupportedPlatform = {
- id: 'unsupported-account',
- platform: 'UNSUPPORTED' as Platform,
- credentials: {
- type: 'unknown' as any
- }
- }
- mockGetAccount.mockResolvedValue(accountWithUnsupportedPlatform)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('unsupported-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain('No strategy available')
- })
- test('should respect force platform option', async () => {
- const asterAccount = {
- id: 'ast-account',
- platform: Platform.ASTER,
- credentials: {
- type: 'secp256k1' as const,
- privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
- }
- }
- mockGetAccount.mockResolvedValue(asterAccount)
- const message = new TextEncoder().encode('test message')
- const result = await unifiedSigner.sign('ast-account', message, {
- forcePlatform: Platform.ASTER
- })
- expect(result.success).toBe(true)
- expect(result.metadata?.platform).toBe(Platform.ASTER)
- })
- })
- describe('Verification Operations', () => {
- test('should verify Pacifica signatures', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(pacificaAccount)
- const message = new TextEncoder().encode('verification test')
- // First sign the message
- const signResult = await unifiedSigner.sign('pac-account', message)
- expect(signResult.success).toBe(true)
- // Then verify it
- const isValid = await unifiedSigner.verify(
- 'pac-account',
- message,
- signResult.signature!
- )
- expect(isValid).toBe(true)
- })
- test('should reject invalid signatures during verification', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(pacificaAccount)
- const message = new TextEncoder().encode('verification test')
- const invalidSignature = 'invalid-signature'
- const isValid = await unifiedSigner.verify('pac-account', message, invalidSignature)
- expect(isValid).toBe(false)
- })
- test('should handle verification errors gracefully', async () => {
- mockGetAccount.mockResolvedValue(null)
- const message = new TextEncoder().encode('test')
- const signature = 'some-signature'
- const isValid = await unifiedSigner.verify('non-existent', message, signature)
- expect(isValid).toBe(false)
- })
- })
- describe('Batch Operations', () => {
- test('should handle batch signing requests', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- const asterAccount = {
- id: 'ast-account',
- platform: Platform.ASTER,
- credentials: {
- type: 'secp256k1' as const,
- privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
- }
- }
- mockGetAccount
- .mockResolvedValueOnce(pacificaAccount)
- .mockResolvedValueOnce(asterAccount)
- const batchRequests: BatchSignRequest[] = [
- {
- accountId: 'pac-account',
- message: new TextEncoder().encode('pacifica message')
- },
- {
- accountId: 'ast-account',
- message: new TextEncoder().encode('aster message')
- }
- ]
- const results = await unifiedSigner.signBatch(batchRequests)
- expect(results).toHaveLength(2)
- expect(results[0].success).toBe(true)
- expect(results[0].accountId).toBe('pac-account')
- expect(results[1].success).toBe(true)
- expect(results[1].accountId).toBe('ast-account')
- })
- test('should handle batch requests with failures', async () => {
- const validAccount = {
- id: 'valid-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount
- .mockResolvedValueOnce(validAccount)
- .mockResolvedValueOnce(null) // Account not found
- const batchRequests: BatchSignRequest[] = [
- {
- accountId: 'valid-account',
- message: new TextEncoder().encode('valid message')
- },
- {
- accountId: 'invalid-account',
- message: new TextEncoder().encode('invalid message')
- }
- ]
- const results = await unifiedSigner.signBatch(batchRequests)
- expect(results).toHaveLength(2)
- expect(results[0].success).toBe(true)
- expect(results[0].accountId).toBe('valid-account')
- expect(results[1].success).toBe(false)
- expect(results[1].accountId).toBe('invalid-account')
- })
- test('should maintain order in batch results', async () => {
- const account = {
- id: 'test-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(account)
- const batchRequests: BatchSignRequest[] = [
- {
- accountId: 'test-account',
- message: new TextEncoder().encode('message 1')
- },
- {
- accountId: 'test-account',
- message: new TextEncoder().encode('message 2')
- },
- {
- accountId: 'test-account',
- message: new TextEncoder().encode('message 3')
- }
- ]
- const results = await unifiedSigner.signBatch(batchRequests)
- expect(results).toHaveLength(3)
- expect(results[0].batchIndex).toBe(0)
- expect(results[1].batchIndex).toBe(1)
- expect(results[2].batchIndex).toBe(2)
- })
- test('should group requests by platform for optimal processing', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(pacificaAccount)
- const batchRequests: BatchSignRequest[] = [
- {
- accountId: 'pac-account',
- message: new TextEncoder().encode('message 1'),
- options: { forcePlatform: Platform.PACIFICA }
- },
- {
- accountId: 'pac-account',
- message: new TextEncoder().encode('message 2'),
- options: { forcePlatform: Platform.PACIFICA }
- }
- ]
- const results = await unifiedSigner.signBatch(batchRequests)
- expect(results).toHaveLength(2)
- expect(results.every(r => r.success)).toBe(true)
- })
- })
- describe('Performance Metrics', () => {
- test('should collect and provide metrics', async () => {
- const account = {
- id: 'test-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(account)
- // Reset metrics
- unifiedSigner.resetMetrics()
- const message = new TextEncoder().encode('metrics test')
- // Perform some operations
- await unifiedSigner.sign('test-account', message, { collectMetrics: true })
- await unifiedSigner.sign('test-account', message, { collectMetrics: true })
- const metrics = unifiedSigner.getMetrics()
- expect(metrics.totalOperations).toBe(2)
- expect(metrics.successfulOperations).toBe(2)
- expect(metrics.failedOperations).toBe(0)
- expect(metrics.averageSigningTime).toBeGreaterThan(0)
- expect(metrics.platformBreakdown[Platform.PACIFICA].operations).toBe(2)
- expect(metrics.platformBreakdown[Platform.PACIFICA].successRate).toBe(1)
- })
- test('should track failures in metrics', async () => {
- mockGetAccount.mockResolvedValue(null) // Always fail
- unifiedSigner.resetMetrics()
- const message = new TextEncoder().encode('failure test')
- // Perform failing operations
- await unifiedSigner.sign('non-existent', message, { collectMetrics: true })
- await unifiedSigner.sign('non-existent', message, { collectMetrics: true })
- const metrics = unifiedSigner.getMetrics()
- expect(metrics.totalOperations).toBe(2)
- expect(metrics.successfulOperations).toBe(0)
- expect(metrics.failedOperations).toBe(2)
- })
- test('should reset metrics correctly', () => {
- unifiedSigner.resetMetrics()
- const metrics = unifiedSigner.getMetrics()
- expect(metrics.totalOperations).toBe(0)
- expect(metrics.successfulOperations).toBe(0)
- expect(metrics.failedOperations).toBe(0)
- expect(metrics.averageSigningTime).toBe(0)
- expect(metrics.lastResetAt).toBeInstanceOf(Date)
- })
- test('should calculate platform-specific metrics correctly', async () => {
- const pacificaAccount = {
- id: 'pac-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- const asterAccount = {
- id: 'ast-account',
- platform: Platform.ASTER,
- credentials: {
- type: 'secp256k1' as const,
- privateKey: '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef'
- }
- }
- mockGetAccount
- .mockResolvedValueOnce(pacificaAccount)
- .mockResolvedValueOnce(asterAccount)
- .mockResolvedValueOnce(null) // Failure
- unifiedSigner.resetMetrics()
- const message = new TextEncoder().encode('platform metrics test')
- await unifiedSigner.sign('pac-account', message, { collectMetrics: true })
- await unifiedSigner.sign('ast-account', message, { collectMetrics: true })
- await unifiedSigner.sign('non-existent', message, { collectMetrics: true })
- const metrics = unifiedSigner.getMetrics()
- expect(metrics.platformBreakdown[Platform.PACIFICA].operations).toBe(1)
- expect(metrics.platformBreakdown[Platform.PACIFICA].successRate).toBe(1)
- expect(metrics.platformBreakdown[Platform.ASTER].operations).toBe(1)
- expect(metrics.platformBreakdown[Platform.ASTER].successRate).toBe(1)
- })
- })
- describe('Timeout Handling', () => {
- test('should respect custom timeout settings', async () => {
- const slowAccount = {
- id: 'slow-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- // Mock a slow account getter
- mockGetAccount.mockImplementation(() =>
- new Promise(resolve => setTimeout(() => resolve(slowAccount), 100))
- )
- const message = new TextEncoder().encode('timeout test')
- // Test with very short timeout
- const result = await unifiedSigner.sign('slow-account', message, {
- timeout: 50 // 50ms timeout, but getter takes 100ms
- })
- expect(result.success).toBe(false)
- expect(result.error).toContain('timed out')
- })
- test('should use default timeout when not specified', async () => {
- const account = {
- id: 'normal-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(account)
- const message = new TextEncoder().encode('default timeout test')
- const result = await unifiedSigner.sign('normal-account', message)
- expect(result.success).toBe(true) // Should succeed with default timeout
- })
- })
- describe('Error Handling', () => {
- test('should handle account getter errors gracefully', async () => {
- mockGetAccount.mockRejectedValue(new Error('Database connection failed'))
- const message = new TextEncoder().encode('error test')
- const result = await unifiedSigner.sign('test-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain('Database connection failed')
- })
- test('should handle signing errors gracefully', async () => {
- const invalidAccount = {
- id: 'invalid-account',
- platform: Platform.PACIFICA,
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'invalid-key' // This will cause signing to fail
- }
- }
- mockGetAccount.mockResolvedValue(invalidAccount)
- const message = new TextEncoder().encode('signing error test')
- const result = await unifiedSigner.sign('invalid-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toBeDefined()
- })
- test('should provide meaningful error messages', async () => {
- const testCases = [
- {
- mockReturn: null,
- expectedError: 'Account not found'
- },
- {
- mockReturn: { id: 'test', credentials: {} },
- expectedError: 'Cannot determine platform'
- }
- ]
- for (const testCase of testCases) {
- mockGetAccount.mockResolvedValueOnce(testCase.mockReturn)
- const message = new TextEncoder().encode('error message test')
- const result = await unifiedSigner.sign('test-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain(testCase.expectedError)
- }
- })
- })
- describe('Integration with Platform Detectors', () => {
- test('should use platform detector when no platform specified', async () => {
- const pacificaAccount = {
- id: 'auto-detect-account',
- credentials: {
- type: 'ed25519' as const,
- privateKey: 'f26670e2ca334117f8859f9f32e50251641953a30b54f6ffcf82db836cfdfea5'
- }
- }
- mockGetAccount.mockResolvedValue(pacificaAccount)
- const message = new TextEncoder().encode('auto-detect test')
- const result = await unifiedSigner.sign('auto-detect-account', message)
- expect(result.success).toBe(true)
- expect(result.metadata?.platform).toBe(Platform.PACIFICA)
- })
- test('should handle platform detection failures', async () => {
- const unknownAccount = {
- id: 'unknown-account',
- credentials: {
- type: 'unknown-type' as any,
- privateKey: 'some-key'
- }
- }
- mockGetAccount.mockResolvedValue(unknownAccount)
- const message = new TextEncoder().encode('detection failure test')
- const result = await unifiedSigner.sign('unknown-account', message)
- expect(result.success).toBe(false)
- expect(result.error).toContain('Cannot determine platform')
- })
- })
- })
|