UNPKG

@bsv/message-box-client

Version:

A client for P2P messaging and payments

211 lines 9.57 kB
/* eslint-env jest */ import { PeerPayClient } from '../PeerPayClient.js'; import { WalletClient, PrivateKey } from '@bsv/sdk'; import { jest } from '@jest/globals'; const toArray = (msg, enc) => { if (Array.isArray(msg)) return msg.slice(); if (msg === undefined) return []; if (typeof msg !== 'string') { return Array.from(msg, (item) => item | 0); } switch (enc) { case 'hex': { const matches = msg.match(/.{1,2}/g); return matches != null ? matches.map(byte => parseInt(byte, 16)) : []; } case 'base64': return Array.from(Buffer.from(msg, 'base64')); default: return Array.from(Buffer.from(msg, 'utf8')); } }; // Mock dependencies jest.mock('@bsv/sdk', () => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-type-assertion const actualSDK = jest.requireActual('@bsv/sdk'); return { ...actualSDK, WalletClient: jest.fn().mockImplementation(() => ({ getPublicKey: jest.fn(), createAction: jest.fn(), internalizeAction: jest.fn(), createHmac: jest.fn().mockResolvedValue({ hmac: [1, 2, 3, 4, 5] }) })) }; }); describe('PeerPayClient Unit Tests', () => { let peerPayClient; let mockWalletClient; beforeEach(() => { jest.clearAllMocks(); mockWalletClient = new WalletClient(); // Ensure a valid compressed public key (33 bytes, hex format) mockWalletClient.getPublicKey.mockResolvedValue({ publicKey: PrivateKey.fromRandom().toPublicKey().toString() }); mockWalletClient.createAction.mockResolvedValue({ tx: toArray('mockedTransaction', 'utf8') }); peerPayClient = new PeerPayClient({ messageBoxHost: 'https://messagebox.babbage.systems', walletClient: mockWalletClient }); }); describe('createPaymentToken', () => { it('should create a valid payment token', async () => { mockWalletClient.getPublicKey.mockResolvedValue({ publicKey: PrivateKey.fromRandom().toPublicKey().toString() }); mockWalletClient.createAction.mockResolvedValue({ tx: toArray('mockedTransaction', 'utf8') }); const payment = { recipient: PrivateKey.fromRandom().toPublicKey().toString(), amount: 5 }; const token = await peerPayClient.createPaymentToken(payment); expect(token).toHaveProperty('amount', 5); expect(mockWalletClient.getPublicKey).toHaveBeenCalledWith(expect.any(Object)); expect(mockWalletClient.createAction).toHaveBeenCalledWith(expect.any(Object)); }); it('should throw an error if recipient public key cannot be derived', async () => { mockWalletClient.getPublicKey.mockResolvedValue({ publicKey: '' }); // Empty key await expect(peerPayClient.createPaymentToken({ recipient: 'invalid', amount: 5 })) .rejects.toThrow('Failed to derive recipient’s public key'); }); it('should throw an error if amount is <= 0', async () => { mockWalletClient.getPublicKey .mockResolvedValue({ publicKey: PrivateKey.fromRandom().toPublicKey().toString() }); await expect(peerPayClient.createPaymentToken({ recipient: PrivateKey.fromRandom().toPublicKey().toString(), amount: 0 })) .rejects.toThrow('Invalid payment details: recipient and valid amount are required'); }); }); // Test: sendPayment describe('sendPayment', () => { it('should call sendMessage with valid payment', async () => { const sendMessageSpy = jest.spyOn(peerPayClient, 'sendMessage').mockResolvedValue({ status: 'success', messageId: 'mockedMessageId' }); const payment = { recipient: 'recipientKey', amount: 3 }; console.log('[TEST] Calling sendPayment...'); await peerPayClient.sendPayment(payment); console.log('[TEST] sendPayment finished.'); expect(sendMessageSpy).toHaveBeenCalledWith({ recipient: 'recipientKey', messageBox: 'payment_inbox', body: expect.any(String) }); }, 10000); }); // Test: sendLivePayment describe('sendLivePayment', () => { it('should call createPaymentToken and sendLiveMessage with correct parameters', async () => { jest.spyOn(peerPayClient, 'createPaymentToken').mockResolvedValue({ customInstructions: { derivationPrefix: 'prefix', derivationSuffix: 'suffix' }, transaction: Array.from(new Uint8Array([1, 2, 3, 4, 5])), amount: 2 }); jest.spyOn(peerPayClient, 'sendLiveMessage').mockResolvedValue({ status: 'success', messageId: 'mockedMessageId' }); const payment = { recipient: 'recipientKey', amount: 2 }; await peerPayClient.sendLivePayment(payment); expect(peerPayClient.createPaymentToken).toHaveBeenCalledWith(payment); expect(peerPayClient.sendLiveMessage).toHaveBeenCalledWith({ recipient: 'recipientKey', messageBox: 'payment_inbox', body: "{\"customInstructions\":{\"derivationPrefix\":\"prefix\",\"derivationSuffix\":\"suffix\"},\"transaction\":[1,2,3,4,5],\"amount\":2}" }); }); }); // Test: acceptPayment describe('acceptPayment', () => { it('should call internalizeAction and acknowledgeMessage', async () => { mockWalletClient.internalizeAction.mockResolvedValue({ accepted: true }); jest.spyOn(peerPayClient, 'acknowledgeMessage').mockResolvedValue('acknowledged'); const payment = { messageId: '123', sender: 'senderKey', token: { customInstructions: { derivationPrefix: 'prefix', derivationSuffix: 'suffix' }, transaction: toArray('mockedTransaction', 'utf8'), amount: 6 } }; await peerPayClient.acceptPayment(payment); expect(mockWalletClient.internalizeAction).toHaveBeenCalled(); expect(peerPayClient.acknowledgeMessage).toHaveBeenCalledWith({ messageIds: ['123'] }); }); }); // Test: rejectPayment describe('rejectPayment', () => { it('should refund payment minus fee', async () => { jest.spyOn(peerPayClient, 'acceptPayment').mockResolvedValue(undefined); jest.spyOn(peerPayClient, 'sendPayment').mockResolvedValue(undefined); jest.spyOn(peerPayClient, 'acknowledgeMessage').mockResolvedValue('acknowledged'); const payment = { messageId: '123', sender: 'senderKey', token: { customInstructions: { derivationPrefix: 'prefix', derivationSuffix: 'suffix' }, transaction: toArray('mockedTransaction', 'utf8'), amount: 2000 } }; await peerPayClient.rejectPayment(payment); expect(peerPayClient.acceptPayment).toHaveBeenCalledWith(payment); expect(peerPayClient.sendPayment).toHaveBeenCalledWith({ recipient: 'senderKey', amount: 1000 // Deduct satoshi fee }); expect(peerPayClient.acknowledgeMessage).toHaveBeenCalledWith({ messageIds: ['123'] }); }); }); // Test: listIncomingPayments describe('listIncomingPayments', () => { it('should return parsed payment messages', async () => { jest.spyOn(peerPayClient, 'listMessages').mockResolvedValue([ { messageId: '1', sender: 'sender1', created_at: '2025-03-05T12:00:00Z', updated_at: '2025-03-05T12:05:00Z', body: JSON.stringify({ customInstructions: { derivationPrefix: 'prefix1', derivationSuffix: 'suffix1' }, transaction: toArray('mockedTransaction1', 'utf8'), amount: 3 }) }, { messageId: '2', sender: 'sender2', created_at: '2025-03-05T12:10:00Z', updated_at: '2025-03-05T12:15:00Z', body: JSON.stringify({ customInstructions: { derivationPrefix: 'prefix2', derivationSuffix: 'suffix2' }, transaction: toArray('mockedTransaction2', 'utf8'), amount: 9 }) } ]); const payments = await peerPayClient.listIncomingPayments(); expect(payments).toHaveLength(2); expect(payments[0]).toHaveProperty('sender', 'sender1'); expect(payments[0].token.amount).toBe(3); expect(payments[1]).toHaveProperty('sender', 'sender2'); expect(payments[1].token.amount).toBe(9); }); }); }); //# sourceMappingURL=PeerPayClientUnit.test.js.map