Skip to content

Commit 612421a

Browse files
fix: harden e2e fixture registration
1 parent a7410f4 commit 612421a

1 file changed

Lines changed: 76 additions & 1 deletion

File tree

tools/register-e2e-fixtures.ts

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,18 @@
2626

2727
import { parseArgs } from "node:util";
2828
import { destroyConnection } from "../src/utils/connection.js";
29+
import { checkAllowance, ensureAllowance } from "../src/utils/account/allowance.js";
2930
import { publishToPlayground, normalizeDomain } from "../src/utils/deploy/playground.js";
31+
import { getReadOnlyRegistryContract } from "../src/utils/registry.js";
3032
import { resolveSigner } from "../src/utils/signer.js";
3133
import { SIGNER, E2E_DOMAINS } from "../e2e/cli/fixtures/accounts.js";
32-
import { destroyTestClient } from "../e2e/cli/helpers/chain.js";
34+
import { destroyTestClient, getTestClient } from "../e2e/cli/helpers/chain.js";
3335
import { fundAccountIfLow } from "../e2e/cli/setup/fund.js";
3436

3537
const DEFAULT_TEMPLATE_DOMAIN = "dot-cli-mod-fixture.dot";
3638
const DEFAULT_TEMPLATE_REPO = "https://github.com/paritytech/Rock-Paper-Scissors";
39+
const REGISTRY_READBACK_ATTEMPTS = 5;
40+
const REGISTRY_READBACK_DELAY_MS = 2_000;
3741

3842
interface Fixture {
3943
domain: string;
@@ -88,6 +92,71 @@ function logPlan(fixtures: readonly Fixture[]): void {
8892
console.log();
8993
}
9094

95+
function errorText(error: unknown): string {
96+
if (error instanceof Error) return `${error.name}: ${error.message}`;
97+
return String(error);
98+
}
99+
100+
function isBulletinTeardownNoise(error: unknown): boolean {
101+
const text = error instanceof Error ? `${error.name}: ${error.message}\n${error.stack ?? ""}` : String(error);
102+
return (
103+
text.includes("ChainHead disjointed") ||
104+
(text.includes("UnsubscriptionError") && text.includes("Not connected"))
105+
);
106+
}
107+
108+
function suppressStandaloneBulletinTeardownNoise(): () => void {
109+
const onUncaught = (error: unknown) => {
110+
if (isBulletinTeardownNoise(error)) {
111+
console.warn(`ignored Bulletin client teardown noise: ${errorText(error)}`);
112+
return;
113+
}
114+
process.off("uncaughtException", onUncaught);
115+
throw error;
116+
};
117+
118+
process.prependListener("uncaughtException", onUncaught);
119+
return () => process.off("uncaughtException", onUncaught);
120+
}
121+
122+
async function delay(ms: number): Promise<void> {
123+
await new Promise((resolve) => setTimeout(resolve, ms));
124+
}
125+
126+
function formatAllowance(status: Awaited<ReturnType<typeof checkAllowance>>): string {
127+
if (!status.authorized) return "not authorized";
128+
return `${status.remainingTxs} tx / ${status.remainingBytes} bytes`;
129+
}
130+
131+
async function ensureFixtureStorageAllowance(address: string): Promise<void> {
132+
const client = await getTestClient();
133+
const before = await checkAllowance(client, address);
134+
console.log(`Bulletin allowance ${formatAllowance(before)}`);
135+
136+
await ensureAllowance(client, address);
137+
138+
const after = await checkAllowance(client, address);
139+
if (formatAllowance(after) !== formatAllowance(before)) {
140+
console.log(`Bulletin allowance ${formatAllowance(after)}`);
141+
}
142+
console.log();
143+
}
144+
145+
async function verifyRegistryEntry(domain: string, metadataCid: string): Promise<void> {
146+
const client = await getTestClient();
147+
const registry = await getReadOnlyRegistryContract(client.raw.assetHub);
148+
149+
for (let attempt = 1; attempt <= REGISTRY_READBACK_ATTEMPTS; attempt++) {
150+
const result = await registry.getMetadataUri.query(domain);
151+
const value = result.value as { isSome?: boolean; value?: string } | undefined;
152+
if (result.success && value?.isSome && value.value === metadataCid) return;
153+
154+
if (attempt < REGISTRY_READBACK_ATTEMPTS) await delay(REGISTRY_READBACK_DELAY_MS);
155+
}
156+
157+
throw new Error(`Registry readback failed for ${domain}: expected metadata CID ${metadataCid}`);
158+
}
159+
91160
async function registerFixture(
92161
fixture: Fixture,
93162
signer: Awaited<ReturnType<typeof resolveSigner>>,
@@ -110,7 +179,10 @@ async function registerFixture(
110179
},
111180
});
112181

182+
await verifyRegistryEntry(result.fullDomain, result.metadataCid);
183+
113184
const elapsed = ((Date.now() - start) / 1000).toFixed(1);
185+
console.log(" verified registry readback");
114186
console.log(` published ${result.metadataCid} (${elapsed}s)`);
115187
console.log();
116188
}
@@ -145,18 +217,21 @@ async function main(): Promise<number> {
145217
}
146218

147219
const signer = await resolveSigner({ suri: values.suri ?? SIGNER.suri });
220+
const restoreErrorHandling = suppressStandaloneBulletinTeardownNoise();
148221
try {
149222
logPlan(fixtures);
150223
console.log(`signer ${signer.address} (${signer.source})`);
151224
await fundAccountIfLow({ name: "fixture signer", address: signer.address });
152225
console.log();
226+
await ensureFixtureStorageAllowance(signer.address);
153227

154228
for (const [index, fixture] of fixtures.entries()) {
155229
await registerFixture(fixture, signer, index + 1, fixtures.length);
156230
}
157231
console.log(`registered ${fixtures.length} fixture(s)`);
158232
return 0;
159233
} finally {
234+
restoreErrorHandling();
160235
signer.destroy();
161236
destroyTestClient();
162237
destroyConnection();

0 commit comments

Comments
 (0)