From dfcaa5a8d43fa9e81ef35ad0d0e223875e1d8b1d Mon Sep 17 00:00:00 2001 From: raizo07 Date: Tue, 26 Nov 2024 18:42:19 +0100 Subject: [PATCH] feat: Improve Test Coverage for packages/web3/src/signer --- packages/web3/src/signer/signer.test.ts | 222 ++++++++++++ packages/web3/src/signer/tx-builder.test.ts | 356 ++++++++++++++++++++ packages/web3/src/signer/types.test.ts | 252 ++++++++++++++ 3 files changed, 830 insertions(+) create mode 100644 packages/web3/src/signer/signer.test.ts create mode 100644 packages/web3/src/signer/tx-builder.test.ts create mode 100644 packages/web3/src/signer/types.test.ts diff --git a/packages/web3/src/signer/signer.test.ts b/packages/web3/src/signer/signer.test.ts new file mode 100644 index 000000000..70c3c5aaa --- /dev/null +++ b/packages/web3/src/signer/signer.test.ts @@ -0,0 +1,222 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ +import { + SignerProvider, + extendMessage, + hashMessage, + verifySignedMessage, + toApiDestination, + toApiDestinations, + fromApiDestination +} from './signer' +import * as utils from '../utils' +import { + Account, + MessageHasher, + SignTransferTxParams, + SignTransferTxResult, + SignDeployContractTxParams, + SignDeployContractTxResult, + SignExecuteScriptTxParams, + SignExecuteScriptTxResult, + SignUnsignedTxParams, + SignUnsignedTxResult, + SignChainedTxParams, + SignChainedTxResult, + SignMessageParams, + SignMessageResult +} from './types' +import { NodeProvider } from '../api' + +describe('SignerModule', () => { + // SignerProvider tests + describe('SignerProvider', () => { + class MockSignerProvider extends SignerProvider { + nodeProvider: NodeProvider | undefined = undefined + explorerProvider = undefined + + async unsafeGetSelectedAccount(): Promise { + return { + address: 'test-address', + publicKey: 'test-public-key', + keyType: 'secp256k1', + group: 0 + } + } + + async signAndSubmitTransferTx(params: SignTransferTxParams): Promise { + return { + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + } + } + + async signAndSubmitDeployContractTx(params: SignDeployContractTxParams): Promise { + return { + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + } + } + + async signAndSubmitExecuteScriptTx(params: SignExecuteScriptTxParams): Promise { + return { + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + } + } + + async signAndSubmitUnsignedTx(params: SignUnsignedTxParams): Promise { + return { + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + } + } + + async signAndSubmitChainedTx(params: SignChainedTxParams[]): Promise { + return params.map(() => ({ + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + })) + } + + async signUnsignedTx(params: SignUnsignedTxParams): Promise { + return { + txId: 'mock-tx-id', + unsignedTx: 'mock-unsigned-tx', + signature: 'mock-signature' + } + } + + async signMessage(params: SignMessageParams): Promise { + return { + signature: 'mock-signature' + } + } + } + + describe('validateAccount', () => { + it('should validate a correct account', () => { + const account: Account = { + address: 'test-address', + publicKey: 'test-public-key', + keyType: 'secp256k1', + group: 0 + } + expect(() => SignerProvider.validateAccount(account)).not.toThrow() + }) + + it('should throw error for invalid account', () => { + const account: Account = { + address: 'wrong-address', + publicKey: 'test-public-key', + keyType: 'secp256k1', + group: 0 + } + expect(() => SignerProvider.validateAccount(account)).toThrow('Invalid accounot data') + }) + }) + + describe('getSelectedAccount', () => { + it('should return validated account', async () => { + const mockProvider = new MockSignerProvider() + const account = await mockProvider.getSelectedAccount() + expect(account.address).toBe('test-address') + }) + }) + }) + + // Utility Functions Tests + describe('Utility Functions', () => { + describe('extendMessage', () => { + it('should prefix message with Alephium Signed Message', () => { + const message = 'hello' + expect(extendMessage(message)).toBe('Alephium Signed Message: hello') + }) + }) + + describe('hashMessage', () => { + const message = 'test' + const hashers: MessageHasher[] = ['alephium', 'sha256', 'blake2b', 'identity'] + + hashers.forEach((hasher) => { + it(`should hash message with ${hasher} hasher`, () => { + const hash = hashMessage(message, hasher) + expect(hash).toBeTruthy() + expect(hash.length).toBeGreaterThan(0) + }) + }) + + it('should throw error for invalid hasher', () => { + expect(() => hashMessage(message, 'invalid' as MessageHasher)).toThrow('Invalid message hasher') + }) + }) + + describe('verifySignedMessage', () => { + const mockPublicKey = 'mock-public-key' + const mockSignature = 'mock-signature' + const message = 'test message' + const hashers: MessageHasher[] = ['alephium', 'sha256', 'blake2b', 'identity'] + + it('should verify signature with different hashers', () => { + hashers.forEach((hasher) => { + // Mock the utility verification + jest.spyOn(utils, 'verifySignature').mockReturnValue(true) + + const result = verifySignedMessage(message, hasher, mockPublicKey, mockSignature) + expect(result).toBe(true) + }) + }) + }) + + describe('API Destination Helpers', () => { + const mockDestination = { + address: 'test-address', + attoAlphAmount: '100', + tokens: [ + { + id: 'token-id', + amount: '50' + } + ] + } + + it('should convert destination to API format', () => { + const apiDestination = toApiDestination(mockDestination) + expect(apiDestination.attoAlphAmount).toBeTruthy() + expect(apiDestination.tokens).toBeTruthy() + }) + + it('should convert multiple destinations to API format', () => { + const destinations = [mockDestination, mockDestination] + const apiDestinations = toApiDestinations(destinations) + expect(apiDestinations.length).toBe(2) + }) + + it('should convert API destination back to original format', () => { + const apiDestination = toApiDestination(mockDestination) + const convertedBack = fromApiDestination(apiDestination) + expect(convertedBack).toEqual(mockDestination) + }) + }) + }) +}) diff --git a/packages/web3/src/signer/tx-builder.test.ts b/packages/web3/src/signer/tx-builder.test.ts new file mode 100644 index 000000000..9552fb0d5 --- /dev/null +++ b/packages/web3/src/signer/tx-builder.test.ts @@ -0,0 +1,356 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ +import { TransactionBuilder } from './tx-builder' +import { NodeProvider } from '../api' +import { addressFromPublicKey } from '../address' +import { SignTransferTxParams, SignDeployContractTxParams, SignExecuteScriptTxParams } from './types' + +describe('TransactionBuilder', () => { + let nodeProvider: jest.Mocked + let transactionBuilder: TransactionBuilder + + beforeEach(() => { + // Create a mock NodeProvider + nodeProvider = { + transactions: { + postTransactionsBuild: jest.fn(), + postTransactionsBuildChained: jest.fn() + }, + contracts: { + postContractsUnsignedTxDeployContract: jest.fn(), + postContractsUnsignedTxExecuteScript: jest.fn() + } + } as any + + // Create a transaction builder using the mock node provider + transactionBuilder = TransactionBuilder.from(nodeProvider) + }) + + describe('static from method', () => { + it('should create a TransactionBuilder from NodeProvider', () => { + const builder = TransactionBuilder.from(nodeProvider) + expect(builder).toBeDefined() + expect(builder.nodeProvider).toBe(nodeProvider) + }) + + it('should create a TransactionBuilder from URL', () => { + const builder = TransactionBuilder.from('http://test-url', 'test-api-key') + expect(builder).toBeDefined() + expect(builder.nodeProvider).toBeDefined() + }) + }) + + describe('buildTransferTx', () => { + const mockPublicKey = 'test-public-key' + const mockSignerAddress = addressFromPublicKey(mockPublicKey) + + const mockTransferParams: SignTransferTxParams = { + signerAddress: mockSignerAddress, + signerKeyType: 'default', + destinations: [ + { + address: 'recipient-address', + attoAlphAmount: '1000000000000000000' + } + ], + gasPrice: '1000000000' + } + + it('should build transfer transaction', async () => { + const mockBuildResponse = { + txId: 'test-tx-id', + fromGroup: 0, + toGroup: 1, + gasPrice: '1000000000' + } + + // Mock the node provider's build method + ;(nodeProvider.transactions.postTransactionsBuild as jest.Mock).mockResolvedValue(mockBuildResponse) + + const result = await transactionBuilder.buildTransferTx(mockTransferParams, mockPublicKey) + + // Verify method call + expect(nodeProvider.transactions.postTransactionsBuild).toHaveBeenCalledWith({ + fromPublicKey: mockPublicKey, + fromPublicKeyType: 'default', + destinations: mockTransferParams.destinations, + gasPrice: mockTransferParams.gasPrice, + signerAddress: mockSignerAddress + }) + + // Verify result + expect(result).toEqual({ + ...mockBuildResponse, + gasPrice: mockBuildResponse.gasPrice + }) + }) + + it('should throw error for mismatched public key', async () => { + const invalidPublicKey = 'invalid-public-key' + + await expect(transactionBuilder.buildTransferTx(mockTransferParams, invalidPublicKey)).rejects.toThrow( + 'Unmatched public key' + ) + }) + }) + + describe('buildDeployContractTx', () => { + const mockPublicKey = 'test-public-key' + const mockSignerAddress = addressFromPublicKey(mockPublicKey) + + const mockDeployParams: SignDeployContractTxParams = { + signerAddress: mockSignerAddress, + signerKeyType: 'default', + bytecode: 'contract-bytecode', + initialAttoAlphAmount: '1000000000000000000', + initialTokenAmounts: [], + gasPrice: '1000000000' + } + + it('should build deploy contract transaction', async () => { + const mockBuildResponse = { + txId: 'test-tx-id', + fromGroup: 0, + contractAddress: '0x1234567890', + gasPrice: '1000000000' + } + + // Mock the node provider's deploy contract method + ;(nodeProvider.contracts.postContractsUnsignedTxDeployContract as jest.Mock).mockResolvedValue(mockBuildResponse) + + const result = await transactionBuilder.buildDeployContractTx(mockDeployParams, mockPublicKey) + + // Verify method call + expect(nodeProvider.contracts.postContractsUnsignedTxDeployContract).toHaveBeenCalledWith({ + fromPublicKey: mockPublicKey, + fromPublicKeyType: 'default', + bytecode: mockDeployParams.bytecode, + initialAttoAlphAmount: mockDeployParams.initialAttoAlphAmount, + initialTokenAmounts: [], + gasPrice: mockDeployParams.gasPrice, + signerAddress: mockSignerAddress + }) + + // Verify result + expect(result).toEqual( + expect.objectContaining({ + txId: 'test-tx-id', + fromGroup: 0, + contractId: expect.any(String), + gasPrice: mockBuildResponse.gasPrice + }) + ) + }) + }) + + describe('buildExecuteScriptTx', () => { + const mockPublicKey = 'test-public-key' + const mockSignerAddress = addressFromPublicKey(mockPublicKey) + + const mockExecuteScriptParams: SignExecuteScriptTxParams = { + signerAddress: mockSignerAddress, + signerKeyType: 'default', + script: 'script-bytecode', + attoAlphAmount: '1000000000000000000', + tokens: [], + gasPrice: '1000000000', + bytecode: '' + } + + it('should build execute script transaction', async () => { + const mockBuildResponse = { + txId: 'test-tx-id', + fromGroup: 0, + gasPrice: '1000000000' + } + + // Mock the node provider's execute script method + ;(nodeProvider.contracts.postContractsUnsignedTxExecuteScript as jest.Mock).mockResolvedValue(mockBuildResponse) + + const result = await transactionBuilder.buildExecuteScriptTx(mockExecuteScriptParams, mockPublicKey) + + // Verify method call + expect(nodeProvider.contracts.postContractsUnsignedTxExecuteScript).toHaveBeenCalledWith({ + fromPublicKey: mockPublicKey, + fromPublicKeyType: 'default', + script: mockExecuteScriptParams.script, + attoAlphAmount: mockExecuteScriptParams.attoAlphAmount, + tokens: [], + gasPrice: mockExecuteScriptParams.gasPrice, + signerAddress: mockSignerAddress + }) + + // Verify result + expect(result).toEqual({ + ...mockBuildResponse, + groupIndex: 0, + gasPrice: mockBuildResponse.gasPrice + }) + }) + }) + + describe('buildChainedTx', () => { + const mockPublicKey1 = 'test-public-key-1' + const mockPublicKey2 = 'test-public-key-2' + const mockSignerAddress1 = addressFromPublicKey(mockPublicKey1) + const mockSignerAddress2 = addressFromPublicKey(mockPublicKey2) + + it('should build chained transactions', async () => { + const mockTransferParams: SignTransferTxParams = { + type: 'Transfer', + signerAddress: mockSignerAddress1, + signerKeyType: 'default', + destinations: [ + { + address: 'recipient-address-1', + attoAlphAmount: '1000000000000000000' + } + ], + gasPrice: '1000000000' + } + + const mockDeployParams: SignDeployContractTxParams = { + type: 'DeployContract', + signerAddress: mockSignerAddress2, + signerKeyType: 'default', + bytecode: 'contract-bytecode', + initialAttoAlphAmount: '1000000000000000000', + initialTokenAmounts: [], + gasPrice: '1000000000' + } + + const mockBuildChainedResponse = [ + { + type: 'Transfer', + value: { + txId: 'transfer-tx-id', + fromGroup: 0, + toGroup: 1, + gasPrice: '1000000000' + } + }, + { + type: 'DeployContract', + value: { + txId: 'deploy-tx-id', + fromGroup: 0, + contractAddress: '0x1234567890', + gasPrice: '1000000000' + } + } + ] + + // Mock the node provider's build chained transactions method + ;(nodeProvider.transactions.postTransactionsBuildChained as jest.Mock).mockResolvedValue(mockBuildChainedResponse) + + const result = await transactionBuilder.buildChainedTx( + [mockTransferParams, mockDeployParams], + [mockPublicKey1, mockPublicKey2] + ) + + // Verify method call + expect(nodeProvider.transactions.postTransactionsBuildChained).toHaveBeenCalledWith([ + { + type: 'Transfer', + value: { + fromPublicKey: mockPublicKey1, + fromPublicKeyType: 'default', + destinations: mockTransferParams.destinations, + gasPrice: mockTransferParams.gasPrice, + signerAddress: mockSignerAddress1 + } + }, + { + type: 'DeployContract', + value: { + fromPublicKey: mockPublicKey2, + fromPublicKeyType: 'default', + bytecode: mockDeployParams.bytecode, + initialAttoAlphAmount: mockDeployParams.initialAttoAlphAmount, + initialTokenAmounts: [], + gasPrice: mockDeployParams.gasPrice, + signerAddress: mockSignerAddress2 + } + } + ]) + + // Verify result + expect(result).toEqual([ + { + type: 'Transfer', + txId: 'transfer-tx-id', + fromGroup: 0, + toGroup: 1, + gasPrice: '1000000000' + }, + expect.objectContaining({ + type: 'DeployContract', + txId: 'deploy-tx-id', + fromGroup: 0, + contractId: expect.any(String), + gasPrice: '1000000000' + }) + ]) + }) + + it('should throw error when number of params and public keys do not match', async () => { + await expect( + transactionBuilder.buildChainedTx([mockTransferParams], [mockPublicKey1, mockPublicKey2]) + ).rejects.toThrow( + 'The number of build chained transaction parameters must match the number of public keys provided' + ) + }) + + it('should throw error for unsupported transaction type', async () => { + const invalidParams = [ + { + type: 'InvalidType', + signerAddress: mockSignerAddress1 + } + ] + + await expect(transactionBuilder.buildChainedTx(invalidParams as any, [mockPublicKey1])).rejects.toThrow( + 'Unsupported transaction type: InvalidType' + ) + }) + }) + + describe('buildUnsignedTx', () => { + it('should build unsigned transaction', () => { + // Mock hex-encoded unsigned transaction + const unsignedTxHex = '0123456789abcdef' + + const result = TransactionBuilder.buildUnsignedTx({ + unsignedTx: unsignedTxHex, + signerAddress: '' + }) + + // Verify result contains expected properties + expect(result).toEqual( + expect.objectContaining({ + unsignedTx: unsignedTxHex, + txId: expect.any(String), + fromGroup: expect.any(Number), + toGroup: expect.any(Number), + gasAmount: expect.any(Number), + gasPrice: expect.any(Number) + }) + ) + }) + }) +}) diff --git a/packages/web3/src/signer/types.test.ts b/packages/web3/src/signer/types.test.ts new file mode 100644 index 000000000..380e4c6f6 --- /dev/null +++ b/packages/web3/src/signer/types.test.ts @@ -0,0 +1,252 @@ +/* +Copyright 2018 - 2022 The Alephium Authors +This file is part of the alephium project. + +The library is free software: you can redistribute it and/or modify +it under the terms of the GNU Lesser General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +The library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public License +along with the library. If not, see . +*/ +import { describe, it, expect } from 'vitest' +import { + Destination, + Account, + SignTransferTxParams, + SignDeployContractTxParams, + SignExecuteScriptTxParams, + SignMessageParams, + MessageHasher, + KeyType, + SignerAddress, + EnableOptionsBase +} from '../src/signer/types' +import { NetworkId } from '../utils' + +describe('Signer Types', () => { + describe('Destination', () => { + it('should create a valid Destination object', () => { + const destination: Destination = { + address: '0x123', + attoAlphAmount: '1000000000000000000', + tokens: [{ id: 'token1', amount: '100' }], + lockTime: 123456, + message: 'Test transfer' + } + + expect(destination.address).toBe('0x123') + expect(destination.attoAlphAmount).toBe('1000000000000000000') + expect(destination.tokens).toHaveLength(1) + expect(destination.lockTime).toBe(123456) + expect(destination.message).toBe('Test transfer') + }) + + it('should allow optional fields', () => { + const destination: Destination = { + address: '0x456', + attoAlphAmount: '500000000000000000' + } + + expect(destination.address).toBe('0x456') + expect(destination.attoAlphAmount).toBe('500000000000000000') + expect(destination.tokens).toBeUndefined() + expect(destination.lockTime).toBeUndefined() + expect(destination.message).toBeUndefined() + }) + }) + + describe('Account', () => { + it('should create a valid Account object', () => { + const account: Account = { + keyType: 'default', + address: '0x789', + group: 1, + publicKey: 'pubkey123' + } + + expect(account.keyType).toBe('default') + expect(account.address).toBe('0x789') + expect(account.group).toBe(1) + expect(account.publicKey).toBe('pubkey123') + }) + + it('should support different key types', () => { + const account: Account = { + keyType: 'bip340-schnorr', + address: '0xabc', + group: 2, + publicKey: 'pubkey456' + } + + expect(account.keyType).toBe('bip340-schnorr') + }) + }) + + describe('SignerAddress', () => { + it('should create a valid SignerAddress object', () => { + const signerAddress: SignerAddress = { + signerAddress: '0x123', + signerKeyType: 'default' + } + + expect(signerAddress.signerAddress).toBe('0x123') + expect(signerAddress.signerKeyType).toBe('default') + }) + + it('should allow optional signerKeyType', () => { + const signerAddress: SignerAddress = { + signerAddress: '0x456' + } + + expect(signerAddress.signerAddress).toBe('0x456') + expect(signerAddress.signerKeyType).toBeUndefined() + }) + }) + + describe('Transaction Params', () => { + describe('SignTransferTxParams', () => { + it('should create a valid SignTransferTxParams object', () => { + const params: SignTransferTxParams = { + signerAddress: '0x123', + destinations: [ + { + address: '0x456', + attoAlphAmount: '1000000000000000000' + } + ], + signerKeyType: 'default', + gasAmount: 100, + gasPrice: '1000000000000000' + } + + expect(params.signerAddress).toBe('0x123') + expect(params.destinations).toHaveLength(1) + expect(params.signerKeyType).toBe('default') + expect(params.gasAmount).toBe(100) + expect(params.gasPrice).toBe('1000000000000000') + }) + }) + + describe('SignDeployContractTxParams', () => { + it('should create a valid SignDeployContractTxParams object', () => { + const params: SignDeployContractTxParams = { + signerAddress: '0x123', + bytecode: '0xabcdef', + signerKeyType: 'bip340-schnorr', + initialAttoAlphAmount: '1000000000000000000', + initialTokenAmounts: [{ id: 'token1', amount: '100' }], + issueTokenAmount: '500', + issueTokenTo: '0x456', + gasAmount: 200, + gasPrice: '2000000000000000' + } + + expect(params.signerAddress).toBe('0x123') + expect(params.bytecode).toBe('0xabcdef') + expect(params.signerKeyType).toBe('bip340-schnorr') + expect(params.initialAttoAlphAmount).toBe('1000000000000000000') + expect(params.initialTokenAmounts).toHaveLength(1) + expect(params.issueTokenAmount).toBe('500') + expect(params.issueTokenTo).toBe('0x456') + expect(params.gasAmount).toBe(200) + expect(params.gasPrice).toBe('2000000000000000') + }) + }) + + describe('SignExecuteScriptTxParams', () => { + it('should create a valid SignExecuteScriptTxParams object', () => { + const params: SignExecuteScriptTxParams = { + signerAddress: '0x123', + bytecode: '0xabcdef', + signerKeyType: 'default', + attoAlphAmount: '1000000000000000000', + tokens: [{ id: 'token1', amount: '100' }], + gasAmount: 150, + gasPrice: '1500000000000000', + gasEstimationMultiplier: 1.5 + } + + expect(params.signerAddress).toBe('0x123') + expect(params.bytecode).toBe('0xabcdef') + expect(params.signerKeyType).toBe('default') + expect(params.attoAlphAmount).toBe('1000000000000000000') + expect(params.tokens).toHaveLength(1) + expect(params.gasAmount).toBe(150) + expect(params.gasPrice).toBe('1500000000000000') + expect(params.gasEstimationMultiplier).toBe(1.5) + }) + }) + }) + + describe('SignMessage', () => { + it('should create a valid SignMessageParams object', () => { + const messageHashers: MessageHasher[] = ['alephium', 'sha256', 'blake2b', 'identity'] + + messageHashers.forEach((hasher) => { + const params: SignMessageParams = { + signerAddress: '0x123', + message: 'Hello, World!', + messageHasher: hasher, + signerKeyType: 'default' + } + + expect(params.signerAddress).toBe('0x123') + expect(params.message).toBe('Hello, World!') + expect(params.messageHasher).toBe(hasher) + expect(params.signerKeyType).toBe('default') + }) + }) + }) + + describe('KeyType', () => { + it('should only allow specific key types', () => { + const validKeyTypes: KeyType[] = ['default', 'bip340-schnorr'] + + validKeyTypes.forEach((keyType) => { + const account: Account = { + keyType, + address: '0x123', + group: 0, + publicKey: 'pubkey' + } + + expect(account.keyType).toBe(keyType) + }) + }) + }) + + describe('EnableOptionsBase', () => { + it('should create a valid EnableOptionsBase object', () => { + const validNetworkIds: NetworkId[] = ['mainnet', 'testnet', 'devnet'] + + validNetworkIds.forEach((networkId) => { + const options: EnableOptionsBase = { + addressGroup: 1, + keyType: 'default', + networkId, + onDisconnected: () => {} + } + + expect(options.addressGroup).toBe(1) + expect(options.keyType).toBe('default') + expect(options.networkId).toBe(networkId) + expect(typeof options.onDisconnected).toBe('function') + }) + + const minimalOptions: EnableOptionsBase = { + onDisconnected: () => {} + } + + expect(minimalOptions.addressGroup).toBeUndefined() + expect(minimalOptions.keyType).toBeUndefined() + expect(minimalOptions.networkId).toBeUndefined() + }) + }) +})