Skip to content

Commit 4d72f73

Browse files
timbrindedffarall
andauthoredAug 23, 2024
test: 🧪 Add BSP testshelper (Moonsong-Labs#162)
* added helper * test fix * Changed default options for assertExtrinsic * docs: 💡 Add doc comments for assert helper functions --------- Co-authored-by: Facundo Farall <[email protected]>
1 parent d37a56f commit 4d72f73

File tree

6 files changed

+139
-7
lines changed

6 files changed

+139
-7
lines changed
 

‎pnpm-lock.yaml

+16-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/suites/integration/bsp/onboard.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ for (const bspNetConfig of bspNetConfigCases) {
2828
});
2929

3030
after(async () => {
31-
await cleardownTest(api);
31+
await cleardownTest({ api });
3232
});
3333

3434
it("New BSP can be created", async () => {

‎test/suites/integration/bsp/slash-provider.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ describe("BSPNet: Slash Provider", () => {
2121
});
2222

2323
after(async () => {
24-
await cleardownTest(api);
24+
await cleardownTest({ api });
2525
});
2626

2727
it("Network launches and can be queried", async () => {

‎test/util/asserts.ts

+112
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,108 @@ import type { EventRecord } from "@polkadot/types/interfaces";
22
import { strictEqual } from "node:assert";
33
import type { ApiPromise } from "@polkadot/api";
44
import type { AugmentedEvent } from "@polkadot/api/types";
5+
import type { BspNetApi } from "./bspNet";
56

7+
/**
8+
* Asserts that a specific extrinsic (module.method) is present in a blockchain block or transaction pool.
9+
*
10+
* @param {BspNetApi} api - The API instance connected to the blockchain network.
11+
* @param {Object} options - Configuration options for the extrinsic check.
12+
* @param {string} [options.blockHeight] - The block height to check. If not provided, the latest block will be used.
13+
* @param {string} [options.blockHash] - The block hash to check. Takes precedence over blockHeight if provided.
14+
* @param {boolean} [options.skipSuccessCheck=false] - If true, skips the check for an associated `ExtrinsicSuccess` event.
15+
* @param {boolean} [options.checkTxPool=false] - If true, checks the pending transaction pool instead of a finalized block.
16+
* @param {string} options.module - The module name of the extrinsic to check (e.g., "balances").
17+
* @param {string} options.method - The method name of the extrinsic to check (e.g., "transfer").
18+
* @param {boolean} [options.ignoreParamCheck=false] - If true, skips the validation check for the module.method existence in the API metadata.
19+
*
20+
* @returns {Promise<Object[]>} - Returns a list of objects representing the extrinsics that match the module.method criteria.
21+
* @throws {Error} - Throws an error if no matching extrinsic is found, or if the success check fails (unless skipped).
22+
*
23+
* TODO: add ability to search nested extrinsics e.g. sudo.sudo(balance.forceTransfer(...))
24+
*/
25+
export const assertExtrinsicPresent = async (
26+
api: BspNetApi,
27+
options: {
28+
blockHeight?: string;
29+
blockHash?: string;
30+
skipSuccessCheck?: boolean;
31+
checkTxPool?: boolean;
32+
module: string;
33+
method: string;
34+
ignoreParamCheck?: boolean;
35+
}
36+
): Promise<
37+
{
38+
module: string;
39+
method: string;
40+
extIndex: number;
41+
}[]
42+
> => {
43+
if (options.ignoreParamCheck !== true) {
44+
strictEqual(
45+
options.module in api.tx,
46+
true,
47+
`Module ${options.module} not found in API metadata. Turn off this check with "ignoreParamCheck: true" if you are sure this exists`
48+
);
49+
strictEqual(
50+
options.method in api.tx[options.module],
51+
true,
52+
`Method ${options.module}.${options.method} not found in metadata. Turn off this check with "ignoreParamCheck: true" if you are sure this exists`
53+
);
54+
}
55+
56+
const blockHash = options?.blockHash
57+
? options.blockHash
58+
: options?.blockHeight
59+
? await api.rpc.chain.getBlockHash(options?.blockHeight)
60+
: await api.rpc.chain.getBlockHash();
61+
62+
const extrinsics = !options.checkTxPool
63+
? await (async () => {
64+
const response = await api.rpc.chain.getBlock(blockHash);
65+
66+
if (!options.blockHeight && !options.blockHash) {
67+
console.log(
68+
`No block height provided, using latest at ${response.block.header.number.toNumber()}`
69+
);
70+
}
71+
return response.block.extrinsics;
72+
})()
73+
: await api.rpc.author.pendingExtrinsics();
74+
const transformed = extrinsics.map(({ method: { method, section } }, index) => {
75+
return { module: section, method, extIndex: index };
76+
});
77+
78+
const matches = transformed.filter(
79+
({ method, module }) => method === options?.method && module === options?.module
80+
);
81+
82+
strictEqual(
83+
matches.length > 0,
84+
true,
85+
`No extrinsics matching ${options?.module}.${options?.method} found. \n Extrinsics in block ${options.blockHeight || blockHash}: ${extrinsics.map(({ method: { method, section } }) => `${section}.${method}`).join(" | ")}`
86+
);
87+
88+
if (options?.skipSuccessCheck !== true && options.checkTxPool !== true) {
89+
const events = await (await api.at(blockHash)).query.system.events();
90+
assertEventPresent(api, "system", "ExtrinsicSuccess", events);
91+
}
92+
93+
return matches;
94+
};
95+
96+
/**
97+
* Asserts that a specific event (module.method) is present in the provided list of events.
98+
*
99+
* @param {ApiPromise} api - The API instance connected to the blockchain network.
100+
* @param {string} module - The module name of the event to check (e.g., "system").
101+
* @param {string} method - The method name of the event to check (e.g., "ExtrinsicSuccess").
102+
* @param {EventRecord[]} [events] - The list of events to search through. If not provided or empty, an error is thrown.
103+
*
104+
* @returns {Object} - Returns an object containing the matching event and its data.
105+
* @throws {Error} - Throws an error if no matching event is found, or if the event does not match the expected structure.
106+
*/
6107
export const assertEventPresent = (
7108
api: ApiPromise,
8109
module: string,
@@ -27,6 +128,17 @@ export const assertEventPresent = (
27128
return { event: event.event, data: event.event.data };
28129
};
29130

131+
/**
132+
* Asserts that multiple instances of a specific event (module.method) are present in the provided list of events.
133+
*
134+
* @param {ApiPromise} api - The API instance connected to the blockchain network.
135+
* @param {string} module - The module name of the event to check (e.g., "system").
136+
* @param {string} method - The method name of the event to check (e.g., "ExtrinsicSuccess").
137+
* @param {EventRecord[]} [events] - The list of events to search through. If not provided or empty, an error is thrown.
138+
*
139+
* @returns {EventRecord[]} - Returns an array of matching events.
140+
* @throws {Error} - Throws an error if no matching events are found.
141+
*/
30142
export const assertEventMany = (
31143
api: ApiPromise,
32144
module: string,

‎test/util/bspNet/helpers.ts

+7-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import "@storagehub/api-augment";
12
import type { ApiPromise } from "@polkadot/api";
23
import type { SubmittableExtrinsic } from "@polkadot/api/types";
34
import type { KeyringPair } from "@polkadot/keyring/types";
@@ -738,9 +739,12 @@ export const createBucket = async (api: ApiPromise, bucketName: string) => {
738739
return event;
739740
};
740741

741-
export const cleardownTest = async (api: BspNetApi) => {
742-
await api.disconnect();
743-
await closeSimpleBspNet();
742+
export const cleardownTest = async (cleardownOptions: {
743+
api: BspNetApi;
744+
keepNetworkAlive?: boolean;
745+
}) => {
746+
await cleardownOptions.api.disconnect();
747+
cleardownOptions.keepNetworkAlive === true ? null : await closeSimpleBspNet();
744748
};
745749

746750
export const createCheckBucket = async (api: BspNetApi, bucketName: string) => {

‎tsconfig.json

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
// Bundler mode
1313
"moduleResolution": "bundler",
1414
"allowImportingTsExtensions": true,
15+
"removeComments": false,
16+
"sourceMap": true,
1517
"verbatimModuleSyntax": true,
1618
"noEmit": true,
1719

0 commit comments

Comments
 (0)
Please sign in to comment.