From f11bb201eacbf21bf9264c2b464ced4668f88859 Mon Sep 17 00:00:00 2001 From: AbigailDeng Date: Thu, 26 Sep 2024 18:09:33 +0800 Subject: [PATCH] feat: unit test --- examples/browser/multiTransaction.html | 4 +- jest-report.xml | 516 +++++++++++++++++++- jest.browser.config.js | 8 +- package.json | 2 +- src/contract/contractMethod.js | 65 +-- test/unit/chain/index.test.js | 104 ++-- test/unit/contract/multiTransaction.test.js | 163 +++++++ 7 files changed, 787 insertions(+), 75 deletions(-) diff --git a/examples/browser/multiTransaction.html b/examples/browser/multiTransaction.html index 7ab1068f..54e8675f 100644 --- a/examples/browser/multiTransaction.html +++ b/examples/browser/multiTransaction.html @@ -41,8 +41,8 @@ // return contract's instance and you can call the methods on this instance return aelf.chain.contractAt(tokenContractAddress,wallet,{ refBlockNumberStrategy: { - AELF: -8, - tDVW: -8 + 9992731: -8, + 1931928: -8 }, multi: { 9992731: { diff --git a/jest-report.xml b/jest-report.xml index 330d4d58..7a9ed5d6 100644 --- a/jest-report.xml +++ b/jest-report.xml @@ -1,9 +1,517 @@ - - - + + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/jest.browser.config.js b/jest.browser.config.js index f022f179..7bd11307 100644 --- a/jest.browser.config.js +++ b/jest.browser.config.js @@ -177,10 +177,10 @@ module.exports = { // The glob patterns Jest uses to detect test files testMatch: [ - // '**/test/unit/**/contractMethod.test.js' - '**/test/unit/**/multiTransaction.test.js' - // '**/test/unit/**/?(*.)+(test).[jt]s?(x)', - // '**/test/unit/util/httpProvider.browser-test.js' + // '**/test/unit/chain/index.test.js' + // '**/test/unit/**/multiTransaction.test.js' + '**/test/unit/**/?(*.)+(test).[jt]s?(x)', + '**/test/unit/util/httpProvider.browser-test.js' // "**/?(*.)+(spec|test).[tj]s?(x)" ], testTimeout: 20000, diff --git a/package.json b/package.json index 17540765..ff9f2202 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "aelf-sdk", - "version": "3.4.16-alpha.1", + "version": "3.4.16-alpha.2", "description": "aelf-sdk js library", "main": "dist/aelf.cjs.js", "browser": "dist/aelf.umd.js", diff --git a/src/contract/contractMethod.js b/src/contract/contractMethod.js index 635fd5c5..533406bb 100644 --- a/src/contract/contractMethod.js +++ b/src/contract/contractMethod.js @@ -35,7 +35,6 @@ export default class ContractMethod { this._option = option || {}; this._gatewayUrl = this._option.gatewayUrl; this._multiOptions = this._option.multi || {}; - // multi chainId ['AELF', 'tDVW'] this._chainIds = Object.keys(this._multiOptions); const { resolvedRequestType, resolvedResponseType } = method; this._inputType = resolvedRequestType; @@ -126,13 +125,14 @@ export default class ContractMethod { throw new Error('refBlockNumberStrategy must be less than 0'); } } - let { refBlockNumberStrategy } = this._option; + // if _chainIds is empty, init refBlockNumberStrategy to undefined + let { refBlockNumberStrategy = this._chainIds.length ? {} : undefined } = this._option; args.forEach(arg => { if (arg.refBlockNumberStrategy) { if (isObject(arg.refBlockNumberStrategy)) { const keys = Object.keys(arg.refBlockNumberStrategy); for (let i = 0; i < keys.length; i++) { - validateItem(arg.refBlockNumberStrategy(keys[i])); + validateItem(arg.refBlockNumberStrategy[keys[i]]); refBlockNumberStrategy[keys[i]] = arg.refBlockNumberStrategy[keys[i]]; } } else { @@ -239,30 +239,37 @@ export default class ContractMethod { this._chainIds.forEach(ele => { encoded[ele] = this.packInput(filterArgs[0][ele]); }); - // if (isView) { - // return this.handleMultiTransaction({}, {}, encoded); - // } const refBlockNumberStrategy = this.validateRefBlockNumberStrategy(args); const chainHeight = {}; const chainHash = {}; this._chainIds.forEach(chainId => { // get chain height and hash const httpProvider = new HttpProvider(this._multiOptions[chainId].chainUrl); - const url = 'api/blockChain/chainStatus'; - const statusRes = httpProvider.send({ - url, - method: 'GET' - }); - let { BestChainHeight, BestChainHash } = statusRes; - if (refBlockNumberStrategy[chainId]) { - BestChainHeight += refBlockNumberStrategy[chainId]; - const block = this._chain.getBlockByHeight(BestChainHeight, true, { - sync: true + const url = 'blockChain/chainStatus'; + try { + const statusRes = httpProvider.send({ + url, + method: 'GET' }); - BestChainHash = block.BlockHash; + let { BestChainHeight, BestChainHash } = statusRes; + if (refBlockNumberStrategy?.[chainId]) { + BestChainHeight += refBlockNumberStrategy[chainId]; + const blockUrl = 'blockChain/blockByHeight'; + const block = httpProvider.send({ + url: blockUrl, + method: 'GET', + params: { + blockHeight: BestChainHeight + } + }); + BestChainHash = block.BlockHash; + } + chainHeight[chainId] = BestChainHeight; + chainHash[chainId] = BestChainHash; + } catch (error) { + console.error(`Error fetching status for chain ${chainId}:`, error); + throw error; } - chainHeight[chainId] = BestChainHeight; - chainHash[chainId] = BestChainHash; }); return this.handleMultiTransaction(chainHeight, chainHash, encoded); } @@ -275,21 +282,13 @@ export default class ContractMethod { this._chainIds.forEach(ele => { encoded[ele] = this.packInput(filterArgs[0][ele]); }); - - // if (isView) { - // return this.handleMultiTransaction({}, {}, encoded); - // } - const refBlockNumberStrategy = this.validateRefBlockNumberStrategy(args); - // console.log(refBlockNumberStrategy, 'refBlockNumberStrategy'); const chainHeight = {}; const chainHash = {}; - await Promise.all( this._chainIds.map(async chainId => { - const httpProvider = new HttpProvider(this._multiOptions[chainId].chainUrl); + const httpProvider = new HttpProvider(this._multiOptions[chainId]?.chainUrl); const url = 'blockChain/chainStatus'; - try { const statusRes = await httpProvider.sendAsync({ url, @@ -298,12 +297,16 @@ export default class ContractMethod { let { BestChainHeight, BestChainHash } = statusRes; if (refBlockNumberStrategy?.[chainId]) { BestChainHeight += refBlockNumberStrategy[chainId]; - const block = this._chain.getBlockByHeight(BestChainHeight, true, { - sync: true + const blockUrl = 'blockChain/blockByHeight'; + const block = await httpProvider.sendAsync({ + url: blockUrl, + method: 'GET', + params: { + blockHeight: BestChainHeight + } }); BestChainHash = block.BlockHash; } - chainHeight[chainId] = BestChainHeight; chainHash[chainId] = BestChainHash; } catch (error) { diff --git a/test/unit/chain/index.test.js b/test/unit/chain/index.test.js index c565b0a0..6508db05 100644 --- a/test/unit/chain/index.test.js +++ b/test/unit/chain/index.test.js @@ -35,10 +35,10 @@ describe('chain should work', () => { test('test refBlockNumberStrategy argument into object', () => { // Arrange const args = [{ refBlockNumberStrategy: 10 }]; - + // Act const result = chain.extractArgumentsIntoObject(args); - + // Assert expect(result.refBlockNumberStrategy).toBe(10); }); @@ -46,26 +46,19 @@ describe('chain should work', () => { const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); const { GenesisContractAddress } = await aelf.chain.getChainStatus(); const args = [{ sync: true }]; - const contract = await chain.contractAt( - GenesisContractAddress, - null, - ...args - ); + const contract = await chain.contractAt(GenesisContractAddress, null, ...args); expect(contract.deserializeLog).toBeInstanceOf(Function); }); test('test is invalid contract when sync', async () => { - const address = - 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; + const address = 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; const args = [{ sync: true }]; // mock contractFileDescriptorSet chain.getContractFileDescriptorSet = jest.fn(() => { return { - file: [], + file: [] }; }); - expect(() => chain.contractAt(address, null, ...args)).toThrow( - 'no such contract' - ); + expect(() => chain.contractAt(address, null, ...args)).toThrow('no such contract'); }); test('test is concrete contract when async', async () => { const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); @@ -74,53 +67,45 @@ describe('chain should work', () => { expect(contract.deserializeLog).toBeInstanceOf(Function); }); test('test is invalid contract when async', async () => { - const address = - 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; + const address = 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; chain.getContractFileDescriptorSet = jest.fn(() => { return Promise.resolve({ - file: [], + file: [] }); }); let error; chain.extractArgumentsIntoObject = jest.fn(() => { return { - callback: (e) => { + callback: e => { error = e; }, - isSync: false, + isSync: false }; }); await chain.contractAt(address, null); expect(error).toEqual(new Error('no such contract')); }, 5000); test('test is invalid contract with noop callback', async () => { - const address = - 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; + const address = 'ELF_iUY5CLwzU8L8vjVgH95vx3ZRuvD5d9hVK3EdPMVD8v9EaQT75_AELF'; chain.getContractFileDescriptorSet = jest.fn(() => { return Promise.resolve({ - file: [], + file: [] }); }); - await expect(chain.contractAt(address, null)).rejects.toEqual( - new Error('no such contract') - ); + await expect(chain.contractAt(address, null)).rejects.toEqual(new Error('no such contract')); }, 5000); test('test txId has corresponding transaction in the block with height when sync', async () => { const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); const blockInfo = await aelf.chain.getBlockByHeight(1, true); const txId = blockInfo.Body.Transactions[0]; - const result = Array.isArray( - aelf.chain.getMerklePath(txId, 1, { sync: true }) - ); + const result = Array.isArray(aelf.chain.getMerklePath(txId, 1, { sync: true })); expect(result).toBe(true); }, 5000); test('test txId has no corresponding transaction in the block with height when sync', async () => { const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); const blockInfo = await chain.getBlockByHeight(1, true); const txId = blockInfo.Body.Transactions[0]; - await expect(() => - aelf.chain.getMerklePath(txId, 2, { sync: true }) - ).toThrow( + await expect(() => aelf.chain.getMerklePath(txId, 2, { sync: true })).toThrow( 'txId dce643d5c142945dc9f0665819dbf0b268b8423a94fa7a488d24cd89c0b67a23 has no correspond transaction in the block with height 2' ); }, 20000); @@ -136,10 +121,63 @@ describe('chain should work', () => { const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); const blockInfo = await aelf.chain.getBlockByHeight(1, true); const txId = blockInfo.Body.Transactions[0]; - await expect( - async () => await aelf.chain.getMerklePath(txId, 2) - ).rejects.toThrow( + await expect(async () => await aelf.chain.getMerklePath(txId, 2)).rejects.toThrow( 'txId dce643d5c142945dc9f0665819dbf0b268b8423a94fa7a488d24cd89c0b67a23 has no correspond transaction in the block with height 2' ); }); }, 20000); + +describe('test multi transaction', () => { + beforeEach(() => { + httpProvider = new HttpProvider(stageEndpoint); + requestManager = new RequestManager(httpProvider); + chain = new Chain(requestManager); + }); + test('multi transaction option with number object refBlockNumberStrategy', async () => { + const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); + const { GenesisContractAddress } = await aelf.chain.getChainStatus(); + expect( + chain.contractAt(GenesisContractAddress, null, { + refBlockNumberStrategy: { + 9992731: -8, + 1931928: -8 + }, + multi: { + 9992731: { + chainUrl: 'https://aelf-test-node.aelf.io/', + contractAddress: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE' + }, + 1931928: { + chainUrl: 'https://tdvw-test-node.aelf.io/', + contractAddress: 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx' + } + }, + gatewayUrl: 'https://gateway-test.aelf.io' + }) + ).resolves.not.toThrow(); + }); + + test('multi transaction option without number object refBlockNumberStrategy', async () => { + const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); + const { GenesisContractAddress } = await aelf.chain.getChainStatus(); + expect( + chain.contractAt(GenesisContractAddress, null, { + refBlockNumberStrategy: { + 9992731: 'test', + 1931928: 'invalid' + }, + multi: { + 9992731: { + chainUrl: 'https://aelf-test-node.aelf.io/', + contractAddress: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE' + }, + 1931928: { + chainUrl: 'https://tdvw-test-node.aelf.io/', + contractAddress: 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx' + } + }, + gatewayUrl: 'https://gateway-test.aelf.io' + }) + ).resolves.not.toThrow(); + }); +}); diff --git a/test/unit/contract/multiTransaction.test.js b/test/unit/contract/multiTransaction.test.js index 6efea970..161ffd52 100644 --- a/test/unit/contract/multiTransaction.test.js +++ b/test/unit/contract/multiTransaction.test.js @@ -70,4 +70,167 @@ describe('multi transaction', () => { }); }); }); + test('send multi transaction to gateway with callback', async () => { + const expectedKeys = ['1931928', '9992731']; + let callbackData; + await contractMethod.sendMultiTransactionToGateway( + { + 9992731: { + symbol: 'ELF', + amount: '100000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }, + 1931928: { + symbol: 'ELF', + amount: '150000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + } + }, + data => { + callbackData = data; + } + ); + expect(Object.keys(callbackData)).toEqual(expectedKeys); + expectedKeys.forEach(key => { + expect(Array.isArray(callbackData[key])).toBe(true); + callbackData[key].forEach(value => { + expect(typeof value).toBe('string'); + }); + }); + }); + test('send multi transaction to gateway with refBlockNumberStrategy', async () => { + const expectedKeys = ['1931928', '9992731']; + const result = await contractMethod.sendMultiTransactionToGateway( + { + 9992731: { + symbol: 'ELF', + amount: '100000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }, + 1931928: { + symbol: 'ELF', + amount: '150000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + } + }, + { + refBlockNumberStrategy: { + 9992731: -8, + 1931928: -8 + } + } + ); + expect(Object.keys(result)).toEqual(expectedKeys); + expectedKeys.forEach(key => { + expect(Array.isArray(result[key])).toBe(true); + result[key].forEach(value => { + expect(typeof value).toBe('string'); + }); + }); + }); + test('sync send multi transaction to gateway', async () => { + const expectedKeys = ['1931928', '9992731']; + const result = contractMethod.sendMultiTransactionToGateway( + { + 9992731: { + symbol: 'ELF', + amount: '100000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }, + 1931928: { + symbol: 'ELF', + amount: '150000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + } + }, + { + sync: true + } + ); + expect(Object.keys(result)).toEqual(expectedKeys); + expectedKeys.forEach(key => { + expect(Array.isArray(result[key])).toBe(true); + result[key].forEach(value => { + expect(typeof value).toBe('string'); + }); + }); + }); +}); + +describe('multi transaction with refBlockNumberStrategy', () => { + const aelf = new AElf(new AElf.providers.HttpProvider(stageEndpoint)); + const wallet = AElf.wallet.getWalletByPrivateKey('943df6d39fd1e1cc6ae9813e54f7b9988cf952814f9c31e37744b52594cb4096'); + const chain = aelf.chain; + const address = 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx'; + const fds = chain.getContractFileDescriptorSet(address, { + sync: true + }); + const factory = new ContractFactory(chain, fds, wallet); + const method = factory.services[2].methods['Transfer'].resolve(); + const contractMethod = new ContractMethod(chain, method, address, wallet, { + refBlockNumberStrategy: { + 9992731: -8, + 1931928: -8 + }, + multi: { + 9992731: { + chainUrl: 'https://aelf-test-node.aelf.io/', + contractAddress: 'JRmBduh4nXWi1aXgdUsj5gJrzeZb2LxmrAbf7W99faZSvoAaE' + }, + 1931928: { + chainUrl: 'https://tdvw-test-node.aelf.io/', + contractAddress: 'ASh2Wt7nSEmYqnGxPPzp4pnVDU4uhj1XW9Se5VeZcX2UDdyjx' + } + }, + gatewayUrl: 'https://gateway-test.aelf.io' + }); + test('send multi transaction to gateway', async () => { + const expectedKeys = ['1931928', '9992731']; + const result = await contractMethod.sendMultiTransactionToGateway({ + 9992731: { + symbol: 'ELF', + amount: '100000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }, + 1931928: { + symbol: 'ELF', + amount: '150000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + } + }); + expect(Object.keys(result)).toEqual(expectedKeys); + expectedKeys.forEach(key => { + expect(Array.isArray(result[key])).toBe(true); + result[key].forEach(value => { + expect(typeof value).toBe('string'); + }); + }); + }); + test('sync send multi transaction to gateway', async () => { + const expectedKeys = ['1931928', '9992731']; + const result = contractMethod.sendMultiTransactionToGateway( + { + 9992731: { + symbol: 'ELF', + amount: '100000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + }, + 1931928: { + symbol: 'ELF', + amount: '150000000', + to: 'GyQX6t18kpwaD9XHXe1ToKxfov8mSeTLE9q9NwUAeTE8tULZk' + } + }, + { + sync: true + } + ); + expect(Object.keys(result)).toEqual(expectedKeys); + expectedKeys.forEach(key => { + expect(Array.isArray(result[key])).toBe(true); + result[key].forEach(value => { + expect(typeof value).toBe('string'); + }); + }); + }); });