From 15761fe5884f4f423f9a94e02ea54cafad09e437 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 3 Dec 2024 16:45:39 +0200 Subject: [PATCH 1/5] API-160: tx pool endpoint tests + new cs image --- .../chain-simulator/docker/docker-compose.yml | 2 +- src/test/chain-simulator/pool.cs-e2e.ts | 79 +++++++++++++++++++ .../utils/chain.simulator.operations.ts | 15 +++- 3 files changed, 93 insertions(+), 3 deletions(-) create mode 100644 src/test/chain-simulator/pool.cs-e2e.ts diff --git a/src/test/chain-simulator/docker/docker-compose.yml b/src/test/chain-simulator/docker/docker-compose.yml index 33be92d9b..69e37abe4 100644 --- a/src/test/chain-simulator/docker/docker-compose.yml +++ b/src/test/chain-simulator/docker/docker-compose.yml @@ -20,7 +20,7 @@ services: chainsimulator: container_name: chainsimulator - image: multiversx/chainsimulator:v1.8.4-barnard-test + image: multiversx/chainsimulator:v1.8.4-barnard-test2 command: ["--node-override-config", "./overridable-config.toml"] volumes: - ./overridable-config.toml:/multiversx/overridable-config.toml diff --git a/src/test/chain-simulator/pool.cs-e2e.ts b/src/test/chain-simulator/pool.cs-e2e.ts new file mode 100644 index 000000000..0fc04a5ba --- /dev/null +++ b/src/test/chain-simulator/pool.cs-e2e.ts @@ -0,0 +1,79 @@ +import axios from 'axios'; +import { config } from './config/env.config'; +import { ChainSimulatorUtils } from './utils/test.utils'; +import { fundAddress, sendTransaction } from './utils/chain.simulator.operations'; + +describe('Pool e2e tests with chain simulator', () => { + beforeAll(async () => { + await ChainSimulatorUtils.waitForEpoch(2); + await fundAddress(config.chainSimulatorUrl, config.aliceAddress); + await sendTransaction({nonceOffset: 20, value: '0', sender: config.aliceAddress, receiver: config.bobAddress, chainSimulatorUrl: config.chainSimulatorUrl, dataField: "pool1"}); + await sendTransaction({nonceOffset: 21, value: '0', sender: config.aliceAddress, receiver: config.bobAddress, chainSimulatorUrl: config.chainSimulatorUrl, dataField: "pool2"}); + await new Promise((resolve) => setTimeout(resolve, 20000)); + }); + + beforeEach(() => { + jest.clearAllMocks(); + }); + + describe('GET /pool', () => { + it('should return status code 200', async () => { + const response = await axios.get(`${config.apiServiceUrl}/pool`); + const tokens = response.data; + + expect(response.status).toBe(200); + expect(tokens).toBeInstanceOf(Array); + }); + + it('should return the transaction pool', async () => { + const response = await axios.get( + `${config.apiServiceUrl}/pool`, + ); + const pool = response.data; + let numTimesAliceWasSender = 0; + let numTimesBobWasReceiver = 0; + for (const tx of pool) { + numTimesAliceWasSender += tx.sender === config.aliceAddress ? 1 : 0; + numTimesBobWasReceiver += tx.receiver === config.bobAddress ? 1 : 0; + expect(tx).toHaveProperty('receiver'); + expect(tx).toHaveProperty('txHash'); + expect(tx).toHaveProperty('nonce'); + expect(tx).toHaveProperty('gasPrice'); + expect(tx).toHaveProperty('gasLimit'); + } + + expect(numTimesAliceWasSender).toBeGreaterThanOrEqual(2); + expect(numTimesBobWasReceiver).toBeGreaterThanOrEqual(2); + }); + }); + + + describe('GET /pool/:txhash', () => { + it('should return status code 200 and the transaction', async () => { + const poolResponse = await axios.get( + `${config.apiServiceUrl}/pool?size=1`, + ); + const response = await axios.get( + `${config.apiServiceUrl}/pool/${poolResponse.data[0].txHash}`, + ); + const token = response.data; + + expect(response.status).toBe(200); + expect(token).toHaveProperty( + 'txHash', + poolResponse.data[0].txHash, + ); + }); + + it('should return status code 400 for non-existent tx hash', async () => { + const nonExistentTxHash = '0000000000000000000000000000000000000000000000000000000000000000'; + try { + await axios.get( + `${config.apiServiceUrl}/pool/${nonExistentTxHash}`, + ); + } catch (error: any) { + expect(error.response.status).toBe(400); + } + }); + }); +}); diff --git a/src/test/chain-simulator/utils/chain.simulator.operations.ts b/src/test/chain-simulator/utils/chain.simulator.operations.ts index c1e3f153e..de56aee22 100644 --- a/src/test/chain-simulator/utils/chain.simulator.operations.ts +++ b/src/test/chain-simulator/utils/chain.simulator.operations.ts @@ -118,10 +118,10 @@ export async function sendTransaction( const tx = { sender: args.sender, receiver: args.receiver, - nonce: nonce, + nonce: nonce + (args.nonceOffset ?? 0), value: args.value, gasPrice: 1000000000, - gasLimit: args.gasLimit, + gasLimit: args.gasLimit ?? (50_000 + 1_500 * args.dataField.length), data: Buffer.from(args.dataField).toString('base64'), signature: 'a'.repeat(128), chainID: 'chain', @@ -133,6 +133,16 @@ export async function sendTransaction( tx, ); const txHash = txHashResponse.data.data.txHash; + if (args.nonceOffset) { + // when a nonce offset is present, it means that the transaction won't be executed in real time, so we should early exit + console.log(`Broadcasted tx hash ${txHash} of sender ${args.sender} with nonce ${tx.nonce}`); + console.log(JSON.stringify(tx)); + await axios.post( + `${args.chainSimulatorUrl}/simulator/generate-blocks/1`, + ); + return txHash; + } + await axios.post( `${args.chainSimulatorUrl}/simulator/generate-blocks-until-transaction-processed/${txHash}`, ); @@ -172,6 +182,7 @@ export class SendTransactionArgs { dataField: string = ''; value?: string = '0'; gasLimit?: number = 100_000_000; + nonceOffset?: number = 0; // useful for scenarios where a higher nonce is desired constructor(options: Partial = {}) { Object.assign(this, options); From da548c77b53dfaf2cb35f63440a10712014a6329 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 3 Dec 2024 16:57:53 +0200 Subject: [PATCH 2/5] push staged file --- config/config.e2e.mainnet.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config/config.e2e.mainnet.yaml b/config/config.e2e.mainnet.yaml index 219ef63d3..ffccff8cb 100644 --- a/config/config.e2e.mainnet.yaml +++ b/config/config.e2e.mainnet.yaml @@ -31,9 +31,9 @@ features: hitsThreshold: 100 ttl: 12 transactionPool: - enabled: false + enabled: true transactionPoolWarmer: - enabled: false + enabled: true cronExpression: '*/5 * * * * *' ttlInSeconds: 60 updateCollectionExtraDetails: From 48e3df5529fda01787a0f4d3eae2a11eed3bfe37 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Tue, 3 Dec 2024 17:03:31 +0200 Subject: [PATCH 3/5] fix test --- src/test/chain-simulator/pool.cs-e2e.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/test/chain-simulator/pool.cs-e2e.ts b/src/test/chain-simulator/pool.cs-e2e.ts index 0fc04a5ba..c5bedf2e3 100644 --- a/src/test/chain-simulator/pool.cs-e2e.ts +++ b/src/test/chain-simulator/pool.cs-e2e.ts @@ -19,10 +19,10 @@ describe('Pool e2e tests with chain simulator', () => { describe('GET /pool', () => { it('should return status code 200', async () => { const response = await axios.get(`${config.apiServiceUrl}/pool`); - const tokens = response.data; + const txsPool = response.data; expect(response.status).toBe(200); - expect(tokens).toBeInstanceOf(Array); + expect(txsPool).toBeInstanceOf(Array); }); it('should return the transaction pool', async () => { @@ -56,23 +56,23 @@ describe('Pool e2e tests with chain simulator', () => { const response = await axios.get( `${config.apiServiceUrl}/pool/${poolResponse.data[0].txHash}`, ); - const token = response.data; + const tx = response.data; expect(response.status).toBe(200); - expect(token).toHaveProperty( + expect(tx).toHaveProperty( 'txHash', poolResponse.data[0].txHash, ); }); - it('should return status code 400 for non-existent tx hash', async () => { + it('should return status code 404 for non-existent tx hash', async () => { const nonExistentTxHash = '0000000000000000000000000000000000000000000000000000000000000000'; try { await axios.get( `${config.apiServiceUrl}/pool/${nonExistentTxHash}`, ); } catch (error: any) { - expect(error.response.status).toBe(400); + expect(error.response.status).toBe(404); } }); }); From c0ba039735f792ee53ed2f3f678ae640bf08b0a3 Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Wed, 4 Dec 2024 17:09:35 +0200 Subject: [PATCH 4/5] remove self sended txs --- src/test/chain-simulator/pool.cs-e2e.ts | 11 +---------- src/test/jest-api.json | 2 +- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/src/test/chain-simulator/pool.cs-e2e.ts b/src/test/chain-simulator/pool.cs-e2e.ts index c5bedf2e3..32e8064fb 100644 --- a/src/test/chain-simulator/pool.cs-e2e.ts +++ b/src/test/chain-simulator/pool.cs-e2e.ts @@ -1,14 +1,12 @@ import axios from 'axios'; import { config } from './config/env.config'; import { ChainSimulatorUtils } from './utils/test.utils'; -import { fundAddress, sendTransaction } from './utils/chain.simulator.operations'; +import { fundAddress } from './utils/chain.simulator.operations'; describe('Pool e2e tests with chain simulator', () => { beforeAll(async () => { await ChainSimulatorUtils.waitForEpoch(2); await fundAddress(config.chainSimulatorUrl, config.aliceAddress); - await sendTransaction({nonceOffset: 20, value: '0', sender: config.aliceAddress, receiver: config.bobAddress, chainSimulatorUrl: config.chainSimulatorUrl, dataField: "pool1"}); - await sendTransaction({nonceOffset: 21, value: '0', sender: config.aliceAddress, receiver: config.bobAddress, chainSimulatorUrl: config.chainSimulatorUrl, dataField: "pool2"}); await new Promise((resolve) => setTimeout(resolve, 20000)); }); @@ -30,20 +28,13 @@ describe('Pool e2e tests with chain simulator', () => { `${config.apiServiceUrl}/pool`, ); const pool = response.data; - let numTimesAliceWasSender = 0; - let numTimesBobWasReceiver = 0; for (const tx of pool) { - numTimesAliceWasSender += tx.sender === config.aliceAddress ? 1 : 0; - numTimesBobWasReceiver += tx.receiver === config.bobAddress ? 1 : 0; expect(tx).toHaveProperty('receiver'); expect(tx).toHaveProperty('txHash'); expect(tx).toHaveProperty('nonce'); expect(tx).toHaveProperty('gasPrice'); expect(tx).toHaveProperty('gasLimit'); } - - expect(numTimesAliceWasSender).toBeGreaterThanOrEqual(2); - expect(numTimesBobWasReceiver).toBeGreaterThanOrEqual(2); }); }); diff --git a/src/test/jest-api.json b/src/test/jest-api.json index 913123878..4b0a4b665 100644 --- a/src/test/jest-api.json +++ b/src/test/jest-api.json @@ -17,4 +17,4 @@ "../.." ], "testTimeout": 180000 -} \ No newline at end of file +} From b18fe5dbc908f69e05fa8dd38479230a0407730c Mon Sep 17 00:00:00 2001 From: bogdan-rosianu Date: Thu, 5 Dec 2024 10:49:07 +0200 Subject: [PATCH 5/5] fixes after review --- src/test/chain-simulator/pool.cs-e2e.ts | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/src/test/chain-simulator/pool.cs-e2e.ts b/src/test/chain-simulator/pool.cs-e2e.ts index 32e8064fb..d0d7d9fec 100644 --- a/src/test/chain-simulator/pool.cs-e2e.ts +++ b/src/test/chain-simulator/pool.cs-e2e.ts @@ -28,17 +28,22 @@ describe('Pool e2e tests with chain simulator', () => { `${config.apiServiceUrl}/pool`, ); const pool = response.data; + const expectedProperties = [ + 'txHash', + 'nonce', + 'receiver', + 'gasPrice', + 'gasLimit', + ]; + for (const tx of pool) { - expect(tx).toHaveProperty('receiver'); - expect(tx).toHaveProperty('txHash'); - expect(tx).toHaveProperty('nonce'); - expect(tx).toHaveProperty('gasPrice'); - expect(tx).toHaveProperty('gasLimit'); + for (const property of expectedProperties) { + expect(tx).toHaveProperty(property); + } } }); }); - describe('GET /pool/:txhash', () => { it('should return status code 200 and the transaction', async () => { const poolResponse = await axios.get(