Skip to content

Commit

Permalink
Add more smoke tests (#536)
Browse files Browse the repository at this point in the history
* proxy st

* treasury

* proxy lint

* fmt

* reduce validators

* withdrawn and deposits

* added test

* fmt

* remove non-used vars!

* fmt

* fix more tests

* fix dancebox instead of Dancebox

* remove line
  • Loading branch information
girazoki authored May 22, 2024
1 parent 5dd5b8b commit 6dc75c9
Show file tree
Hide file tree
Showing 7 changed files with 292 additions and 16 deletions.
50 changes: 41 additions & 9 deletions test/suites/smoke-test-common/test-configuration-consistency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ describeSuite({
beforeAll(() => {
api = context.polkadotJs();
const chain = api.consts.system.version.specName.toString();
blocksPerSession = chain == "Dancebox" ? 600n : 50n;
blocksPerSession = chain == "dancebox" ? 600n : 50n;
});

it({
Expand Down Expand Up @@ -58,8 +58,13 @@ describeSuite({

it({
id: "C03",
title: "Config all registered paras should be filled if more than min collators in orchestrator",
title: "Config registered paras should be filled if more than min collators in orchestrator",
test: async function () {
const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber();

const blockToCheck = Math.trunc(currentBlock / Number(blocksPerSession)) * Number(blocksPerSession);
const apiBeforeLatestNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 1));

const config = await api.query.configuration.activeConfig();
// get current session
const sessionIndex = (await api.query.session.currentIndex()).toNumber();
Expand All @@ -69,10 +74,16 @@ describeSuite({
await api.query.authorityAssignment.collatorContainerChain(sessionIndex + 1)
).toJSON();

// If we have container chain collators, is because we at least assigned min to orchestrator
if (
Object.keys(authorities["orchestratorChain"]).length > config["minOrchestratorCollators"].toNumber()
) {
const currentAuthorities = await api.query.session.validators();

const currentCollatorNumber = Math.min(currentAuthorities.length, config.maxCollators);

const maxParas = Math.trunc(
(currentCollatorNumber - config.minOrchestratorCollators) / config.collatorsPerContainer
);

// If we have container chain collators, is because the collator number is higher
if (maxParas > 0) {
let containersToCompareAgainst: Vec<u32>;
// If pending para ids for the session are empty we compare with registered para id, otherwise
// we compare with pending para ids.
Expand All @@ -95,20 +106,41 @@ describeSuite({
// This should be true as long as they have enough credits for getting collators
for (const container of containersToCompareAgainst) {
// we should only check those who have enough credits
// we compare against the credits they had just before new session obviously
// that is when they were charged for tokens
if (
await hasEnoughCredits(api, container, blocksPerSession, 2n, costPerSession, costPerBlock)
await hasEnoughCredits(
apiBeforeLatestNewSession,
container,
blocksPerSession,
1n,
2n,
costPerSession,
costPerBlock
)
) {
// A different test checks that this number is correct with respect to configuration
// test-collator-number-consistency
// Here we only check that that we have collators
expect(authorities["containerChains"][container.toString()].length).to.be.greaterThan(0);
// If we are able to cover all paras, then all of them should have collators if credits
if (maxParas >= containersToCompareAgainst.length) {
expect(authorities["containerChains"][container.toString()].length).to.be.greaterThan(
0
);
}
} else {
numWithNoCredits += 1;
}
}

// There might be some chains that because demand is higher than offer do not get collators
// However we are going to check that at least the expected number of chains was assigned
const expectedNumberOfChainsAssigned = Math.min(
containersToCompareAgainst.length - numWithNoCredits,
maxParas
);
expect(Object.keys(authorities["containerChains"]).length).to.be.equal(
containersToCompareAgainst.length - numWithNoCredits
expectedNumberOfChainsAssigned
);
}
},
Expand Down
18 changes: 15 additions & 3 deletions test/suites/smoke-test-common/test-consistency-services-payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,20 @@ describeSuite({
api = context.polkadotJs();
runtimeVersion = api.runtimeVersion.specVersion.toNumber();
const chain = api.consts.system.version.specName.toString();
blocksPerSession = chain == "Dancebox" ? 600n : 50n;
blocksPerSession = chain == "dancebox" ? 600n : 50n;
});

it({
id: "C01",
title: "All scheduled parachains should be able to pay for at least 2 sessions",
title: "All scheduled parachains should be able to pay for at least 1 session worth of blocks",
test: async function () {
if (runtimeVersion < 500) {
return;
}
const currentBlock = (await api.rpc.chain.getBlock()).block.header.number.toNumber();

const blockToCheck = Math.trunc(currentBlock / Number(blocksPerSession)) * Number(blocksPerSession);
const apiBeforeLatestNewSession = await api.at(await api.rpc.chain.getBlockHash(blockToCheck - 1));

// If they have collators scheduled, they should have at least enough money to pay
let pending = await api.query.collatorAssignment.pendingCollatorContainerChain();
Expand All @@ -37,7 +41,15 @@ describeSuite({
if (pending["containerChains"] != undefined) {
for (const container of Object.keys(pending.toJSON()["containerChains"])) {
expect(
await hasEnoughCredits(api, container, blocksPerSession, 2n, costPerSession, costPerBlock),
await hasEnoughCredits(
apiBeforeLatestNewSession,
container,
blocksPerSession,
1n,
2n,
costPerSession,
costPerBlock
),
`Container chain ${container} was assigned collators without having a way to pay for it`
).toBe(true);
}
Expand Down
118 changes: 118 additions & 0 deletions test/suites/smoke-test-common/test-proxy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
import "@tanssi/api-augment";
import { ApiDecoration } from "@polkadot/api/types";
import chalk from "chalk";
import { expect, beforeAll, describeSuite } from "@moonwall/cli";
import type { PalletProxyProxyDefinition } from "@polkadot/types/lookup";
import { ApiPromise } from "@polkadot/api";

describeSuite({
id: "S17",
title: "Verify account proxies created",
foundationMethods: "read_only",
testCases: ({ context, it, log }) => {
const proxiesPerAccount: { [account: string]: PalletProxyProxyDefinition[] } = {};
const proxyAccList: string[] = [];
let atBlockNumber: number = 0;
let apiAt: ApiDecoration<"promise">;
let paraApi: ApiPromise;

beforeAll(async function () {
paraApi = context.polkadotJs("para");
const limit = 1000;
let last_key = "";
let count = 0;

// Configure the api at a specific block
// (to avoid inconsistency querying over multiple block when the test takes a long time to
// query data and blocks are being produced)
atBlockNumber = process.env.BLOCK_NUMBER
? parseInt(process.env.BLOCK_NUMBER)
: (await paraApi.rpc.chain.getHeader()).number.toNumber();
apiAt = await paraApi.at(await paraApi.rpc.chain.getBlockHash(atBlockNumber));

for (;;) {
const query = await apiAt.query.proxy.proxies.entriesPaged({
args: [],
pageSize: limit,
startKey: last_key,
});

if (query.length == 0) {
break;
}
count += query.length;

// TEMPLATE: convert the data into the format you want (usually a dictionary per account)
for (const proxyData of query) {
const accountId = `0x${proxyData[0].toHex().slice(-40)}`;
last_key = proxyData[0].toString();
proxiesPerAccount[accountId] = proxyData[1][0].toArray();
proxyAccList.push(accountId);
}

// log logs to make sure it keeps progressing
// TEMPLATE: Adapt log line
if (count % (10 * limit) == 0) {
log(`Retrieved ${count} proxies`);
}
}

// TEMPLATE: Adapt proxies
log(`Retrieved ${count} total proxies`);
}, 30_000);

it({
id: "C100",
title: "should have no more than the maximum allowed proxies",
timeout: 240000,
test: async function () {
const maxProxies = paraApi.consts.proxy.maxProxies.toNumber();
const failedProxies: { accountId: string; proxiesCount: number }[] = [];

for (const accountId of Object.keys(proxiesPerAccount)) {
const proxiesCount = proxiesPerAccount[accountId].length;
if (proxiesCount > maxProxies) {
failedProxies.push({ accountId, proxiesCount });
}
}

if (failedProxies.length > 0) {
log("Failed accounts with too many proxies:");
log(
failedProxies
.map(({ accountId, proxiesCount }) => {
return `accountId: ${accountId} - ${chalk.red(
proxiesCount.toString().padStart(4, " ")
)} proxies (expected max: ${maxProxies})`;
})
.join(`\n`)
);
}

expect(failedProxies.length, "Failed max proxies").to.equal(0);

log(`Verified ${Object.keys(proxiesPerAccount).length} total accounts (at #${atBlockNumber})`);
},
});

it({
id: "C200",
title: "should have a maximum allowed proxies of 32",
test: async function () {
const runtimeName = paraApi.runtimeVersion.specName.toString();
const maxProxies = (await paraApi.consts.proxy.maxProxies).toNumber();

switch (runtimeName) {
case "dancebox":
expect(maxProxies).to.equal(32);
break;
case "flashbox":
expect(maxProxies).to.equal(32);
break;
}

log(`Verified maximum allowed proxies constant`);
},
});
},
});
40 changes: 40 additions & 0 deletions test/suites/smoke-test-common/test-supply-variance.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { beforeAll, describeSuite, expect } from "@moonwall/cli";

import { ApiPromise } from "@polkadot/api";
import { fetchWithdrawnAmount, fetchDepositedAmount } from "util/block";

describeSuite({
id: "S08",
title: "Sample suite that only runs on Dancebox chains",
foundationMethods: "read_only",
testCases: ({ it, context }) => {
let api: ApiPromise;

beforeAll(() => {
api = context.polkadotJs();
});

it({
id: "C03",
title: "Supply variance is correct",
test: async function () {
const latestBlock = await api.rpc.chain.getBlock();

const latestBlockHash = latestBlock.block.hash;
const latestParentBlockHash = latestBlock.block.header.parentHash;
const apiAtIssuanceAfter = await api.at(latestBlockHash);
const apiAtIssuanceBefore = await api.at(latestParentBlockHash);

const supplyBefore = (await apiAtIssuanceBefore.query.balances.totalIssuance()).toBigInt();

const events = await apiAtIssuanceAfter.query.system.events();

const withdrawnAmount = await fetchWithdrawnAmount(events);
const depositAmount = await fetchDepositedAmount(events);

const supplyAfter = (await apiAtIssuanceAfter.query.balances.totalIssuance()).toBigInt();
expect(supplyAfter).to.equal(supplyBefore + depositAmount - withdrawnAmount);
},
});
},
});
41 changes: 41 additions & 0 deletions test/suites/smoke-test-dancebox/test-treasury.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import "@tanssi/api-augment";
import { ApiDecoration } from "@polkadot/api/types";
import { describeSuite, expect, beforeAll } from "@moonwall/cli";
import { ApiPromise } from "@polkadot/api";

describeSuite({
id: "S24",
title: "Verify treasury consistency",
foundationMethods: "read_only",
testCases: ({ context, it, log }) => {
let atBlockNumber: number = 0;
let apiAt: ApiDecoration<"promise">;
let paraApi: ApiPromise;

beforeAll(async function () {
paraApi = context.polkadotJs("para");
atBlockNumber = (await paraApi.rpc.chain.getHeader()).number.toNumber();
apiAt = await paraApi.at(await paraApi.rpc.chain.getBlockHash(atBlockNumber));
});

it({
id: "C100",
title: "should have value > 0",
test: async function () {
// Load data
const treasuryPalletId = paraApi.consts.treasury.palletId;
const treasuryAccount = await apiAt.query.system.account(
`0x6d6f646C${treasuryPalletId.toString().slice(2)}0000000000000000000000000000000000000000`
);

console.log(
`0x6d6f646C${treasuryPalletId.toString().slice(2)}0000000000000000000000000000000000000000`
);
expect(treasuryAccount.data.free.toBigInt() > 0n).to.be.true;
expect(treasuryAccount.data.reserved.toBigInt()).to.be.equal(0n);

log(`Verified treasury free/reserved balance`);
},
});
},
});
30 changes: 30 additions & 0 deletions test/util/block.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,36 @@ export function fetchIssuance(events: EventRecord[] = []) {
return filtered[0];
}

export function fetchWithdrawnAmount(events: EventRecord[] = []) {
let withdrawnAmount = 0n;
const filtered = filterAndApply(
events,
"balances",
["Withdraw"],
({ event }: EventRecord) => event.data as unknown as { amount: u128 }
);

for (const event of filtered) {
withdrawnAmount += event.amount.toBigInt();
}
return withdrawnAmount;
}

export function fetchDepositedAmount(events: EventRecord[] = []) {
let depositAmount = 0n;
const filtered = filterAndApply(
events,
"balances",
["Deposit"],
({ event }: EventRecord) => event.data as unknown as { amount: u128 }
);

for (const event of filtered) {
depositAmount += event.amount.toBigInt();
}
return depositAmount;
}

export function fetchCollatorAssignmentTip(events: EventRecord[] = []) {
const filtered = filterAndApply(
events,
Expand Down
11 changes: 7 additions & 4 deletions test/util/payment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ export async function hasEnoughCredits(
paraId: ParaId,
blocksPerSession: bigint,
// TODO: minSessionRequirement should be 2 if the chain had collators in the previous session, and 1 otherwise
minSessionRequirement: bigint,
minCollatorSessionRequirement: bigint,
minBlockSessionRequirement: bigint,
costPerSession: bigint,
costPerBlock: bigint
): Promise<boolean> {
Expand All @@ -32,11 +33,13 @@ export async function hasEnoughCredits(

// We need, combined, at least credits for 2 session coverage + blocks
const neededBlockPaymentAfterCredits =
minSessionRequirement * blocksPerSession - freeBlockCredits < 0n
minBlockSessionRequirement * blocksPerSession - freeBlockCredits < 0n
? 0n
: minSessionRequirement * blocksPerSession - freeBlockCredits;
: minBlockSessionRequirement * blocksPerSession - freeBlockCredits;
const neededCollatorAssignmentPaymentAfterCredits =
minSessionRequirement - freeSessionCredits < 0n ? 0n : minSessionRequirement - freeSessionCredits;
minCollatorSessionRequirement - freeSessionCredits < 0n
? 0n
: minCollatorSessionRequirement - freeSessionCredits;

if (neededBlockPaymentAfterCredits > 0n || neededCollatorAssignmentPaymentAfterCredits > 0n) {
const neededTankMoney =
Expand Down

0 comments on commit 6dc75c9

Please sign in to comment.