evOo-local/packages/testkit/src/model-emulator.test.ts
cynarAI 6c7eed7580 Initial clean evOo-local from f00577b
Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline
(Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/*
packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
2026-09-20 17:15:16 +02:00

80 lines
2.8 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { startModelEmulator } from "./model-emulator.js";
const servers: Array<Awaited<ReturnType<typeof startModelEmulator>>> = [];
afterEach(async () => {
await Promise.all(servers.splice(0).map((server) => server.close()));
});
async function post(server: Awaited<ReturnType<typeof startModelEmulator>>, overrides = {}) {
return fetch(`${server.baseUrl}/chat/completions`, {
method: "POST",
headers: { authorization: "Bearer local", "content-type": "application/json" },
body: JSON.stringify({
model: server.model.id,
stream: true,
messages: [{ role: "user", content: "fixture" }],
...overrides,
}),
});
}
describe("model HTTP emulator contract", () => {
it("fails locally when a request assertion fails, even if the caller ignores the HTTP error", async () => {
const server = await startModelEmulator({
steps: [
{
expect(request) {
expect(request.messages.at(-1)?.content).toBe("required task");
},
response: { type: "text", text: "must not be returned" },
},
],
});
servers.push(server);
const response = await post(server);
expect(response.status).toBe(400);
expect(await response.text()).not.toContain("must not be returned");
expect(() => server.assertComplete()).toThrow(/required task/);
});
it("rejects incomplete scenarios and unexpected extra model requests", async () => {
const server = await startModelEmulator({
steps: [{ expect() {}, response: { type: "text", text: "fixture" } }],
});
servers.push(server);
expect(() => server.assertComplete()).toThrow(/Not all model fixture steps/);
expect((await post(server)).status).toBe(200);
server.assertComplete();
expect((await post(server)).status).toBe(400);
expect(() => server.assertComplete()).toThrow(/Unexpected model request 2/);
});
it("rejects a request routed to the wrong model", async () => {
const server = await startModelEmulator({ steps: [] });
servers.push(server);
expect((await post(server, { model: "wrong-model" })).status).toBe(400);
expect(() => server.assertComplete()).toThrow(/Model routing changed/);
});
it("requires argument fragments to faithfully represent the scripted tool arguments", async () => {
const server = await startModelEmulator({
steps: [
{
expect() {},
response: {
type: "tool",
id: "bad-fragments",
name: "write_file",
arguments: { path: "file" },
argumentChunks: ["{}"],
},
},
],
});
servers.push(server);
expect((await post(server)).status).toBe(400);
expect(() => server.assertComplete()).toThrow();
});
});