UNPKG

@dolaned/wallet-sdk-ts

Version:

Wallet SDK for the Nexa blockchain

341 lines (288 loc) 13.1 kB
import { describe, test, expect, beforeEach, vi } from 'vitest' import { HDPrivateKey, Networks } from 'libnexa-ts' import AccountStore from '../../../src/wallet/accounts/AccountStore' import { AccountType } from '../../../src/utils/WalletUtils' import { BaseAccount } from '../../../src/wallet/accounts/interfaces/BaseAccountInterface' import DAppAccount from '../../../src/wallet/accounts/models/DappAccount' import VaultAccount from '../../../src/wallet/accounts/models/VaultAccount' import DefaultAccount from '../../../src/wallet/accounts/models/DefaultAccount' // Mock the WalletUtils vi.mock('../../../src/utils/WalletUtils', () => ({ AccountType: { NEXA_ACCOUNT: 0, VAULT_ACCOUNT: 1, DAPP_ACCOUNT: 2 }, getNextAccountIndex: vi.fn().mockResolvedValue(0), generateAccountKey: vi.fn().mockReturnValue({ deriveChild: vi.fn().mockReturnValue({ privateKey: { toAddress: vi.fn().mockReturnValue({ toString: vi.fn().mockReturnValue('nexatest:address123') }) } }) }), generateKeyAndAddress: vi.fn().mockReturnValue({ key: { privateKey: { toAddress: vi.fn().mockReturnValue({ toString: vi.fn().mockReturnValue('nexatest:address123') }) } }, address: 'nexatest:address123', balance: '0', tokensBalance: {} }), generateKeysAndAddresses: vi.fn().mockReturnValue({ receiveKeys: [{ key: { privateKey: {} }, address: 'nexatest:receive123', balance: '0', tokensBalance: {} }], changeKeys: [{ key: { privateKey: {} }, address: 'nexatest:change123', balance: '0', tokensBalance: {} }] }) })) // Mock the account classes vi.mock('../../../src/wallet/accounts/models/DappAccount', () => ({ default: vi.fn().mockImplementation((purpose, index, addressKey) => ({ purpose, index, addressKey, accountKeys: { receiveKeys: [addressKey], changeKeys: [] }, loadBalances: vi.fn().mockResolvedValue(undefined), getAccountStoreKey: vi.fn().mockReturnValue(`${purpose}.${index}`) })) })) vi.mock('../../../src/wallet/accounts/models/VaultAccount', () => ({ default: vi.fn().mockImplementation((purpose, index, addressKey) => ({ purpose, index, addressKey, accountKeys: { receiveKeys: [addressKey], changeKeys: [] }, loadBalances: vi.fn().mockResolvedValue(undefined), getAccountStoreKey: vi.fn().mockReturnValue(`${purpose}.${index}`) })) })) vi.mock('../../../src/wallet/accounts/models/DefaultAccount', () => ({ default: vi.fn().mockImplementation((index, indexes, keys) => ({ index, indexes, accountKeys: keys, loadBalances: vi.fn().mockResolvedValue(undefined), getAccountStoreKey: vi.fn().mockReturnValue(String(index)) })) })) describe('AccountStore', () => { let accountStore: AccountStore let mockMasterKey: HDPrivateKey beforeEach(() => { accountStore = new AccountStore() mockMasterKey = {} as HDPrivateKey vi.clearAllMocks() }) describe('constructor', () => { test('should create empty account store', () => { expect(accountStore).toBeInstanceOf(AccountStore) expect(accountStore.listAccounts().size).toBe(0) }) }) describe('createAccount', () => { test('should create DApp account', async () => { const account = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) expect(account).toBeDefined() expect(DAppAccount).toHaveBeenCalledWith(2, 0, expect.any(Object)) expect(accountStore.listAccounts().size).toBe(1) }) test('should create Vault account', async () => { const account = await accountStore.createAccount(AccountType.VAULT_ACCOUNT, mockMasterKey) expect(account).toBeDefined() expect(VaultAccount).toHaveBeenCalledWith(1, 0, expect.any(Object)) expect(accountStore.listAccounts().size).toBe(1) }) test('should create Default NEXA account', async () => { const account = await accountStore.createAccount(AccountType.NEXA_ACCOUNT, mockMasterKey) expect(account).toBeDefined() expect(DefaultAccount).toHaveBeenCalledWith(0, expect.any(Object), expect.any(Object)) expect(accountStore.listAccounts().size).toBe(1) }) test('should return existing account if already created', async () => { const account1 = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) const account2 = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) expect(account1).toBe(account2) expect(accountStore.listAccounts().size).toBe(1) }) }) describe('getAccountStoreKey', () => { test('should generate correct key for DApp account', () => { const key = accountStore['getAccountStoreKey'](AccountType.DAPP_ACCOUNT, 0) expect(key).toBe('2.0') }) test('should generate correct key for Vault account', () => { const key = accountStore['getAccountStoreKey'](AccountType.VAULT_ACCOUNT, 1) expect(key).toBe('1.1') }) test('should generate correct key for Default account', () => { const key = accountStore['getAccountStoreKey'](AccountType.NEXA_ACCOUNT, 0) expect(key).toBe('0') }) }) describe('findKeyForAddress', () => { test('should find key for existing address', async () => { const mockAccount = { accountKeys: { receiveKeys: [{ address: 'nexatest:target123', key: { privateKey: 'mock-key' } }], changeKeys: [{ address: 'nexatest:change123', key: { privateKey: 'mock-key-2' } }] } } accountStore['_accounts'].set('test', mockAccount as any) const result = accountStore.findKeyForAddress('nexatest:target123') expect(result).toBeDefined() expect(result?.address).toBe('nexatest:target123') }) test('should return null for non-existent address', () => { const result = accountStore.findKeyForAddress('nexatest:nonexistent') expect(result).toBeNull() }) test('should search through both receive and change keys', async () => { const mockAccount = { accountKeys: { receiveKeys: [{ address: 'nexatest:receive123', key: { privateKey: 'mock-key' } }], changeKeys: [{ address: 'nexatest:change123', key: { privateKey: 'mock-key-2' } }] } } accountStore['_accounts'].set('test', mockAccount as any) const receiveResult = accountStore.findKeyForAddress('nexatest:receive123') const changeResult = accountStore.findKeyForAddress('nexatest:change123') expect(receiveResult?.address).toBe('nexatest:receive123') expect(changeResult?.address).toBe('nexatest:change123') }) }) describe('importAccount', () => { test('should import new account', () => { const mockAccount = { getAccountStoreKey: vi.fn().mockReturnValue('imported.0') } as any accountStore.importAccount(mockAccount) expect(accountStore.listAccounts().size).toBe(1) expect(accountStore.getAccount('imported.0')).toBe(mockAccount) }) test('should throw error for duplicate account', () => { const mockAccount = { getAccountStoreKey: vi.fn().mockReturnValue('duplicate.0') } as any accountStore.importAccount(mockAccount) expect(() => { accountStore.importAccount(mockAccount) }).toThrow('Account already exists!') }) }) describe('exportAccount', () => { test('should export existing account', () => { const mockAccount = { getAccountStoreKey: vi.fn().mockReturnValue('export.0') } as any accountStore['_accounts'].set('export.0', mockAccount) const exported = accountStore.exportAccount('export.0') expect(exported).toBe(mockAccount) }) test('should throw error for non-existent account', () => { expect(() => { accountStore.exportAccount('nonexistent') }).toThrow('Cannot find account!') }) }) describe('removeAccount', () => { test('should remove existing account', () => { const mockAccount = { getAccountStoreKey: vi.fn().mockReturnValue('remove.0') } as any accountStore['_accounts'].set('remove.0', mockAccount) expect(accountStore.listAccounts().size).toBe(1) accountStore.removeAccount('remove.0') expect(accountStore.listAccounts().size).toBe(0) }) test('should throw error for non-existent account', () => { expect(() => { accountStore.removeAccount('nonexistent') }).toThrow('Cannot find account!') }) }) describe('listAccounts', () => { test('should return all accounts', async () => { await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) await accountStore.createAccount(AccountType.VAULT_ACCOUNT, mockMasterKey) const accounts = accountStore.listAccounts() expect(accounts.size).toBe(2) }) test('should return empty map when no accounts', () => { const accounts = accountStore.listAccounts() expect(accounts.size).toBe(0) expect(accounts).toBeInstanceOf(Map) }) }) describe('getAccount', () => { test('should return account by index', async () => { const account = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) const retrieved = accountStore.getAccount('2.0') expect(retrieved).toBe(account) }) test('should return undefined for non-existent account', () => { const retrieved = accountStore.getAccount('nonexistent') expect(retrieved).toBeUndefined() }) }) describe('error handling', () => { test('should handle account creation errors gracefully', async () => { // Mock getNextAccountIndex to throw an error const { getNextAccountIndex } = await import('../../../src/utils/WalletUtils') vi.mocked(getNextAccountIndex).mockRejectedValueOnce(new Error('Network error')) await expect( accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) ).rejects.toThrow('Network error') }) }) describe('integration tests', () => { test('should handle multiple account types', async () => { const dappAccount = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) const vaultAccount = await accountStore.createAccount(AccountType.VAULT_ACCOUNT, mockMasterKey) const nexaAccount = await accountStore.createAccount(AccountType.NEXA_ACCOUNT, mockMasterKey) expect(accountStore.listAccounts().size).toBe(3) expect(accountStore.getAccount('2.0')).toBe(dappAccount) expect(accountStore.getAccount('1.0')).toBe(vaultAccount) expect(accountStore.getAccount('0')).toBe(nexaAccount) }) test('should maintain account independence', async () => { const account1 = await accountStore.createAccount(AccountType.DAPP_ACCOUNT, mockMasterKey) const account2 = await accountStore.createAccount(AccountType.VAULT_ACCOUNT, mockMasterKey) accountStore.removeAccount('2.0') expect(accountStore.getAccount('2.0')).toBeUndefined() expect(accountStore.getAccount('1.0')).toBe(account2) }) }) })