From f776bfc5ad19c88dfc6c6e692f9357a5a1564dd7 Mon Sep 17 00:00:00 2001 From: Qiiks Date: Sat, 19 Sep 2026 06:43:55 +0530 Subject: [PATCH] test(plugin): assert shadow retirement without depending on fire-and-forget dispose timing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both retire-shadow tests asserted `expect(disposed).toBe(true)` against a provider whose `dispose` was `async` with no await before setting the flag. disposeProvider is fire-and-forget (`void provider.dispose()`), so that assertion only held because the async body happened to run synchronously up to its first await — it measured the microtask schedule, not retirement. If disposal ever deferred, the test would silently stop verifying it. Record the flag when dispose is called and return a resolved promise, so the assertion covers the deterministic contract: dispose was invoked. The outcome assertions (cohort null, primary lane intact) are unchanged. --- packages/pi-plugin/src/embedding-bootstrap.test.ts | 14 ++++++++++++-- .../plugin/src/plugin/embedding-bootstrap.test.ts | 6 +++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/packages/pi-plugin/src/embedding-bootstrap.test.ts b/packages/pi-plugin/src/embedding-bootstrap.test.ts index 0fa706d46a..3b214c5dc6 100644 --- a/packages/pi-plugin/src/embedding-bootstrap.test.ts +++ b/packages/pi-plugin/src/embedding-bootstrap.test.ts @@ -114,8 +114,13 @@ describe("ensureProjectRegisteredFromPiDirectory", () => { embed: async () => new Float32Array([1, 0]), embedBatch: async (texts: string[]) => texts.map(() => new Float32Array([1, 0])), - dispose: async () => { + // Non-async: the flag is recorded when dispose is *called*, which is the + // deterministic contract. disposeProvider is fire-and-forget + // (`void provider.dispose()`), so an async body here would make the + // assertion below depend on that body running before its first await. + dispose: () => { disposed = true; + return Promise.resolve(); }, isLoaded: () => true, })); @@ -214,8 +219,13 @@ describe("ensureProjectRegisteredFromPiDirectory", () => { embed: async () => new Float32Array([1, 0]), embedBatch: async (texts: string[]) => texts.map(() => new Float32Array([1, 0])), - dispose: async () => { + // Non-async: the flag is recorded when dispose is *called*, which is the + // deterministic contract. disposeProvider is fire-and-forget + // (`void provider.dispose()`), so an async body here would make the + // assertion below depend on that body running before its first await. + dispose: () => { disposed = true; + return Promise.resolve(); }, isLoaded: () => true, })); diff --git a/packages/plugin/src/plugin/embedding-bootstrap.test.ts b/packages/plugin/src/plugin/embedding-bootstrap.test.ts index 6cc3486ba3..fc94930935 100644 --- a/packages/plugin/src/plugin/embedding-bootstrap.test.ts +++ b/packages/plugin/src/plugin/embedding-bootstrap.test.ts @@ -36,8 +36,12 @@ function installShadowProvider(onDispose: () => void): void { initialize: async () => true, embed: async () => new Float32Array([1, 0]), embedBatch: async (texts: string[]) => texts.map(() => new Float32Array([1, 0])), - dispose: async () => { + // Non-async: record the flag at call time. disposeProvider is + // fire-and-forget, so an async body would make the assertion depend on + // that body running before its first await rather than on retirement. + dispose: () => { onDispose(); + return Promise.resolve(); }, isLoaded: () => true, }));