@dolaned/wallet-sdk-ts
Version:
Wallet SDK for the Nexa blockchain
456 lines (363 loc) • 16.5 kB
text/typescript
import { describe, test, expect, beforeEach, vi } from 'vitest'
import { TransactionBuilder, Networks, Transaction } from 'libnexa-ts'
import { TransactionCreator } from '../../../src/wallet/transactions/interfaces/TransactionCreator'
import { PermissionLabel } from '../../../src/models/transaction.entities'
// Mock the rostrumProvider
vi.mock('../../../src/network/RostrumProvider', () => ({
rostrumProvider: {
broadcast: vi.fn().mockResolvedValue('txid123')
}
}))
// Mock the WalletUtils
vi.mock('../../../src/utils/WalletUtils', () => ({
isValidNexaAddress: vi.fn().mockReturnValue(true)
}))
// Create a concrete implementation of TransactionCreator for testing
class TestTransactionCreator extends TransactionCreator {
constructor(tx?: TransactionBuilder | string | Buffer) {
super(tx)
}
public parseTxHex(tx: string): this {
// Mock implementation
return this
}
public parseTxBuffer(tx: Buffer): this {
// Mock implementation
return this
}
public populate(): this {
// Mock implementation
return this
}
}
describe('TransactionCreator', () => {
let creator: TestTransactionCreator
beforeEach(() => {
creator = new TestTransactionCreator()
})
describe('constructor', () => {
test('should create instance without transaction builder', () => {
const creator = new TestTransactionCreator()
expect(creator).toBeInstanceOf(TransactionCreator)
expect(creator.network).toBe(Networks.mainnet)
expect(creator.totalValue).toBe(BigInt(0))
})
test('should create instance with transaction builder', () => {
const txBuilder = new TransactionBuilder()
const creator = new TestTransactionCreator(txBuilder)
expect(creator).toBeInstanceOf(TransactionCreator)
})
})
describe('onNetwork', () => {
test('should set network by string', () => {
const result = creator.onNetwork('testnet')
expect(result).toBe(creator)
expect(creator.network).toBe(Networks.testnet)
})
test('should set network by Networkish object', () => {
const result = creator.onNetwork(Networks.testnet)
expect(result).toBe(creator)
expect(creator.network).toBe(Networks.testnet)
})
test('should be chainable', () => {
const result = creator
.onNetwork('testnet')
.onNetwork('mainnet')
expect(result).toBe(creator)
expect(creator.network).toBe(Networks.mainnet)
})
})
describe('property getters and setters', () => {
test('should get and set txOptions', () => {
const options = { feeFromAmount: true }
creator.txOptions = options
expect(creator.txOptions).toEqual(options)
})
test('should get and set network', () => {
creator.network = Networks.testnet
expect(creator.network).toBe(Networks.testnet)
})
test('should get and set totalValue', () => {
const value = BigInt(1000)
creator.totalValue = value
expect(creator.totalValue).toBe(value)
})
test('should get and set builder', () => {
const mockFn = vi.fn()
creator.builder = [mockFn]
expect(creator.builder).toHaveLength(1)
expect(creator.builder[0]).toBe(mockFn)
})
test('should get and set tokens', () => {
const tokenAction = {
token: 'test-token',
amount: BigInt(100),
action: 'send'
}
creator.tokens.add(tokenAction)
expect(creator.tokens.size).toBe(1)
expect(creator.tokens.has(tokenAction)).toBe(true)
})
})
describe('consolidate', () => {
test('should add consolidate operation to builder', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const result = creator.consolidate(toAddr)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const result = creator
.consolidate(toAddr)
.consolidate(toAddr)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('feeFromAmount', () => {
test('should add feeFromAmount operation to builder', () => {
const result = creator.feeFromAmount()
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const result = creator
.feeFromAmount()
.feeFromAmount()
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('sendToToken', () => {
test('should add token send operation to builder', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const amount = '1000'
const token = 'nexatest:tqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const result = creator.sendToToken(toAddr, amount, token)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const amount = '1000'
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const result = creator
.sendToToken(toAddr, amount, token)
.sendToToken(toAddr, amount, token)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('sendTo', () => {
test('should add NEXA send operation to builder', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const amount = '1000'
const result = creator.sendTo(toAddr, amount)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const amount = '1000'
const result = creator
.sendTo(toAddr, amount)
.sendTo(toAddr, amount)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('renewAuthority', () => {
test('should add renew authority operation to builder', () => {
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const perms: PermissionLabel[] = ['mint', 'melt']
const result = creator.renewAuthority(token, perms)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should add renew authority operation with address', () => {
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const perms: PermissionLabel[] = ['mint', 'melt']
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const result = creator.renewAuthority(token, perms, toAddr)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
})
describe('deleteAuthority', () => {
test('should add delete authority operation to builder', () => {
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const outpoint = 'abc123:0'
const result = creator.deleteAuthority(token, outpoint)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const outpoint = 'abc123:0'
const result = creator
.deleteAuthority(token, outpoint)
.deleteAuthority(token, outpoint)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('legacy methods', () => {
test('legacyToken should add operation to builder', () => {
const result = creator.legacyToken('Test Token', 'TEST', 2, 'https://test.com', 'hash123')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('legacyGroup should add operation to builder', () => {
const result = creator.legacyGroup('Test Group', 'TGRP', 'https://group.com', 'hash456')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const result = creator
.legacyToken('Test Token', 'TEST', 2, 'https://test.com', 'hash123')
.legacyGroup('Test Group', 'TGRP', 'https://group.com', 'hash456')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('token', () => {
test('should add token creation to builder', () => {
const result = creator.token('Test Token', 'TEST', 2, 'https://test.com', 'hash123')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const result = creator
.token('Test Token', 'TEST', 2, 'https://test.com', 'hash123')
.token('Test Token 2', 'TEST2', 4, 'https://test2.com', 'hash456')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('collection', () => {
test('should add collection creation to builder', () => {
const result = creator.collection('Test Collection', 'TCOL', 'https://collection.com', 'hash123')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const result = creator
.collection('Test Collection', 'TCOL', 'https://collection.com', 'hash123')
.collection('Test Collection 2', 'TCOL2', 'https://collection2.com', 'hash456')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('NFT', () => {
test('should add NFT creation to builder', () => {
const parent = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const result = creator.nft(parent, 'https://nft.com/content.zip', 'hash123')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const parent = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const result = creator
.nft(parent, 'https://nft1.com/content.zip', 'hash123')
.nft(parent, 'https://nft2.com/content.zip', 'hash456')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('addOpReturn', () => {
test('should add OP_RETURN with string data', () => {
const data = 'Hello World'
const result = creator.addOpReturn(data)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should add OP_RETURN with buffer data', () => {
const data = Buffer.from('Hello World')
const result = creator.addOpReturn(data)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should add OP_RETURN with full script', () => {
const data = 'Hello World'
const result = creator.addOpReturn(data, true)
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(1)
})
test('should be chainable', () => {
const result = creator
.addOpReturn('Hello')
.addOpReturn('World')
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2)
})
})
describe('build', () => {
test('should execute all builder functions and return serialized transaction', async () => {
const mockFunction = vi.fn().mockResolvedValue(undefined)
creator.builder = [mockFunction]
const result = await creator.build()
expect(mockFunction).toHaveBeenCalledTimes(1)
expect(typeof result).toBe('string')
})
test('should execute multiple builder functions in order', async () => {
const mockFunction1 = vi.fn().mockResolvedValue(undefined)
const mockFunction2 = vi.fn().mockResolvedValue(undefined)
creator.builder = [mockFunction1, mockFunction2]
await creator.build()
expect(mockFunction1).toHaveBeenCalledTimes(1)
expect(mockFunction2).toHaveBeenCalledTimes(1)
})
})
describe('integration tests', () => {
test('should handle complete transaction flow', async () => {
const toAddr = 'nexatest:nqtsq5g5jsdmqqywaqd82lhnnk3a8wqunjz6gtxdtavnnekc'
const amount = '1000'
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const result = creator
.onNetwork('testnet')
.sendTo(toAddr, amount)
.sendToToken(toAddr, amount, token)
.addOpReturn('Transaction data')
.feeFromAmount()
.populate()
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(4) // sendTo, sendToToken, addOpReturn, feeFromAmount (populate not called yet)
expect(creator.network).toBe(Networks.testnet)
const tx = await creator.build()
expect(typeof tx).toBe('string')
})
test('should handle token authority operations', async () => {
const token = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const perms: PermissionLabel[] = ['mint', 'melt']
const outpoint = 'abc123:0'
const result = creator
.renewAuthority(token, perms)
.deleteAuthority(token, outpoint)
.populate()
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(2) // renewAuthority, deleteAuthority (populate not called yet)
const tx = await creator.build()
expect(typeof tx).toBe('string')
})
test('should handle token and collection creation', async () => {
const parent = 'nexatest:tq8r37lcjlqazz7vuvug84q2ev50573hesrnxkv9y6hvhhl5k5qqqnmyf79mx'
const result = creator
.token('Test Token', 'TEST', 2, 'https://test.com', 'hash123')
.collection('Test Collection', 'TCOL', 'https://collection.com', 'hash456')
.nft(parent, 'https://nft.com/content.zip', 'hash789')
.populate()
expect(result).toBe(creator)
expect(creator.builder).toHaveLength(3) // token, collection, NFT (populate not called yet)
const tx = await creator.build()
expect(typeof tx).toBe('string')
})
})
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')
})
})
})