test(ai): answer both switch readers from one source in flag mocks

Eight files mocked `@/lib/feature-flags` with `getAssistantFlags` alone.
The capability loader reads `loadAssistantSwitches`, so wherever it ran
it threw and every capability resolved to `check_failed`. A shared
factory now points both readers at the function the test drives.

Only the config-change chain reached the loader. Its fixture means AI
switched off by the operator; it now supplies the loader's other inputs
and asserts `operator_disabled` on the digest, so a starved loader turns
it red instead of passing on the failure path.
This commit is contained in:
Marc-André Bombeck committed 2026-09-24 10:52:03 +02:00
1 parent c2005fd8f1
commit 2c4dec08f1
9 files changed
+123 -30

No files matched your search

@@ -43,14 +43,14 @@ vi.mock("@/lib/rate-limit", () => ({
}));
// Operator master flag = on by default; coach delegation keys off it.
// Partial mock: the rest of the module stays real.
vi.mock("@/lib/feature-flags", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/feature-flags")>();
return {
...actual,
getAssistantFlags: vi.fn().mockResolvedValue({ coach: true }),
};
});
// Partial mock: the rest of the module stays real, and both switch readers
// answer from the one the test drives.
vi.mock("@/lib/feature-flags", async (importOriginal) => ({
...(await importOriginal<typeof import("@/lib/feature-flags")>()),
...(
await import("@/__tests__/helpers/assistant-switches-mock")
).mockAssistantSwitches(vi.fn().mockResolvedValue({ coach: true })),
}));
vi.mock("@/lib/cache/invalidate", async (importOriginal) => {
const actual =
@@ -36,10 +36,12 @@ vi.mock("@/lib/modules/operator-availability", async (importOriginal) => {
>();
return { ...actual, getOperatorModuleAvailability: vi.fn() };
});
vi.mock("@/lib/feature-flags", async (importOriginal) => {
const actual = await importOriginal<typeof import("@/lib/feature-flags")>();
return { ...actual, getAssistantFlags: vi.fn() };
});
vi.mock("@/lib/feature-flags", async (importOriginal) => ({
...(await importOriginal<typeof import("@/lib/feature-flags")>()),
...(
await import("@/__tests__/helpers/assistant-switches-mock")
).mockAssistantSwitches(vi.fn()),
}));
// The heavy builder is stubbed: this test pins the GATE, not the aggregate
// (which has its own coverage). It returns one card per emitted kind.
vi.mock("@/lib/cache/server-cache", () => ({
@@ -48,9 +48,11 @@ vi.mock("@/lib/ai/server-provider-health", () => ({
readServerProviderHealth: vi.fn(),
}));
vi.mock("@/lib/feature-flags", () => ({
getAssistantFlags: vi.fn(),
}));
vi.mock("@/lib/feature-flags", async () =>
(
await import("@/__tests__/helpers/assistant-switches-mock")
).mockAssistantSwitches(vi.fn()),
);
vi.mock("@/lib/sharing/provider-work-authority", () => ({
providerWorkAuthorityForRecord: vi.fn(() => ({ origin: "owner" })),