diff --git a/CHANGELOG.md b/CHANGELOG.md index a6a04e95b..ddafb7b1b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ - Migrate integration test files to Typescript & Hardhat: - 000_fullchain.js (#156, #157) + - 00X_fullchain-Xworkers.js (#158) - Remove `smock` from unit tests: - IexecEscrow.v8 (#154, #155) - IexecPocoDelegate (#149, #151) diff --git a/test/000_fullchain.test.ts b/test/000_fullchain.test.ts index 419f444d5..33a63a3db 100644 --- a/test/000_fullchain.test.ts +++ b/test/000_fullchain.test.ts @@ -16,15 +16,16 @@ import { } from '../utils/poco-tools'; import { IexecWrapper } from './utils/IexecWrapper'; -// +---------+-------------+-------------+-------------+----------+-----+----------------+ -// | | Sponsorship | Replication | Beneficiary | Callback | BoT | Type | -// +---------+-------------+-------------+-------------+----------+-----+----------------+ -// | [1] | ✔ | ✔ | ✔ | ✔ | ✔ | Standard | -// | [2] | x | ✔ | ✔ | ✔ | ✔ | Standard | -// | [3] | ✔ | x | ✔ | ✔ | ✔ | Standard,TEE | -// | [4] | x | x | ✔ | ✔ | ✔ | Standard,TEE | -// | [5] | x | x | x | x | x | Standard,TEE | -// +---------+-------------+-------------+----------+-----+-------------+----------------+ +// +---------+-------------+-------------+-------------+----------+-----+---------------------------------+ +// | | Sponsorship | Replication | Beneficiary | Callback | BoT | Type | +// +---------+-------------+-------------+-------------+----------+-----+---------------------------------+ +// | [1] | ✔ | ✔ | ✔ | ✔ | ✔ | Standard | +// | [2] | x | ✔ | ✔ | ✔ | ✔ | Standard | +// | [3] | ✔ | x | ✔ | ✔ | ✔ | Standard,TEE | +// | [4] | x | x | ✔ | ✔ | ✔ | Standard,TEE | +// | [5] | x | x | x | x | x | Standard,TEE | +// | [6.x] | x | ✔ | x | x | x | Standard,TEE, X good workers | +// +---------+-------------+-------------+-------------+----------+-----+---------------------------------+ const standardDealTag = '0x0000000000000000000000000000000000000000000000000000000000000000'; const teeDealTag = '0x0000000000000000000000000000000000000000000000000000000000000001'; @@ -32,7 +33,7 @@ const appPrice = 1000; const datasetPrice = 1_000_000; const workerpoolPrice = 1_000_000_000; const callbackAddress = ethers.Wallet.createRandom().address; -const { results } = buildUtf8ResultAndDigest('result'); +const { results, resultDigest } = buildUtf8ResultAndDigest('result'); const { resultsCallback, callbackResultDigest } = buildResultCallbackAndDigest(123); let proxyAddress: string; @@ -48,6 +49,10 @@ let [ scheduler, anyone, worker1, + worker2, + worker3, + worker4, + worker5, ]: SignerWithAddress[] = []; let ordersActors: OrdersActors; let ordersAssets: OrdersAssets; @@ -72,6 +77,10 @@ describe('Integration tests', function () { scheduler, anyone, worker1, + worker2, + worker3, + worker4, + worker5, } = accounts); iexecWrapper = new IexecWrapper(proxyAddress, accounts); ({ appAddress, datasetAddress, workerpoolAddress } = await iexecWrapper.createAssets()); @@ -198,6 +207,103 @@ describe('Integration tests', function () { it('[4] No sponsorship, beneficiary, callback, BoT, no replication', async function () {}); it('[5] No sponsorship, no beneficiary, no callback, no BoT, no replication', async function () {}); + + describe('Integration tests array of worker', function () { + for (let workerNumber = 1; workerNumber < 6; workerNumber++) { + it(`[6.${workerNumber}] No sponsorship, no beneficiary, no callback, no BoT, up to ${workerNumber} workers`, async function () { + const volume = 1; + const disposableWorkers = [worker1, worker2, worker3, worker4, worker5]; + const workers = disposableWorkers.slice(0, workerNumber); + const acounts = [requester, scheduler, appProvider, datasetProvider, ...workers]; + // Create deal. + const orders = buildOrders({ + assets: ordersAssets, + prices: ordersPrices, + requester: requester.address, + tag: standardDealTag, + beneficiary: beneficiary.address, + volume, + trust: workerNumber ** 2 - 1, + }); + const { dealId, dealPrice, schedulerStakePerDeal } = + await iexecWrapper.signAndMatchOrders(...orders.toArray()); + const taskPrice = appPrice + datasetPrice + workerpoolPrice; + const schedulerStakePerTask = schedulerStakePerDeal / volume; + const workerRewardPerTask = await iexecWrapper.computeWorkerRewardPerTask( + dealId, + PocoMode.CLASSIC, + ); + const schedulerRewardPerTask = workerpoolPrice - workerRewardPerTask; + // Check initial balances. + let accountsInitBalances = [ + { + address: proxyAddress, + frozen: (await iexecPoco.frozenOf(proxyAddress)).toNumber(), + }, + ]; + for (const account of acounts) { + let address = account.address; + let frozen = (await iexecPoco.frozenOf(account.address)).toNumber(); + accountsInitBalances.push({ address, frozen }); + } + for (let i = 0; i < workerNumber; i++) { + expect(await iexecPoco.viewScore(workers[i].address)).to.be.equal(0); + } + const taskId = await iexecWrapper.initializeTask(dealId, 0); + // Finalize each task and check balance changes. + const workerStake = await iexecPoco + .viewDeal(dealId) + .then((deal) => deal.workerStake.toNumber()); + + for (let i = 0; i < workerNumber; i++) { + await iexecWrapper.contributeToTask(dealId, 0, resultDigest, workers[i]); + } + for (let i = 0; i < workerNumber; i++) { + await iexecPoco + .connect(workers[i]) + .reveal(taskId, resultDigest) + .then((tx) => tx.wait()); + } + const finalizeTx = await iexecPoco + .connect(scheduler) + .finalize(taskId, results, '0x'); + expect(finalizeTx).to.changeTokenBalances( + iexecPoco, + [proxyAddress, requester, scheduler, appProvider, datasetProvider], + [ + -(dealPrice + schedulerStakePerDeal), + 0, + schedulerStakePerTask + schedulerRewardPerTask, + appPrice, + datasetPrice, + ], + ); + for (let i = 0; i < workerNumber; i++) { + expect(finalizeTx).to.changeTokenBalances( + iexecPoco, + [workers[i]], + [workerStake + workerRewardPerTask / workerNumber], + ); + } + expect((await iexecPoco.viewTask(taskId)).status).to.equal( + TaskStatusEnum.COMPLETED, + ); + const expectedFrozenChanges = [0, -taskPrice, -schedulerStakePerTask, 0, 0]; + for (let i = 0; i < workerNumber; i++) { + expectedFrozenChanges.push(0); + } + await changesInFrozen({ + accountsInitBalances, + frozenChanges: expectedFrozenChanges, + }); + for (let i = 0; i < workerNumber; i++) { + if (workerNumber == 1) { + expect(await iexecPoco.viewScore(workers[i].address)).to.be.equal(0); + } + } + }); + } + }); }); async function checkBalancesAndFrozens(args: { @@ -214,3 +320,16 @@ async function checkBalancesAndFrozens(args: { expect(await iexecPoco.frozenOf(account.signer.address)).to.equal(account.frozen, message); } } + +async function changesInFrozen(args: { + accountsInitBalances: { address: string; frozen: number }[]; + frozenChanges: number[]; +}) { + for (let i = 0; i < args.accountsInitBalances.length; i++) { + const message = `Failed with account at index ${i}`; + expect(await iexecPoco.frozenOf(args.accountsInitBalances[i].address)).to.equal( + args.accountsInitBalances[i].frozen + args.frozenChanges[i], + message, + ); + } +} diff --git a/test/001_fullchain-1workers.js b/test/001_fullchain-1workers.js.skip similarity index 100% rename from test/001_fullchain-1workers.js rename to test/001_fullchain-1workers.js.skip diff --git a/test/002_fullchain-2workers.js b/test/002_fullchain-2workers.js.skip similarity index 100% rename from test/002_fullchain-2workers.js rename to test/002_fullchain-2workers.js.skip diff --git a/test/003_fullchain-3workers.js b/test/003_fullchain-3workers.js.skip similarity index 100% rename from test/003_fullchain-3workers.js rename to test/003_fullchain-3workers.js.skip diff --git a/test/004_fullchain-4workers.js b/test/004_fullchain-4workers.js.skip similarity index 100% rename from test/004_fullchain-4workers.js rename to test/004_fullchain-4workers.js.skip