FN-8670: reuse model runtime fixtures in engine tests

Share a warmed Pi model runtime across engine catalog tests.

- Add an isolated in-memory model registry fixture backed by a per-file shared runtime.
- Warm the runtime before catalog tests and verify custom-provider registry isolation.
- Document the required fixture pattern for real Pi SDK catalog tests.

Files changed:
 docs/testing.md                                    |  1 +
 .../engine/src/__tests__/_model-runtime-fixture.ts | 40 ++++++++++++++++++++++
 .../custom-providers-openai-completions.test.ts    | 15 +++-----
 .../custom-providers-openai-responses.test.ts      | 15 +++-----
 .../src/__tests__/provider-registration.test.ts    | 33 ++++++++++++------
 5 files changed, 73 insertions(+), 31 deletions(-)

Fusion-Task-Id: FN-8670

Fusion-Task-Lineage: 7410d114-1853-44cc-a09b-a997fbc7f119

Co-authored-by: Fusion (runfusion.ai) <noreply@runfusion.ai>
This commit is contained in:
gsxdsm
2026-08-01 01:34:43 -07:00
parent 5786c87eff
commit 13f7266b67
5 changed files with 73 additions and 31 deletions

View File

@@ -631,6 +631,7 @@ When a test owns a process-wide singleton that has asynchronous owners (timers,
- Prefer fake timers over real polling/time waits (FN-2707 pattern: advance timers inside `act(...)`, restore with `afterEach(() => vi.useRealTimers())`).
- Do **not** mask slowness by raising worker/concurrency knobs (`FUSION_TEST_TOTAL_WORKERS`, `FUSION_TEST_CONCURRENCY`, `VITEST_MAX_WORKERS`, workspace concurrency settings).
- Do **not** add net-new real-network calls, real-`setTimeout` polling loops, or mock-the-world component shells when a narrower seam exists.
- Real Pi SDK catalog tests in the engine package must use `src/__tests__/_model-runtime-fixture.ts`: warm its shared runtime in `beforeAll` and request a fresh registry rather than constructing `ModelRuntime` inside timed test bodies.
- Use the canonical taxonomy in **What NOT to write** and **What TO keep unconditionally** when deciding trim vs keep.
- See `docs/test-speed-audit-FN-5048.md` for the measured baseline offender list and optimization priorities.

View File

@@ -0,0 +1,40 @@
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
/*
FNXC:ModelCatalog 2026-08-01-08:14:
FN-8670 moves real Pi SDK catalog construction out of timed test bodies into beforeAll. The runtime is
memoized per test file because Vitest file-level isolation gives each file its own module graph.
ModelRegistry.registerProvider writes into its shared ModelRuntime, so each handed-out registry clears
prior extension providers before use. The SDK remains real, not stubbed, because this coverage catches
catalog regressions such as FN-8564's native Kimi K3; widening timeouts or adding retries is forbidden here.
*/
let sharedModelRuntime: Promise<ModelRuntime> | undefined;
export function getSharedModelRuntime(): Promise<ModelRuntime> {
sharedModelRuntime ??= ModelRuntime.create({
credentials: {
read: async () => undefined,
list: async () => [],
modify: async (_id, fn) => fn(undefined),
delete: async () => undefined,
},
modelsPath: null,
allowModelNetwork: false,
});
return sharedModelRuntime;
}
export async function warmSharedModelRuntime(): Promise<void> {
await getSharedModelRuntime();
}
export async function createInMemoryModelRegistry(): Promise<ModelRegistry> {
const modelRegistry = new ModelRegistry(await getSharedModelRuntime());
for (const providerId of modelRegistry.getRegisteredProviderIds()) {
modelRegistry.unregisterProvider(providerId);
}
await modelRegistry.refresh();
return modelRegistry;
}

View File

@@ -1,5 +1,4 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { completeSimple } from "@earendil-works/pi-ai/compat";
/*
FNXC:Dependencies 2026-07-01-08:16:
@@ -7,6 +6,7 @@ The pi 0.80 SDK keeps compatibility helpers under ./compat and exposes provider
*/
import { convertMessages } from "@earendil-works/pi-ai/api/openai-completions";
import { customProviderRegistryKey, type CustomProvider } from "@fusion/core";
import { createInMemoryModelRegistry, warmSharedModelRuntime } from "./_model-runtime-fixture.js";
function createSseResponse(): Response {
const stream = new ReadableStream({
@@ -21,14 +21,9 @@ function createSseResponse(): Response {
}
async function createInMemoryModelRegistry(): Promise<ModelRegistry> {
const runtime = await ModelRuntime.create({
credentials: { read: async () => undefined, list: async () => [], modify: async (_id, fn) => fn(undefined), delete: async () => undefined },
modelsPath: null,
allowModelNetwork: false,
});
return new ModelRegistry(runtime);
}
beforeAll(async () => {
await warmSharedModelRuntime();
});
describe("custom providers openai-completions regression", () => {
afterEach(() => {

View File

@@ -1,19 +1,14 @@
import { mkdir, mkdtemp, rm, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { afterEach, beforeEach, describe, expect, it } from "vitest";
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { customProviderRegistryKey, type CustomProvider } from "@fusion/core";
import { readCustomProviders } from "../custom-providers.js";
import { createInMemoryModelRegistry, warmSharedModelRuntime } from "./_model-runtime-fixture.js";
async function createInMemoryModelRegistry(): Promise<ModelRegistry> {
const runtime = await ModelRuntime.create({
credentials: { read: async () => undefined, list: async () => [], modify: async (_id, fn) => fn(undefined), delete: async () => undefined },
modelsPath: null,
allowModelNetwork: false,
});
return new ModelRegistry(runtime);
}
beforeAll(async () => {
await warmSharedModelRuntime();
});
describe("custom providers openai-responses regression", () => {
let homeDir: string;

View File

@@ -1,9 +1,9 @@
import { afterEach, describe, expect, it, vi } from "vitest";
import { afterEach, beforeAll, describe, expect, it, vi } from "vitest";
import { customProviderRegistryKey, type CustomProvider } from "@fusion/core";
import { ModelRegistry, ModelRuntime } from "@earendil-works/pi-coding-agent";
import { completeSimple } from "@earendil-works/pi-ai/compat";
import { seedDashboardProviders } from "../provider-registration.js";
import { registerCustomProviders } from "../custom-provider-registry.js";
import { createInMemoryModelRegistry, warmSharedModelRuntime } from "./_model-runtime-fixture.js";
/*
FNXC:ProviderRegistration 2026-07-07-00:00:
@@ -36,15 +36,6 @@ function makeAuthStorage() {
} as any;
}
async function createInMemoryModelRegistry(): Promise<ModelRegistry> {
const runtime = await ModelRuntime.create({
credentials: { read: async () => undefined, list: async () => [], modify: async (_id, fn) => fn(undefined), delete: async () => undefined },
modelsPath: null,
allowModelNetwork: false,
});
return new ModelRegistry(runtime);
}
function makeModelRegistry() {
const registeredProviders = new Map<string, { models: Array<{ provider: string; id: string }> }>();
return {
@@ -94,6 +85,10 @@ function makeStore(initialCustomProviders?: CustomProvider[]) {
};
}
beforeAll(async () => {
await warmSharedModelRuntime();
});
const customProvider = (overrides: Partial<CustomProvider> = {}): CustomProvider => ({
id: "550e8400-e29b-41d4-a716-446655440000",
name: "Acme AI",
@@ -144,6 +139,22 @@ describe("seedDashboardProviders", () => {
});
});
it("hands each real registry an isolated custom-provider catalog", async () => {
const provider = customProvider({ id: "isolation-provider-id", name: "Fixture Isolation Provider" });
const registryA = await createInMemoryModelRegistry();
registryA.registerProvider(customProviderRegistryKey(provider, [provider]), {
baseUrl: provider.baseUrl,
api: "openai-completions",
apiKey: provider.apiKey,
models: [{ id: "acme-1", name: "Acme Model 1", reasoning: false, input: ["text"], cost: { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 }, contextWindow: 128000, maxTokens: 16384 }],
});
const registryB = await createInMemoryModelRegistry();
expect(registryB.find("fixture-isolation-provider", "acme-1")).toBeUndefined();
expect(registryB.find("kimi-coding", "k3")).toMatchObject({ provider: "kimi-coding", id: "k3" });
});
it("registers one custom provider alongside built-ins", async () => {
const store = makeStore([customProvider()]);
const authStorage = makeAuthStorage();