UNPKG

@dolaned/wallet-sdk-ts

Version:

Wallet SDK for the Nexa blockchain

363 lines (293 loc) 12.3 kB
import { describe, test, expect, beforeEach, vi } from 'vitest' import { TransactionBuilder, Networks, PrivateKey } from 'libnexa-ts' import WalletTransactionCreator from '../../../src/wallet/transactions/WalletTransactionCreator' import { BaseAccount } from '../../../src/wallet/accounts/interfaces/BaseAccountInterface' import { AddressKey } from '../../../src/models/wallet.entities' // Mock the TXUtils functions vi.mock('../../../src/utils/TXUtils', () => ({ populateTokenAuth: vi.fn().mockResolvedValue([{} as PrivateKey]), buildCreateGroupTransaction: vi.fn().mockResolvedValue([{} as PrivateKey]), populateAndDuplicateTokenAuths: vi.fn().mockResolvedValue([{} as PrivateKey]), prepareDeleteTransaction: vi.fn().mockResolvedValue([{} as PrivateKey]), populateTokenInputsAndChange: vi.fn().mockResolvedValue([{} as PrivateKey]), populateNexaInputsAndChange: vi.fn().mockResolvedValue([{} as PrivateKey]) })) // Mock the rostrumProvider vi.mock('../../../src/network/RostrumProvider', () => ({ rostrumProvider: { getUtxo: vi.fn().mockResolvedValue({ tx_hash: 'mock-tx-hash', amount: 1000000, scriptpubkey: 'mock-scriptpubkey', addresses: ['nexatest:address123'] }), getBlockTip: vi.fn().mockResolvedValue({ height: 100000 }) } })) describe('WalletTransactionCreator', () => { let creator: WalletTransactionCreator let mockAccount: BaseAccount beforeEach(() => { mockAccount = { accountKeys: { receiveKeys: [ { key: { privateKey: {} as PrivateKey }, address: 'nexatest:receive123', balance: '1000000', tokensBalance: {} } ], changeKeys: [ { key: { privateKey: {} as PrivateKey }, address: 'nexatest:change123', balance: '500000', tokensBalance: {} } ] } } as BaseAccount creator = new WalletTransactionCreator(mockAccount) }) describe('constructor', () => { test('should create instance with account', () => { expect(creator).toBeInstanceOf(WalletTransactionCreator) }) test('should create instance with account and transaction builder', () => { const txBuilder = new TransactionBuilder() const creator = new WalletTransactionCreator(mockAccount, txBuilder) expect(creator).toBeInstanceOf(WalletTransactionCreator) }) test('should throw error for account without receive keys', () => { const invalidAccount = { accountKeys: { receiveKeys: [], changeKeys: [] } } as unknown as BaseAccount expect(() => { new WalletTransactionCreator(invalidAccount) }).toThrow('No receive keys available in account') }) test('should throw error for account without account keys', () => { const invalidAccount = {} as BaseAccount expect(() => { new WalletTransactionCreator(invalidAccount) }).toThrow('Account keys are not initialized') }) }) describe('fromAccount', () => { test('should set account and return this for chaining', () => { const newAccount = { accountKeys: { receiveKeys: [{ key: {privateKey: {} as PrivateKey}, address: 'nexatest:new123', balance: '0', tokensBalance: {} }], changeKeys: [] } } as unknown as BaseAccount const result = creator.fromAccount(newAccount) expect(result).toBe(creator) expect(creator['_account']).toBe(newAccount) }) }) describe('parseTxHex', () => { test('should add parse operation to builder', () => { const hex = 'deadbeef' const result = creator.parseTxHex(hex) expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) }) describe('parseTxBuffer', () => { test('should add parse operation to builder', () => { const buffer = Buffer.from('deadbeef', 'hex') const result = creator.parseTxBuffer(buffer) expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) }) describe('mint', () => { test('should add mint operation to builder', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const result = creator.mint(token, amount) expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) test('should be chainable', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const result = creator .mint(token, amount) .mint(token, amount) expect(result).toBe(creator) expect(creator.builder).toHaveLength(2) }) }) describe('melt', () => { test('should add melt operation to builder', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const result = creator.melt(token, amount) expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) test('should be chainable', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const result = creator .melt(token, amount) .melt(token, amount) expect(result).toBe(creator) expect(creator.builder).toHaveLength(2) }) }) describe('populate', () => { test('should add populate operation to builder', () => { const result = creator.populate() expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) test('should validate account before populating', () => { const invalidCreator = new WalletTransactionCreator(mockAccount) invalidCreator['_account'] = null as any expect(() => { invalidCreator.populate() }).toThrow('Account must be set before performing transactions') }) }) describe('sign', () => { test('should add sign operation to builder', () => { const result = creator.sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(1) }) test('should be chainable', () => { const result = creator .populate() .sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(2) }) }) describe('findPrivateKeyFromAddress', () => { test('should find key for existing address', () => { const result = creator['findPrivateKeyFromAddress']('nexatest:receive123') expect(result).toBeDefined() expect(result?.address).toBe('nexatest:receive123') }) test('should find key in change addresses', () => { const result = creator['findPrivateKeyFromAddress']('nexatest:change123') expect(result).toBeDefined() expect(result?.address).toBe('nexatest:change123') }) test('should return undefined for non-existent address', () => { const result = creator['findPrivateKeyFromAddress']('nexatest:nonexistent') expect(result).toBeUndefined() }) }) describe('validateAccount', () => { test('should not throw for valid account', () => { expect(() => { creator['validateAccount']() }).not.toThrow() }) test('should throw for null account', () => { creator['_account'] = null as any expect(() => { creator['validateAccount']() }).toThrow('Account must be set before performing transactions') }) test('should throw for account without keys', () => { creator['_account'] = {} as BaseAccount expect(() => { creator['validateAccount']() }).toThrow('Account keys are not initialized') }) test('should throw for account without receive keys', () => { creator['_account'] = { accountKeys: { receiveKeys: [], changeKeys: [] } } as unknown as BaseAccount expect(() => { creator['validateAccount']() }).toThrow('No receive keys available in account') }) }) describe('integration tests', () => { test('should handle complete transaction flow', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const result = creator .onNetwork('testnet') .sendTo(toAddr, amount) .mint(token, amount) .populate() .sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(4) // sendTo, mint, populate, sign expect(creator.network).toBe(Networks.testnet) }) test('should handle token operations', () => { const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc' const amount = '1000' const result = creator .mint(token, amount) .melt(token, amount) .populate() .sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(4) }) test('should handle token creation', () => { const result = creator .token('Test Token', 'TEST', 2, 'https://test.com', 'hash123') .populate() .sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(3) }) test('should handle transaction parsing and signing', () => { const hexTx = 'deadbeef' const result = creator .parseTxHex(hexTx) .sign() expect(result).toBe(creator) expect(creator.builder).toHaveLength(2) }) }) describe('error handling', () => { test('should handle async errors in builder functions', async () => { const errorFunction = vi.fn().mockRejectedValue(new Error('Test error')) creator.builder = [errorFunction] await expect(creator.build()).rejects.toThrow('Test error') }) test('should handle validation errors during operations', () => { const invalidCreator = new WalletTransactionCreator(mockAccount) invalidCreator['_account'] = null as any expect(() => { invalidCreator.mint('token', '1000') }).not.toThrow() // Error should be thrown when builder executes, not when adding to builder }) }) describe('network handling', () => { test('should set network correctly', () => { const result = creator.onNetwork('testnet') expect(result).toBe(creator) expect(creator.network).toBe(Networks.testnet) }) test('should default to mainnet', () => { expect(creator.network).toBe(Networks.mainnet) }) }) })