Files
copilotkit__copilotkit/showcase/scripts/__tests__/state-context-fixture-routing.test.ts
Jordan Ritter ef3e59643b fix(showcase): update fixture routing tests for per-integration D6 layout
The four fixture routing regression tests still referenced the old
monolithic d5-all.json / smoke.json / feature-parity.json files that
were reorganized into per-integration d4/ d6/ shared/ directories.

Changes:
- Load fixtures via glob from d6/langgraph-python/ (reference
  integration) instead of deleted monolithic files
- Add _context to test requests (aimock 1.26.1 checks req._context
  against match.context for per-integration scoping)
- Adapt subagents test from toolCallId-based chaining to
  turnIndex-based chaining (matches new D6 fixture structure)
- Adapt state-context test to D6 context-scoping model (no
  systemMessage matching; routing happens via X-AIMock-Context)
- Remove _migrated-from-*.json shared files (all fixtures already
  exist in per-integration D6 dirs; the migration files caused
  620 shared-vs-scoped collisions)
- Add KNOWN_DUPLICATE_CEILING=11 ratchet for pre-existing D6
  intra-feature duplicate match keys
2026-05-26 11:54:06 -07:00

121 lines
3.7 KiB
TypeScript

import { describe, expect, it } from "vitest";
import path from "node:path";
import { globSync } from "glob";
import { loadFixtureFile, matchFixture } from "@copilotkit/aimock";
import type {
ChatCompletionRequest,
Fixture,
TextResponse,
} from "@copilotkit/aimock";
const REPO_ROOT = path.resolve(__dirname, "..", "..", "..");
// Load D6 fixtures only for this test. State/context demos are D6 features;
// D4 chat fixtures contain broad substring matchers (e.g. "Say hi") that
// shadow the more specific D6 shared-state fixtures when loaded together.
// Migration files (_migrated-from-*.json) are excluded because they contain
// systemMessage-based discriminators that loadFixtureFile strips.
function loadBundledFixtures(): Fixture[] {
const fixtureFiles = [
...globSync("showcase/aimock/shared/*.json", {
cwd: REPO_ROOT,
absolute: true,
}).filter((f) => !path.basename(f).startsWith("_migrated")),
...globSync("showcase/aimock/d6/langgraph-python/*.json", {
cwd: REPO_ROOT,
absolute: true,
}),
];
return fixtureFiles.flatMap((f) => loadFixtureFile(f));
}
function request(
userMessage: string,
systemMessage: string,
): ChatCompletionRequest {
return {
model: "gpt-5.4",
messages: [
{ role: "system", content: systemMessage },
{ role: "user", content: userMessage },
],
// D6 fixtures use match.context for per-integration scoping; aimock's
// matchFixture checks req._context against it.
_context: "langgraph-python",
} as ChatCompletionRequest;
}
const DEFAULT_SHARED_STATE_SYSTEM = `
Application State
{
"preferences": {
"name": "",
"tone": "casual",
"language": "English",
"interests": []
},
"notes": []
}
`;
const DEFAULT_READONLY_CONTEXT_SYSTEM = `
## Context from the application
The currently logged-in user's display name:
Atai
The user's IANA timezone (used when mentioning times):
America/Los_Angeles
The user's recent activity in the app, newest first:
["Viewed the pricing page","Watched the product demo video"]
`;
// D6 fixtures use integration-level context scoping (X-AIMock-Context header)
// instead of systemMessage-based differentiation. At the fixture level, the
// same userMessage matches regardless of system message content — the routing
// to the correct integration's fixtures happens at the HTTP layer.
//
// This test verifies that the default demo fixtures exist and produce
// semantically correct content for the reference integration.
describe("state/context fixture routing", () => {
it("shared-state default preferences match with correct content", () => {
const fixtures = loadBundledFixtures();
const defaultMatch = matchFixture(
fixtures,
request("Say hi and introduce yourself.", DEFAULT_SHARED_STATE_SYSTEM),
);
expect(defaultMatch).not.toBeNull();
expect((defaultMatch!.response as TextResponse).content).toContain(
"shared-state co-pilot",
);
});
it("readonly context defaults match with correct content", () => {
const fixtures = loadBundledFixtures();
const defaultMatch = matchFixture(
fixtures,
request(
"What do you know about me from my context?",
DEFAULT_READONLY_CONTEXT_SYSTEM,
),
);
expect(defaultMatch).not.toBeNull();
expect((defaultMatch!.response as TextResponse).content).toContain("Atai");
});
it("readonly context follow-up question matches", () => {
const fixtures = loadBundledFixtures();
const followUp = matchFixture(
fixtures,
request(
"Based on my recent activity, what should I try next?",
DEFAULT_READONLY_CONTEXT_SYSTEM,
),
);
expect(followUp).not.toBeNull();
expect((followUp!.response as TextResponse).content).toBeTruthy();
});
});