diff --git a/packages/sdk/tests-qvac/tests/shared/executors/logging-executor.ts b/packages/sdk/tests-qvac/tests/shared/executors/logging-executor.ts index b014988d16..e3f61ea7b2 100644 --- a/packages/sdk/tests-qvac/tests/shared/executors/logging-executor.ts +++ b/packages/sdk/tests-qvac/tests/shared/executors/logging-executor.ts @@ -7,6 +7,7 @@ import { diffusion, unloadModel, SDK_LOG_ID, + SDK_SERVER_ERROR_CODES, } from "@qvac/sdk"; import { type TestResult } from "@tetherto/qvac-test-suite"; import { AbstractModelExecutor } from "./abstract-model-executor.js"; @@ -51,6 +52,23 @@ class AddonBusyTimeoutError extends Error { } } +// "Slot still occupied" surfaces as either the legacy addon throw or, since +// the `oneAtATimePerModel` registry policy, a typed admission rejection. +// Errors cross the RPC boundary as `RPCError`, so we match by code, not +// `instanceof`. +function isTransientAddonBusy(err: unknown): boolean { + if ( + err !== null + && typeof err === "object" + && "code" in err + && (err as { code?: unknown }).code === SDK_SERVER_ERROR_CODES.REQUEST_REJECTED_BY_POLICY + ) { + return true; + } + const msg = err instanceof Error ? err.message : String(err); + return msg.includes(ADDON_BUSY_MARKER); +} + export async function callWhenAddonIdle( fn: () => Promise, timeoutMs = ADDON_BUSY_TIMEOUT_MS, @@ -61,8 +79,7 @@ export async function callWhenAddonIdle( try { return await fn(); } catch (err) { - const msg = err instanceof Error ? err.message : String(err); - if (msg.includes(ADDON_BUSY_MARKER)) { + if (isTransientAddonBusy(err)) { if (Date.now() >= deadline) throw new AddonBusyTimeoutError(timeoutMs, err); await sleep(intervalMs); continue; @@ -248,13 +265,26 @@ export class LoggingExecutor extends AbstractModelExecutor } const reloadedModelId = await this.resources.ensureLoaded(dep); - return collectLogs({ + + // Join the trigger before returning — `collectLogs` races it against + // log arrival, so without this the next test can race the still-open + // completion slot. + let triggerPromise: Promise = Promise.resolve(); + const result = await collectLogs({ testId, targetId: reloadedModelId, target: 1, postTriggerWaitMs: RELOAD_DRAIN_MS, - trigger: () => callWhenAddonIdle(() => runCompletion(reloadedModelId, "Post-reload test", false)), + trigger: () => { + triggerPromise = callWhenAddonIdle(() => + runCompletion(reloadedModelId, "Post-reload test", false), + ); + return triggerPromise; + }, }); + + await triggerPromise.catch(() => {}); + return result; } protected triggerLlm(modelId: string): Promise {