UNPKG

@dolaned/wallet-sdk-ts

Version:

Wallet SDK for the Nexa blockchain

252 lines (201 loc) 9.17 kB
import { describe, test, expect, beforeEach, vi } from 'vitest' import { Networks, TransactionBuilder } from 'libnexa-ts' import WatchOnlyWallet from '../../../src/wallet/WatchOnlyWallet' import { WatchOnlyAddress } from '../../../src/models/wallet.entities' // Mock the rostrumProvider vi.mock('../../../src/network/RostrumProvider', () => ({ rostrumProvider: { broadcast: vi.fn().mockResolvedValue('txid123'), subscribeToAddresses: vi.fn().mockResolvedValue(undefined) } })) // Mock the ValidationUtils vi.mock('../../../src/utils/ValidationUtils', () => ({ default: { validateArgument: vi.fn() } })) // Mock the WalletUtils vi.mock('../../../src/utils/WalletUtils', () => ({ isValidNexaAddress: vi.fn().mockReturnValue(true) })) describe('WatchOnlyWallet', () => { let mockAddresses: WatchOnlyAddress[] beforeEach(() => { mockAddresses = [ { address: 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' }, { address: 'nexatest:nqtsq5g5dsgh6mwjchqypn8hvdrjue0xpmz293fl7rm926xv' } ] }) describe('constructor', () => { test('should create wallet with valid addresses and default network', () => { const wallet = new WatchOnlyWallet(mockAddresses) expect(wallet).toBeInstanceOf(WatchOnlyWallet) expect(wallet.getWatchedAddresses()).toHaveLength(2) expect(wallet.getWatchedAddresses()[0].address).toBe(mockAddresses[0].address) }) test('should create wallet with custom network', () => { const wallet = new WatchOnlyWallet(mockAddresses, 'testnet') expect(wallet).toBeInstanceOf(WatchOnlyWallet) expect(wallet['_network']).toBe(Networks.testnet) }) test('should throw error for invalid network type', () => { expect(() => { new WatchOnlyWallet(mockAddresses, 123 as any) }).toThrow('Network must be a string') }) test('should throw error for invalid network', () => { expect(() => { new WatchOnlyWallet(mockAddresses, 'invalidnetwork') }).toThrow('Invalid network: invalidnetwork') }) test('should throw error for null addresses', () => { expect(() => { new WatchOnlyWallet(null as any) }).toThrow('addresesToWatch is required') }) test('should throw error for non-array addresses', () => { expect(() => { new WatchOnlyWallet('not-an-array' as any) }).toThrow('addressesToWatch must be an array') }) test('should throw error for empty addresses array', () => { expect(() => { new WatchOnlyWallet([]) }).toThrow('addressesToWatch cannot be empty') }) test('should throw error for invalid address object', () => { expect(() => { new WatchOnlyWallet(['invalid'] as any) }).toThrow('addressesToWatch[0] must be an object') }) test('should throw error for missing address property', () => { expect(() => { new WatchOnlyWallet([{ notAddress: 'test' }] as any) }).toThrow('addressesToWatch[0].address must be a string') }) test('should throw error for empty address string', () => { expect(() => { new WatchOnlyWallet([{ address: '' }]) }).toThrow('addressesToWatch[0].address cannot be empty') }) test('should throw error for duplicate addresses', () => { const duplicates = [ { address: 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' }, { address: 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' } ] expect(() => { new WatchOnlyWallet(duplicates) }).toThrow('Duplicate address found') }) test('should handle optional xPub property', () => { const addressesWithXPub = [ { address: 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc', xPub: {} as any } ] const wallet = new WatchOnlyWallet(addressesWithXPub) expect(wallet.getWatchedAddresses()[0].xPub).toBeDefined() }) test('should handle optional derivationPath property', () => { const addressesWithPath = [ { address: 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc', derivationPath: "m/44'/29223'/0'/0/0" } ] const wallet = new WatchOnlyWallet(addressesWithPath) expect(wallet.getWatchedAddresses()[0].derivationPath).toBe("m/44'/29223'/0'/0/0") }) }) describe('newTransaction', () => { let wallet: WatchOnlyWallet beforeEach(() => { wallet = new WatchOnlyWallet(mockAddresses, 'testnet') }) test('should create transaction without parameters', () => { const tx = wallet.newTransaction() expect(tx).toBeDefined() expect(tx.network).toBe(Networks.testnet) }) test('should create transaction with TransactionBuilder', () => { const txBuilder = new TransactionBuilder() const tx = wallet.newTransaction(txBuilder) expect(tx).toBeDefined() }) test('should create transaction with hex string', () => { const hexTx = 'deadbeef' const tx = wallet.newTransaction(hexTx) expect(tx).toBeDefined() }) test('should create transaction with buffer', () => { const bufferTx = Buffer.from('deadbeef', 'hex') const tx = wallet.newTransaction(bufferTx) expect(tx).toBeDefined() }) }) describe('sendTransaction', () => { let wallet: WatchOnlyWallet beforeEach(() => { wallet = new WatchOnlyWallet(mockAddresses) }) test('should broadcast transaction and return txid', async () => { const txHex = 'deadbeef' const result = await wallet.sendTransaction(txHex) expect(result).toBe('txid123') }) }) describe('subscribeToAddressNotifications', () => { let wallet: WatchOnlyWallet beforeEach(() => { wallet = new WatchOnlyWallet(mockAddresses) }) test('should subscribe to address notifications', async () => { const callback = vi.fn() await wallet.subscribeToAddressNotifications(callback) // Should not throw and complete successfully expect(callback).not.toHaveBeenCalled() // Callback is passed to provider, not called directly }) }) describe('getWatchedAddresses', () => { test('should return copy of watched addresses', () => { const wallet = new WatchOnlyWallet(mockAddresses) const addresses = wallet.getWatchedAddresses() expect(addresses).toHaveLength(2) expect(addresses).not.toBe(wallet['_addressesToWatch']) // Should be a copy expect(addresses[0].address).toBe(mockAddresses[0].address) }) }) describe('validation edge cases', () => { test('should trim whitespace from addresses', () => { const addressesWithWhitespace = [ { address: ' nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc ' } ] const wallet = new WatchOnlyWallet(addressesWithWhitespace) expect(wallet.getWatchedAddresses()[0].address).toBe('nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc') }) test('should handle mixed address formats', () => { const mixedAddresses = [ 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc', { address: 'nexatest:nqtsq5g5dsgh6mwjchqypn8hvdrjue0xpmz293fl7rm926xv' } ] // This should work with the updated from() method in transaction creator const wallet = new WatchOnlyWallet([ { address: mixedAddresses[0] as string }, mixedAddresses[1] as WatchOnlyAddress ]) expect(wallet.getWatchedAddresses()).toHaveLength(2) }) }) describe('network handling', () => { test('should default to mainnet when network is undefined', () => { const wallet = new WatchOnlyWallet(mockAddresses, undefined) expect(wallet['_network']).toBe(Networks.mainnet) }) test('should handle testnet correctly', () => { const wallet = new WatchOnlyWallet(mockAddresses, 'testnet') expect(wallet['_network']).toBe(Networks.testnet) }) }) })