From dd116d99c92aed6c87b50a4cf29a8666cabd3150 Mon Sep 17 00:00:00 2001 From: rodrigopavezi Date: Tue, 26 Nov 2024 00:11:52 -0300 Subject: [PATCH] feat: run all test locally --- Dockerfile | 18 +++--- Dockerfile.graph-deploy | 13 ++++ README.md | 27 ++++++++ docker-compose.yml | 62 +++++++++++++++++++ lerna.json | 3 +- nx.json | 18 ++++++ package.json | 4 +- packages/ethereum-storage/package.json | 2 +- .../test/gas-fee-definer.test.ts | 4 +- .../address-based-info-retriever.test.ts | 2 +- .../test/payment/batch-proxy.test.ts | 2 +- packages/request-client.js/test/index.test.ts | 1 + .../test/getChannelsByTopic.test.ts | 3 +- .../test/getConfirmedTransaction.test.ts | 14 ++--- .../test/getTransactionsByChannelId.test.ts | 14 +++-- .../request-node/test/requestNode.test.ts | 1 + packages/smart-contracts/hardhat.config.ts | 2 +- .../thegraph-data-access/test/index.test.ts | 5 ++ .../transaction-manager/test/index.test.ts | 18 +++--- .../test/unit/transactions-factory.test.ts | 5 +- .../test/unit/transactions-parser.test.ts | 4 +- 21 files changed, 182 insertions(+), 40 deletions(-) create mode 100644 Dockerfile.graph-deploy create mode 100644 docker-compose.yml create mode 100644 nx.json create mode 100644 packages/thegraph-data-access/test/index.test.ts diff --git a/Dockerfile b/Dockerfile index 34cbeb2380..c7d88a7eaf 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,16 +1,20 @@ -FROM node:16-alpine +FROM node:18-alpine +RUN apk add --no-cache git +RUN npm install -g solc -WORKDIR /app +## Warning! This Docker config is meant to be used for development and debugging, not in prod. -RUN apk add --virtual .build-deps git python g++ bash make +WORKDIR /base COPY package.json . COPY yarn.lock . - -RUN yarn +RUN yarn install COPY . . -RUN yarn +RUN yarn install +RUN yarn clean RUN yarn build -RUN apk del .build-deps +# Port configuration +ENV PORT 3000 +EXPOSE 3000 diff --git a/Dockerfile.graph-deploy b/Dockerfile.graph-deploy new file mode 100644 index 0000000000..5056075655 --- /dev/null +++ b/Dockerfile.graph-deploy @@ -0,0 +1,13 @@ +FROM node:18-alpine +RUN apk add --no-cache git + +WORKDIR /base/storage-subgraph + +RUN git clone https://github.com/RequestNetwork/storage-subgraph /base/storage-subgraph +RUN npm -g install @graphprotocol/graph-cli +RUN yarn +RUN yarn codegen subgraph-private.yaml + +#CMD yarn create-local ; yarn deploy-local ./subgraph-private.yaml + +CMD graph create --node http://graph-node:8020/ RequestNetwork/request-storage && graph deploy --node http://graph-node:8020/ --ipfs http://ipfs:5001 --version-label $(git rev-parse --short HEAD) RequestNetwork/request-storage ./subgraph-private.yaml \ No newline at end of file diff --git a/README.md b/README.md index 9546524a00..56121e8ab3 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,27 @@ yarn run lint Test all the packages in the monorepo. +Some tests will require services to be running locally + +```bash +docker compose up +``` + +Deploy Smart Contracts + +```bash +yarn run deploy:contracts +``` + +Run request-node locally + +```bash +cp ./packages/request-node/.env.example ./packages/request-node/.env +yarn run start:request-node +``` + +Run all tests + ```bash yarn run test ``` @@ -89,6 +110,12 @@ Test a specific package by replacing `@requestnetwork/request-client.js` with th yarn workspace @requestnetwork/request-client.js test ``` +Clean Docker Volumes + +```bash +dcoker compose down -v +``` + ## License [MIT](https://github.com/RequestNetwork/requestNetwork/blob/master/LICENSE) diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000000..130e1c6a73 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,62 @@ +# Warning! This Docker config is meant to be used for development and debugging, specially for running tests, not in prod. +services: + graph-node: + image: graphprotocol/graph-node:v0.25.0 + ports: + - '8000:8000' + - '8001:8001' + - '8020:8020' + - '8030:8030' + - '8040:8040' + depends_on: + - ipfs + - postgres + - ganache + environment: + postgres_host: postgres + postgres_user: graph-node + postgres_pass: let-me-in + postgres_db: graph-node + ipfs: 'ipfs:5001' + ethereum: 'private:http://ganache:8545' + RUST_LOG: info + GRAPH_ALLOW_NON_DETERMINISTIC_IPFS: 1 + ipfs: + image: requestnetwork/request-ipfs:v0.13.0 + ports: + - '5001:5001' + restart: on-failure:20 + # volumes: + # - ./data/ipfs:/data/ipfs + ganache: + image: trufflesuite/ganache:v7.6.0 + ports: + - 8545:8545 + command: + - '-l' + - '90000000' + - '-m' + - 'candy maple cake sugar pudding cream honey rich smooth crumble sweet treat' + - '-k' + - 'london' + restart: on-failure:20 + postgres: + image: postgres + ports: + - '5432:5432' + command: ['postgres', '-cshared_preload_libraries=pg_stat_statements'] + environment: + POSTGRES_USER: graph-node + POSTGRES_PASSWORD: let-me-in + POSTGRES_DB: graph-node + restart: on-failure:20 + graph-deploy: + build: + context: . + dockerfile: Dockerfile.graph-deploy + depends_on: + - ipfs + - postgres + - graph-node + - ganache + restart: on-failure:20 diff --git a/lerna.json b/lerna.json index cdd9836382..b5c29e2395 100644 --- a/lerna.json +++ b/lerna.json @@ -1,5 +1,6 @@ { - "lerna": "3.2.1", + "lerna": "6.6.2", + "useWorkspaces": true, "packages": ["packages/*"], "version": "independent", "npmClient": "yarn" diff --git a/nx.json b/nx.json new file mode 100644 index 0000000000..a6fb292015 --- /dev/null +++ b/nx.json @@ -0,0 +1,18 @@ +{ + "tasksRunnerOptions": { + "default": { + "runner": "nx/tasks-runners/default", + "options": { + "cacheableOperations": ["build", "test"] + } + } + }, + "targetDefaults": { + "build": { + "dependsOn": ["^build"] + }, + "test": { + "dependsOn": ["build"] + } + } +} diff --git a/package.json b/package.json index 6805d68cb6..0745da4202 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,9 @@ "publish-npm": "lerna publish --conventional-commits --exact", "publish-manual-prerelease": "lerna publish prerelease --conventional-commits --exact", "publish-prerelease": "yarn lerna publish --preid development --skip-git --yes --canary", - "test": "lerna run test", + "deploy:contracts": "yarn workspace @requestnetwork/smart-contracts deploy", + "start:request-node": "yarn workspace @requestnetwork/request-node start", + "test": "lerna run test --concurrency=1", "format": "prettier . -w", "format:check": "prettier . -c", "link:all": "for d in packages/*; do cd $d; yarn link; cd -; done", diff --git a/packages/ethereum-storage/package.json b/packages/ethereum-storage/package.json index c6ac648551..125a1cf660 100644 --- a/packages/ethereum-storage/package.json +++ b/packages/ethereum-storage/package.json @@ -35,7 +35,7 @@ "clean": "rm -rf dist tsconfig.tsbuildinfo tsconfig.build.tsbuildinfo", "lint": "eslint . --fix", "lint:check": "eslint .", - "test": "jest", + "test": "jest --maxWorkers=1", "test:watch": "yarn test --watch", "init-ipfs": "node scripts/init-ipfs.js" }, diff --git a/packages/ethereum-storage/test/gas-fee-definer.test.ts b/packages/ethereum-storage/test/gas-fee-definer.test.ts index d31e734bb4..2544ecd370 100644 --- a/packages/ethereum-storage/test/gas-fee-definer.test.ts +++ b/packages/ethereum-storage/test/gas-fee-definer.test.ts @@ -36,6 +36,8 @@ const gasFeeDefiner = new GasFeeDefiner({ provider, logger: console }); describe('Gas fee estimation', () => { it('Should not be undefined', async () => { + provider.send('evm_increaseTime', [-60]); + provider.send('evm_mine', []); const estimation = await gasFeeDefiner.getGasFees(); expect(estimation.maxFeePerGas).toBeDefined(); expect(estimation.maxPriorityFeePerGas).toBeDefined(); @@ -49,7 +51,7 @@ describe('Gas fee estimation', () => { await provider.send('evm_mine', []); const secondEstimation = await gasFeeDefiner.getGasFees(); - expect(firstEstimation.maxFeePerGas?.toNumber()).toBeGreaterThan( + expect(firstEstimation.maxFeePerGas?.toNumber()).toBeGreaterThanOrEqual( secondEstimation.maxFeePerGas?.toNumber() || 0, ); }); diff --git a/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts b/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts index 1c195e7682..f9e5b55369 100644 --- a/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts +++ b/packages/payment-detection/test/erc20/address-based-info-retriever.test.ts @@ -10,7 +10,7 @@ describe('api/erc20/address-based-info-retriever', () => { describe('on localhost', () => { const paymentAddress = '0xf17f52151EbEF6C7334FAD080c5704D77216b732'; const payerAddress = '0x627306090abaB3A6e1400e9345bC60c78a8BEf57'; - const emptyAddress = '0xC5fdf4076b8F3A5357c5E395ab970B5B54098Fef'; + const emptyAddress = '0x56dDdAA262139112d2490b50BE328D237a499A14'; it('can get the localhost balance of an address', async () => { const infoRetriever = new ERC20InfoRetriever( diff --git a/packages/payment-processor/test/payment/batch-proxy.test.ts b/packages/payment-processor/test/payment/batch-proxy.test.ts index 094291f24d..50042dc843 100644 --- a/packages/payment-processor/test/payment/batch-proxy.test.ts +++ b/packages/payment-processor/test/payment/batch-proxy.test.ts @@ -736,7 +736,7 @@ describe('batch-proxy', () => { ).toEqual(ETHTotal.toString()); expect(toNewBalanceETH.sub(toOldBalanceETH)).toEqual(ETHAmount); expect(feeNewBalanceETH.sub(feeOldBalanceETH).toString()).toEqual(ETHFeesTotal.toString()); - }); + }, 20000); }); }); diff --git a/packages/request-client.js/test/index.test.ts b/packages/request-client.js/test/index.test.ts index ba41b14881..a048a77662 100644 --- a/packages/request-client.js/test/index.test.ts +++ b/packages/request-client.js/test/index.test.ts @@ -26,6 +26,7 @@ import { http, HttpResponse } from 'msw'; import { setupServer, SetupServer } from 'msw/node'; import config from '../src/http-config-defaults'; +jest.setTimeout(20000); const httpConfig: Partial = { getConfirmationDeferDelay: 0, }; diff --git a/packages/request-node/test/getChannelsByTopic.test.ts b/packages/request-node/test/getChannelsByTopic.test.ts index ccd50ff07a..fcfb8c3e15 100644 --- a/packages/request-node/test/getChannelsByTopic.test.ts +++ b/packages/request-node/test/getChannelsByTopic.test.ts @@ -5,6 +5,7 @@ import { RequestNode } from '../src/requestNode'; import { normalizeKeccak256Hash } from '@requestnetwork/utils'; import { providers } from 'ethers'; +jest.setTimeout(30000); // enable re-running these tests on local environment by having a different channel ID each time. const time = Date.now(); const channelId = `01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa${time}`; @@ -116,7 +117,7 @@ describe('getChannelsByTopic', () => { }); }; await Promise.all([confirm(transactionData), confirm(otherTransactionData)]); - }, 10000); + }); it('responds with no transaction to requests with a non-existent topic', async () => { const serverResponse = await request(server) diff --git a/packages/request-node/test/getConfirmedTransaction.test.ts b/packages/request-node/test/getConfirmedTransaction.test.ts index d4abf057a3..14b3d5938c 100644 --- a/packages/request-node/test/getConfirmedTransaction.test.ts +++ b/packages/request-node/test/getConfirmedTransaction.test.ts @@ -5,9 +5,10 @@ import { getRequestNode } from '../src/server'; import { RequestNode } from '../src/requestNode'; import { providers } from 'ethers'; -const channelId = '010aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa'; +const time = Date.now(); +const channelId = `01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa${time}`; -const transactionData = { data: `this is sample data for a transaction ${Date.now()}` }; +const transactionData = { data: `this is sample data for a transaction ${time}` }; const transactionHash = normalizeKeccak256Hash(transactionData).value; const provider = new providers.JsonRpcProvider('http://localhost:8545'); @@ -41,14 +42,13 @@ describe('getConfirmedTransaction', () => { .set('Accept', 'application/json') .expect(StatusCodes.NOT_FOUND); - // mining is required for TheGraph to index data - await provider.send('evm_mine', []); - let serverResponse: request.Response | undefined; // retry mechanism to account for ganache delay - for (let i = 0; i < 10; i++) { + for (let i = 0; i < 20; i++) { // wait a bit for the confirmation await new Promise((resolve): any => setTimeout(resolve, 1000)); + // mining is required for TheGraph to index data + await provider.send('evm_mine', []); serverResponse = await request(server) .get('/getConfirmedTransaction') @@ -64,7 +64,7 @@ describe('getConfirmedTransaction', () => { expect(serverResponse!.body.result).toMatchObject({}); // 'getConfirmedTransaction request meta' expect(serverResponse!.body.meta.storageMeta.state).toBe('confirmed'); - }, 30000); + }, 40000); it('responds with status 422 to requests with no value', async () => { await request(server) diff --git a/packages/request-node/test/getTransactionsByChannelId.test.ts b/packages/request-node/test/getTransactionsByChannelId.test.ts index 966dd7a5c2..a179d87da8 100644 --- a/packages/request-node/test/getTransactionsByChannelId.test.ts +++ b/packages/request-node/test/getTransactionsByChannelId.test.ts @@ -3,14 +3,16 @@ import request from 'supertest'; import { getRequestNode } from '../src/server'; import { RequestNode } from '../src/requestNode'; -const channelId = '01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaab'; -const anotherChannelId = '01bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbc'; -const nonExistentChannelId = '01cccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccccd'; +jest.setTimeout(20000); +const time = Date.now(); +const channelId = `01aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa${time}`; +const anotherChannelId = `01bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb${time}`; +const nonExistentChannelId = `01ccccccccccccccccccccccccccccccccccccccccccccccccccc${time}`; const transactionData = { - data: `this is sample data for a transaction to test getTransactionsByChannelId ${Date.now()}`, + data: `this is sample data for a transaction to test getTransactionsByChannelId ${time}`, }; const otherTransactionData = { - data: 'this is other sample data for a transaction to test getTransactionsByChannelId', + data: `this is other sample data for a transaction to test getTransactionsByChannelId ${time}`, }; let requestNodeInstance: RequestNode; @@ -41,7 +43,7 @@ describe('getTransactionsByChannelId', () => { let serverResponse = await request(server) .get('/getTransactionsByChannelId') - .query({ channelId }) + .query({ channelId: channelId }) .set('Accept', 'application/json') .expect(StatusCodes.OK); diff --git a/packages/request-node/test/requestNode.test.ts b/packages/request-node/test/requestNode.test.ts index 33ecfae667..2c3a7ebd09 100644 --- a/packages/request-node/test/requestNode.test.ts +++ b/packages/request-node/test/requestNode.test.ts @@ -3,6 +3,7 @@ import request from 'supertest'; import { getRequestNode } from '../src/server'; import { RequestNode } from '../src/requestNode'; +jest.setTimeout(20000); const packageJson = require('../package.json'); const requestNodeVersion = packageJson.version; diff --git a/packages/smart-contracts/hardhat.config.ts b/packages/smart-contracts/hardhat.config.ts index f0e532f609..3f54db1bb0 100644 --- a/packages/smart-contracts/hardhat.config.ts +++ b/packages/smart-contracts/hardhat.config.ts @@ -82,7 +82,7 @@ export default { hardfork: 'london', }, private: { - url: 'http://127.0.0.1:8545', + url: url('private'), accounts: undefined, }, mainnet: { diff --git a/packages/thegraph-data-access/test/index.test.ts b/packages/thegraph-data-access/test/index.test.ts new file mode 100644 index 0000000000..4f8a653e21 --- /dev/null +++ b/packages/thegraph-data-access/test/index.test.ts @@ -0,0 +1,5 @@ +describe('the test', () => { + it('should pass', () => { + expect(true).toBe(true); + }); +}); diff --git a/packages/transaction-manager/test/index.test.ts b/packages/transaction-manager/test/index.test.ts index 2c2e1be486..c147ce8f59 100644 --- a/packages/transaction-manager/test/index.test.ts +++ b/packages/transaction-manager/test/index.test.ts @@ -129,7 +129,7 @@ describe('index', () => { }), ); expect(fakeDataAccess.persistTransaction).toHaveBeenCalledTimes(1); - }); + }, 10000); it('cannot persist a transaction if data access emit error', async () => { const fakeDataAccessEmittingError = Object.assign({}, fakeDataAccess); @@ -668,7 +668,7 @@ describe('index', () => { ], }, }); - }); + }, 10000); it('can get two transactions with different encryptions from the same encrypted channel the first has the right hash but wrong data', async () => { const encryptedTxFakeHash = await TransactionsFactory.createEncryptedTransactionInNewChannel( @@ -755,7 +755,7 @@ describe('index', () => { transactions: [null, null], }, }); - }); + }, 10000); it('can get two transactions, the first is encrypted but the second is clear (will be ignored)', async () => { const encryptedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(data, [ @@ -831,7 +831,7 @@ describe('index', () => { ], }, }); - }); + }, 10000); it('can get two transactions first encrypted but decrypt impossible and second clear', async () => { const encryptedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(data, [ @@ -1106,7 +1106,7 @@ describe('index', () => { // 'return is wrong' expect(ret).toEqual(expectedRet); - }); + }, 20000); }); describe('getChannelsByTopic', () => { @@ -1190,7 +1190,7 @@ describe('index', () => { }), ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); - }); + }, 15000); it('cannot get an encrypted channel indexed by topic without decryptionProvider', async () => { const encryptedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(data, [ @@ -1259,7 +1259,7 @@ describe('index', () => { }), ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); - }); + }, 10000); it('can get an clear channel indexed by topic without decryptionProvider even if an encrypted transaction happen first', async () => { const encryptedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(data, [ @@ -1341,7 +1341,7 @@ describe('index', () => { }), ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); - }); + }, 10000); it('can get channels indexed by topics with channelId not matching the first transaction hash', async () => { const txWrongHash: DataAccessTypes.ITimestampedTransaction = { @@ -1467,7 +1467,7 @@ describe('index', () => { }), ); expect(fakeDataAccess.getChannelsByTopic).toHaveBeenCalledWith(extraTopics[0], undefined); - }); + }, 10000); }); describe('getChannelsByMultipleTopic', () => { diff --git a/packages/transaction-manager/test/unit/transactions-factory.test.ts b/packages/transaction-manager/test/unit/transactions-factory.test.ts index 4fb7f26f3f..131f5c8604 100644 --- a/packages/transaction-manager/test/unit/transactions-factory.test.ts +++ b/packages/transaction-manager/test/unit/transactions-factory.test.ts @@ -4,6 +4,7 @@ import TransactionsFactory from '../../src/transactions-factory'; import * as TestData from './utils/test-data'; const data = '{ "what": "ever", "it": "is,", "this": "must", "work": true }'; +jest.setTimeout(20000); // in milliseconds /* eslint-disable @typescript-eslint/no-unused-expressions */ describe('transaction-factory', () => { @@ -67,7 +68,7 @@ describe('transaction-factory', () => { (ek) => ek.slice(0, 2) === MultiFormatTypes.prefix.ECIES_ENCRYPTED, ), ).toBe(true); - }); + }, 10000); it('cannot create encrypted transaction with encryption parameters not ECIES', async () => { await expect( @@ -117,7 +118,7 @@ describe('transaction-factory', () => { // 'keys not right' expect(encryptedTx.keys).toBeUndefined(); - }); + }, 10000); it('cannot create encrypted transaction with encryption parameters not AES256-CBC', async () => { const channelKeyWrong = { diff --git a/packages/transaction-manager/test/unit/transactions-parser.test.ts b/packages/transaction-manager/test/unit/transactions-parser.test.ts index 54ac90c4a2..92a41cce76 100644 --- a/packages/transaction-manager/test/unit/transactions-parser.test.ts +++ b/packages/transaction-manager/test/unit/transactions-parser.test.ts @@ -3,6 +3,8 @@ import TransactionsFactory from '../../src/transactions-factory'; import TransactionsParser from '../../src/transactions-parser'; import * as TestData from './utils/test-data'; +jest.setTimeout(20000); // in milliseconds + let transactionParser: TransactionsParser; const data = '{ "what": "ever", "it": "is,", "this": "must", "work": true }'; @@ -90,7 +92,7 @@ describe('transaction-parser', () => { expect(await ret.transaction.getData()).toBe(data); // 'channelKey wrong' expect(ret.channelKey).toBeDefined(); - }); + }, 15000); it('cannot parse encrypted transaction without decryptionProvider', async () => { transactionParser = new TransactionsParser(); const encryptedParsedTx = await TransactionsFactory.createEncryptedTransactionInNewChannel(