Skip to content

Commit f419435

Browse files
committed
fix(test): msp and solochain-evm tests use proper fisherman deletion flow
1 parent 9056241 commit f419435

File tree

2 files changed

+94
-56
lines changed

2 files changed

+94
-56
lines changed

test/suites/integration/msp/duplicate-storage-request.test.ts

Lines changed: 79 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import {
55
type FileMetadata,
66
shUser,
77
sleep,
8+
type SqlClient,
89
waitFor
910
} from "../../../util";
1011

@@ -17,15 +18,27 @@ await describeMspNet(
1718
indexerMode: "fishing",
1819
standaloneIndexer: true
1920
},
20-
({ before, createUserApi, createBspApi, createMsp1Api, it, getLaunchResponse }) => {
21+
({
22+
before,
23+
createUserApi,
24+
createBspApi,
25+
createMsp1Api,
26+
createFishermanApi,
27+
it,
28+
getLaunchResponse,
29+
createSqlClient,
30+
createIndexerApi
31+
}) => {
2132
let userApi: EnrichedBspApi;
2233
let bspApi: EnrichedBspApi;
2334
let mspApi: EnrichedBspApi;
35+
let sql: SqlClient;
2436

2537
const bucketId1 = "cloud-bucket-1";
2638
const bucketId2 = "cloud-bucket-2";
2739
let file1: FileMetadata;
2840
let file2: FileMetadata;
41+
let indexerApi: EnrichedBspApi;
2942

3043
// Helper to build and sign a file deletion intention
3144
const buildSignedDelete = (fileKey: string) => {
@@ -44,9 +57,17 @@ await describeMspNet(
4457
userApi = await createUserApi();
4558
bspApi = await createBspApi();
4659
const maybeMspApi = await createMsp1Api();
60+
sql = createSqlClient();
4761

4862
assert(maybeMspApi, "MSP API not available");
4963
mspApi = maybeMspApi;
64+
65+
assert(createFishermanApi, "Fisherman API not available");
66+
await createFishermanApi();
67+
68+
assert(createIndexerApi, "Indexer API not available");
69+
indexerApi = await createIndexerApi();
70+
await indexerApi.indexer.waitForIndexing({ producerApi: userApi, sql });
5071
});
5172

5273
it("Network launches and can be queried", async () => {
@@ -230,68 +251,73 @@ await describeMspNet(
230251
signer: shUser
231252
});
232253

233-
// Fisherman submits delete_files extrinsics (MSP + BSP)
234-
await userApi.assert.extrinsicPresent({
235-
method: "deleteFiles",
236-
module: "fileSystem",
237-
checkTxPool: true,
238-
assertLength: 2 // 1 for MSP and 1 for BSP
239-
});
240-
const block = await userApi.block.seal();
254+
// Finalize the block on the indexer node and wait for the indexer to process the block
255+
await indexerApi.indexer.waitForIndexing({ producerApi: userApi, sql });
241256

242-
// Verify file removed from first bucket's forest
243-
await waitFor({
244-
lambda: async () =>
245-
(await mspApi.rpc.storagehubclient.isFileInForest(file1.bucketId, file1.fileKey)).isFalse
257+
// Wait for fisherman to process the file deletions
258+
await userApi.fisherman.retryableWaitAndVerifyBatchDeletions({
259+
blockProducerApi: userApi,
260+
deletionType: "User",
261+
expectExt: 2,
262+
userApi,
263+
bspApi,
264+
expectedBspCount: 1,
265+
mspApi: mspApi,
266+
expectedBucketCount: 1,
267+
maxRetries: 3
246268
});
247269

248-
// Finalising block in MSP node to trigger the event to delete the file.
249-
// First we make sure the MSP is caught up to the block.
250-
// Actually, it should trigger, but the file still shouldn't be deleted.
251-
await mspApi.wait.nodeCatchUpToChainTip(userApi);
252-
await mspApi.block.finaliseBlock(block.blockReceipt.blockHash.toString());
270+
// Non-producer nodes must explicitly finalize imported blocks to trigger file deletion
271+
// Producer node (user) has finalized blocks, but BSP and MSP must finalize locally
272+
const finalisedBlockHash = await userApi.rpc.chain.getFinalizedHead();
253273

254-
// Verify that the file metadata from the first file was removed from
255-
// the file storage of the MSP, meaning that it should respond that this
256-
// fileKey is not in the file storage.
257-
await waitFor({
258-
lambda: async () =>
259-
(await mspApi.rpc.storagehubclient.isFileInFileStorage(file1.fileKey)).isFileNotFound
260-
});
274+
await bspApi.wait.blockImported(finalisedBlockHash.toString());
275+
await bspApi.block.finaliseBlock(finalisedBlockHash.toString());
261276

262-
// But verify MSP still has the file in file storage via the second file key,
263-
// meaning that for that second file key it has both the file metadata and the file content
264-
// (which was the same file content as the first file)
265-
await waitFor({
266-
lambda: async () =>
267-
(await mspApi.rpc.storagehubclient.isFileInFileStorage(file2.fileKey)).isFileFound
268-
});
277+
await mspApi.wait.blockImported(finalisedBlockHash.toString());
278+
await mspApi.block.finaliseBlock(finalisedBlockHash.toString());
269279

270-
// Verify file removed from BSP's forest
280+
// Verify that the file metadata from the second file was removed from
281+
// the file storage of the BSP and MSP, meaning that it should respond that this
282+
// fileKey is not in the file storage. Now the file content should have
283+
// been deleted from the file storage as well.
271284
await waitFor({
272-
lambda: async () =>
273-
(await bspApi.rpc.storagehubclient.isFileInForest(null, file1.fileKey)).isFalse
274-
});
285+
lambda: async () => {
286+
// Check file is NOT in BSP forest
287+
const bspForestResult = await bspApi.rpc.storagehubclient.isFileInForest(
288+
null,
289+
file1.fileKey
290+
);
291+
if (bspForestResult.isTrue) {
292+
return false;
293+
}
275294

276-
// Finalising block in BSP node to trigger the event to delete the file.
277-
// First we make sure the BSP is caught up to the block.
278-
await bspApi.wait.nodeCatchUpToChainTip(userApi);
279-
await bspApi.block.finaliseBlock(block.blockReceipt.blockHash.toString());
295+
// Check file is NOT in BSP file storage
296+
const bspFileStorageResult = await bspApi.rpc.storagehubclient.isFileInFileStorage(
297+
file1.fileKey
298+
);
299+
if (bspFileStorageResult.isFileFound) {
300+
return false;
301+
}
280302

281-
// Verify that the file metadata from the first file was removed from
282-
// the file storage of the BSP, meaning that it should respond that this
283-
// fileKey is not in the file storage.
284-
await waitFor({
285-
lambda: async () =>
286-
(await bspApi.rpc.storagehubclient.isFileInFileStorage(file1.fileKey)).isFileNotFound
287-
});
303+
// Check file is NOT in MSP forest
304+
const mspForestResult = await mspApi.rpc.storagehubclient.isFileInForest(
305+
file2.bucketId,
306+
file1.fileKey
307+
);
308+
if (mspForestResult.isTrue) {
309+
return false;
310+
}
288311

289-
// But verify BSP still has the file in file storage via the second file key,
290-
// meaning that for that second file key it has both the file metadata and the file content
291-
// (which was the same file content as the first file)
292-
await waitFor({
293-
lambda: async () =>
294-
(await bspApi.rpc.storagehubclient.isFileInFileStorage(file2.fileKey)).isFileFound
312+
// Check file is NOT in MSP file storage
313+
const mspFileStorageResult = await mspApi.rpc.storagehubclient.isFileInFileStorage(
314+
file1.fileKey
315+
);
316+
if (mspFileStorageResult.isFileFound) {
317+
return false;
318+
}
319+
return true;
320+
}
295321
});
296322
});
297323

test/suites/integration/solochain-evm/sdk-precompiles.test.ts

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import {
1515
import { MspClient } from "@storagehub-sdk/msp-client";
1616
import { createPublicClient, createWalletClient, defineChain, http, getAddress } from "viem";
1717
import { privateKeyToAccount } from "viem/accounts";
18-
import { describeMspNet, type EnrichedBspApi, ShConsts } from "../../../util";
18+
import { describeMspNet, type EnrichedBspApi, ShConsts, type SqlClient } from "../../../util";
1919
import { SH_EVM_SOLOCHAIN_CHAIN_ID } from "../../../util/evmNet/consts";
2020
import { ALITH_PRIVATE_KEY } from "../../../util/evmNet/keyring";
2121

@@ -26,15 +26,18 @@ await describeMspNet(
2626
runtimeType: "solochain",
2727
indexer: true,
2828
backend: true,
29-
fisherman: true
29+
fisherman: true,
30+
standaloneIndexer: true
3031
},
31-
({ before, it, createUserApi, createMsp1Api }) => {
32+
({ before, it, createUserApi, createMsp1Api, createSqlClient, createIndexerApi }) => {
3233
let userApi: EnrichedBspApi;
3334
let msp1Api: EnrichedBspApi;
35+
let indexerApi: EnrichedBspApi;
3436
let storageHubClient: InstanceType<typeof StorageHubClient>;
3537
let publicClient: ReturnType<typeof createPublicClient>;
3638
let walletClient: ReturnType<typeof createWalletClient>;
3739
let account: ReturnType<typeof privateKeyToAccount>;
40+
let sql: SqlClient;
3841
let bucketId: string;
3942
let fileManager: FileManager;
4043
let fileKey: H256;
@@ -52,6 +55,7 @@ await describeMspNet(
5255
} else {
5356
throw new Error("MSP API for first MSP not available");
5457
}
58+
sql = createSqlClient();
5559

5660
// Set up the StorageHub SDK client using viem
5761
const rpcUrl = `http://127.0.0.1:${ShConsts.NODE_INFOS.user.port}`;
@@ -105,6 +109,10 @@ await describeMspNet(
105109
// Set up the authentication with the MSP backend
106110
const siweSession = await mspClient.auth.SIWE(walletClient);
107111
sessionToken = siweSession.token;
112+
113+
assert(createIndexerApi, "Indexer API not available");
114+
indexerApi = await createIndexerApi();
115+
await indexerApi.indexer.waitForIndexing({ producerApi: userApi, sql });
108116
});
109117

110118
it("Postgres DB is ready", async () => {
@@ -528,6 +536,10 @@ await describeMspNet(
528536
// Verify the deletion request was enqueued on-chain
529537
await userApi.assert.eventPresent("fileSystem", "FileDeletionRequested");
530538

539+
// Finalize the block on the indexer node and wait for the indexer to process the block
540+
await indexerApi.indexer.waitForIndexing({ producerApi: userApi, sql });
541+
542+
// Wait for fisherman to process the file deletions
531543
await userApi.fisherman.retryableWaitAndVerifyBatchDeletions({
532544
blockProducerApi: userApi,
533545
deletionType: "User",

0 commit comments

Comments
 (0)