Files
HealthLog/src/lib/ai/reasoning/__tests__/controls.test.ts
T
Marc-André Bombeck 74dcc67ef7 feat(insights): let the usage level decide the background AI work
The level now sets, per record: the status-card regeneration gap after a
sync (frugal 24 h, standard the operator's gap, full 1 h) and after a hand
entry (frugal 6 h, otherwise 1 h); the status snapshot cap (10 000 compact
characters on frugal, 14 000 above); background reasoning for the briefing
and the period narratives (off on frugal); the daily briefing's form (a
short form on frugal that keeps every field and at most one
recommendation, the full contract on standard and full); and whether the
automatic document read may act (frugal reads on request only, the stored
switch is kept).

A deterministic simulation of a watch-synced day through the invalidator
counts 122 ingest-driven status generations on full (the v1.42 behaviour),
25 on standard and 2 on frugal, plus the nightly warm on each.

Refs #1217
2026-10-15 22:55:40 +02:00

135 lines
3.8 KiB
TypeScript

/**
* v1.41 — the Coach turn's level as the chat route resolves it: the stored
* preference against the operator's controls, read through the real loader
* against a stubbed settings row.
*/
import { beforeEach, describe, expect, it, vi } from "vitest";
const { row, user } = vi.hoisted(() => ({
row: { current: null as unknown, fail: false },
user: { level: null as string | null },
}));
vi.mock("@/lib/db", () => ({
prisma: {
appSettings: {
findUnique: vi.fn(async () => {
if (row.fail) throw new Error("db down");
return row.current;
}),
},
user: {
findUnique: vi.fn(async () => ({ aiUsageLevel: user.level })),
},
},
}));
vi.mock("@/lib/logging/context", () => ({
annotate: vi.fn(),
getEvent: () => undefined,
}));
import {
loadReasoningControls,
resolveCoachTurnReasoningLevel,
resolveJobReasoning,
} from "../controls";
beforeEach(() => {
row.current = null;
row.fail = false;
user.level = null;
});
describe("loadReasoningControls", () => {
it("reads an untouched instance as on and uncapped", async () => {
expect(await loadReasoningControls()).toEqual({
enabled: true,
maxEffort: "high",
});
});
it("fails closed when the row cannot be read", async () => {
row.fail = true;
expect((await loadReasoningControls()).enabled).toBe(false);
});
it("reads an unknown stored cap as no cap", async () => {
row.current = { aiReasoningEnabled: true, aiReasoningMaxEffort: "xhigh" };
expect((await loadReasoningControls()).maxEffort).toBe("high");
});
});
describe("resolveCoachTurnReasoningLevel", () => {
it("runs a never-chosen preference at medium", async () => {
expect(await resolveCoachTurnReasoningLevel(null)).toBe("medium");
expect(await resolveCoachTurnReasoningLevel({ tone: "warm" })).toBe(
"medium",
);
});
it("reads a malformed stored level as the default", async () => {
expect(await resolveCoachTurnReasoningLevel({ reasoning: "max" })).toBe(
"medium",
);
});
it("clamps the stored level to the operator's cap", async () => {
row.current = { aiReasoningEnabled: true, aiReasoningMaxEffort: "low" };
expect(await resolveCoachTurnReasoningLevel({ reasoning: "high" })).toBe(
"low",
);
});
it("is off when the operator switched reasoning off", async () => {
row.current = { aiReasoningEnabled: false, aiReasoningMaxEffort: "high" };
expect(await resolveCoachTurnReasoningLevel({ reasoning: "high" })).toBe(
"off",
);
});
it("is off when the controls cannot be read", async () => {
row.fail = true;
expect(await resolveCoachTurnReasoningLevel({ reasoning: "high" })).toBe(
"off",
);
});
});
// #1217 — background reasoning per AI usage level.
describe("resolveJobReasoning by usage level", () => {
const chain = [{ providerType: "openai" as const }];
it("asks for nothing on frugal", async () => {
user.level = "FRUGAL";
row.current = { aiUsageMaxLevel: "FULL", aiUsageDefaultLevel: "STANDARD" };
expect(
await resolveJobReasoning("daily_briefing", chain, "u1"),
).toBeUndefined();
});
it.each(["STANDARD", "FULL"])(
"keeps the job's level on %s",
async (level) => {
user.level = level;
row.current = {
aiUsageMaxLevel: "FULL",
aiUsageDefaultLevel: "STANDARD",
};
expect(await resolveJobReasoning("daily_briefing", chain, "u1")).toEqual({
effort: "medium",
summaries: false,
});
},
);
it("follows a frugal instance default for an account without a choice", async () => {
row.current = {
aiUsageMaxLevel: "STANDARD",
aiUsageDefaultLevel: "FRUGAL",
};
expect(
await resolveJobReasoning("daily_briefing", chain, "u1"),
).toBeUndefined();
});
});