mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-12 14:16:12 +00:00
test(ai): settle the ratchets after the empty-chain change
The resolver tests queue their fixture rows through one helper instead of an `as never` per row, the no-provider status case also proves the single-provider resolver is never asked, and the size and comment baselines take the lower numbers the change left behind.
This commit is contained in:
1 parent
17fd1a0906
commit
1d5236a326
6 files changed
+67
-65
No files matched your search
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Production files over 800 lines and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
|
||||
"count": 84,
|
||||
"total": 94047,
|
||||
"total": 93880,
|
||||
"entries": {
|
||||
"app/medications/page-client.tsx": 839,
|
||||
"components/charts/mood-chart.tsx": 1166,
|
||||
@@ -30,7 +30,7 @@
|
||||
"lib/ai/coach/types.ts": 1008,
|
||||
"lib/ai/codex-client.ts": 907,
|
||||
"lib/ai/prompts/insight-generator.ts": 1115,
|
||||
"lib/ai/provider.ts": 1628,
|
||||
"lib/ai/provider.ts": 1497,
|
||||
"lib/analytics/score/reader.ts": 998,
|
||||
"lib/analytics/sleep-night.ts": 1345,
|
||||
"lib/analytics/summaries-slice.ts": 1178,
|
||||
@@ -56,7 +56,7 @@
|
||||
"lib/import/health-connect/import.ts": 1689,
|
||||
"lib/import/unzip-export-xml.ts": 825,
|
||||
"lib/insights/comprehensive-aggregator.ts": 868,
|
||||
"lib/insights/comprehensive-generate.ts": 1674,
|
||||
"lib/insights/comprehensive-generate.ts": 1670,
|
||||
"lib/insights/correlation-channel-series.ts": 1339,
|
||||
"lib/insights/correlation-discovery.ts": 1240,
|
||||
"lib/insights/correlations.ts": 1026,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Production functions over 200 lines, keyed \"file :: name\", and their length. Maintained by src/__tests__/source-size-ratchet.test.ts.",
|
||||
"count": 344,
|
||||
"total": 111520,
|
||||
"count": 343,
|
||||
"total": 111236,
|
||||
"entries": {
|
||||
"app/achievements/page.tsx :: AchievementsPage": 206,
|
||||
"app/api/admin/backups/upload/route.ts :: apiHandler(arg)": 275,
|
||||
@@ -287,7 +287,7 @@
|
||||
"lib/import/labs-csv.ts :: importLabsCsv": 307,
|
||||
"lib/import/labs-csv.ts :: importLabsCsv > flush": 229,
|
||||
"lib/insights/comprehensive-aggregator.ts :: buildFromRollups": 217,
|
||||
"lib/insights/comprehensive-generate.ts :: generateComprehensiveInsight": 876,
|
||||
"lib/insights/comprehensive-generate.ts :: generateComprehensiveInsight": 872,
|
||||
"lib/insights/features.ts :: extractFeatures": 796,
|
||||
"lib/insights/mood-aggregates.ts :: fetchMoodAggregates": 246,
|
||||
"lib/insights/status-provider.ts :: runStatusCompletion": 215,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "as never casts in tests, per test file under src/. Maintained by src/__tests__/test-realism-ratchet.test.ts.",
|
||||
"count": 523,
|
||||
"total": 5475,
|
||||
"total": 5464,
|
||||
"entries": {
|
||||
"__tests__/api/auth/login/mfa-branch.test.ts": 10,
|
||||
"__tests__/api/auth/mfa/verify.test.ts": 9,
|
||||
@@ -196,7 +196,7 @@
|
||||
"app/api/medications/[id]/intake/import/__tests__/route.test.ts": 9,
|
||||
"app/api/medications/[id]/schedule-revisions/__tests__/route.test.ts": 24,
|
||||
"app/api/medications/compliance/__tests__/route.test.ts": 10,
|
||||
"app/api/medications/extract/__tests__/route.test.ts": 38,
|
||||
"app/api/medications/extract/__tests__/route.test.ts": 37,
|
||||
"app/api/medications/intake/__tests__/route.test.ts": 59,
|
||||
"app/api/medications/intake/bulk/__tests__/dose-arrival-scope.test.ts": 9,
|
||||
"app/api/medications/intake/bulk/__tests__/route.test.ts": 68,
|
||||
@@ -300,7 +300,7 @@
|
||||
"lib/ai/__tests__/provider-reasoning-effort.test.ts": 5,
|
||||
"lib/ai/__tests__/provider-response-timeout.test.ts": 2,
|
||||
"lib/ai/__tests__/provider.test.ts": 35,
|
||||
"lib/ai/__tests__/resolve-chain.test.ts": 12,
|
||||
"lib/ai/__tests__/resolve-chain.test.ts": 2,
|
||||
"lib/ai/__tests__/server-provider-health.test.ts": 1,
|
||||
"lib/ai/capabilities/__tests__/egress.test.ts": 1,
|
||||
"lib/ai/capabilities/__tests__/gate.test.ts": 1,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$comment": "Comment lines that open with a release version, per file under src/. Maintained by src/__tests__/version-stamp-ratchet.test.ts.",
|
||||
"count": 2892,
|
||||
"total": 9220,
|
||||
"total": 9217,
|
||||
"entries": {
|
||||
"__tests__/account-payload-consumer-guard.test.ts": 2,
|
||||
"__tests__/acting-account-boundary-guard.test.ts": 7,
|
||||
@@ -331,7 +331,7 @@
|
||||
"app/api/insights/ecg/[id]/route.ts": 2,
|
||||
"app/api/insights/ecg/route.ts": 1,
|
||||
"app/api/insights/feedback/route.ts": 2,
|
||||
"app/api/insights/generate/__tests__/route.test.ts": 12,
|
||||
"app/api/insights/generate/__tests__/route.test.ts": 11,
|
||||
"app/api/insights/glp1-plateau/route.ts": 2,
|
||||
"app/api/insights/glp1-timeline/route.ts": 4,
|
||||
"app/api/insights/health-status/route.ts": 3,
|
||||
|
||||
@@ -24,6 +24,17 @@ beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
/** Queue the user rows the resolver reads, in the order it reads them. */
|
||||
function userRows(...rows: Record<string, unknown>[]) {
|
||||
const findUnique = vi.mocked(prisma.user.findUnique);
|
||||
for (const row of rows) findUnique.mockResolvedValueOnce(row as never);
|
||||
}
|
||||
|
||||
/** The operator's settings row. */
|
||||
function operator(settings: Record<string, unknown>) {
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue(settings as never);
|
||||
}
|
||||
|
||||
describe("resolveProviderChain", () => {
|
||||
it("returns the default chain when user.aiProviderChain is null", async () => {
|
||||
vi.mocked(decryptCodexCreds).mockReturnValue({
|
||||
@@ -32,9 +43,9 @@ describe("resolveProviderChain", () => {
|
||||
accountId: "acct",
|
||||
expiresAt: new Date(Date.now() + 3600_000),
|
||||
});
|
||||
vi.mocked(prisma.user.findUnique)
|
||||
userRows(
|
||||
// user-level config read
|
||||
.mockResolvedValueOnce({
|
||||
{
|
||||
aiProvider: null,
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -42,19 +53,20 @@ describe("resolveProviderChain", () => {
|
||||
aiLocalKeyEncrypted: null,
|
||||
aiOpenaiKeyEncrypted: null,
|
||||
aiProviderChain: null,
|
||||
} as never)
|
||||
},
|
||||
// codex tokens read
|
||||
.mockResolvedValueOnce({
|
||||
{
|
||||
codexAccessTokenEncrypted: "enc-a",
|
||||
codexRefreshTokenEncrypted: "enc-r",
|
||||
codexTokenExpiresAt: new Date(Date.now() + 3600_000),
|
||||
codexConnectionStatus: "connected",
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
},
|
||||
);
|
||||
operator({
|
||||
adminAiKeyEncrypted: "enc-admin",
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
// Default chain trims out entries with no creds; codex (tokens
|
||||
@@ -69,7 +81,7 @@ describe("resolveProviderChain", () => {
|
||||
});
|
||||
|
||||
it("respects user-defined chain priority order", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValueOnce({
|
||||
userRows({
|
||||
aiProvider: null,
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -82,12 +94,12 @@ describe("resolveProviderChain", () => {
|
||||
{ providerType: "anthropic", priority: 2, enabled: true },
|
||||
{ providerType: "admin-openai", priority: 3, enabled: false },
|
||||
],
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
});
|
||||
operator({
|
||||
adminAiKeyEncrypted: "enc-admin",
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
const types = chain.map((c) => c.providerType);
|
||||
@@ -95,7 +107,7 @@ describe("resolveProviderChain", () => {
|
||||
});
|
||||
|
||||
it("filters out chain entries the user has not credentialed", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValueOnce({
|
||||
userRows({
|
||||
aiProvider: null,
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -106,12 +118,12 @@ describe("resolveProviderChain", () => {
|
||||
{ providerType: "anthropic", priority: 1, enabled: true },
|
||||
{ providerType: "openai", priority: 2, enabled: true },
|
||||
],
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
});
|
||||
operator({
|
||||
adminAiKeyEncrypted: null,
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
const types = chain.map((c) => c.providerType);
|
||||
@@ -119,7 +131,7 @@ describe("resolveProviderChain", () => {
|
||||
});
|
||||
|
||||
it("skips disabled entries even if the credential is present", async () => {
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValueOnce({
|
||||
userRows({
|
||||
aiProvider: null,
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -130,12 +142,12 @@ describe("resolveProviderChain", () => {
|
||||
{ providerType: "openai", priority: 1, enabled: false }, // disabled
|
||||
{ providerType: "admin-openai", priority: 2, enabled: true },
|
||||
],
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
});
|
||||
operator({
|
||||
adminAiKeyEncrypted: "enc-admin",
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
const types = chain.map((c) => c.providerType);
|
||||
@@ -143,8 +155,8 @@ describe("resolveProviderChain", () => {
|
||||
});
|
||||
|
||||
it("returns an empty array when nothing is configured at all", async () => {
|
||||
vi.mocked(prisma.user.findUnique)
|
||||
.mockResolvedValueOnce({
|
||||
userRows(
|
||||
{
|
||||
aiProvider: null,
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -152,18 +164,19 @@ describe("resolveProviderChain", () => {
|
||||
aiLocalKeyEncrypted: null,
|
||||
aiOpenaiKeyEncrypted: null,
|
||||
aiProviderChain: null,
|
||||
} as never)
|
||||
.mockResolvedValueOnce({
|
||||
},
|
||||
{
|
||||
codexAccessTokenEncrypted: null,
|
||||
codexRefreshTokenEncrypted: null,
|
||||
codexTokenExpiresAt: null,
|
||||
codexConnectionStatus: "disconnected",
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
},
|
||||
);
|
||||
operator({
|
||||
adminAiKeyEncrypted: null,
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
expect(chain).toEqual([]);
|
||||
@@ -173,7 +186,7 @@ describe("resolveProviderChain", () => {
|
||||
// Only the local model on, its address missing; the person's own key and
|
||||
// the operator's key are both switched off. Nothing may serve behind those
|
||||
// switches, however the record's single provider field reads.
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValueOnce({
|
||||
userRows({
|
||||
aiProvider: "OPENAI",
|
||||
aiModel: null,
|
||||
aiBaseUrl: null,
|
||||
@@ -185,12 +198,12 @@ describe("resolveProviderChain", () => {
|
||||
{ providerType: "openai", priority: 2, enabled: false },
|
||||
{ providerType: "admin-openai", priority: 3, enabled: false },
|
||||
],
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
});
|
||||
operator({
|
||||
adminAiKeyEncrypted: "enc-admin",
|
||||
adminAiModel: "gpt-4o-mini",
|
||||
adminAiBaseUrl: "https://api.openai.com/v1",
|
||||
} as never);
|
||||
});
|
||||
|
||||
expect(await resolveProviderChain("user-1")).toEqual([]);
|
||||
});
|
||||
@@ -208,26 +221,27 @@ describe("resolveProviderChain", () => {
|
||||
aiCompatModel: "llama-3",
|
||||
role: "USER",
|
||||
};
|
||||
vi.mocked(prisma.user.findUnique)
|
||||
.mockResolvedValueOnce({ ...gatewayRow, aiProviderChain: null } as never)
|
||||
.mockResolvedValueOnce({
|
||||
userRows(
|
||||
{ ...gatewayRow, aiProviderChain: null },
|
||||
{
|
||||
codexAccessTokenEncrypted: null,
|
||||
codexRefreshTokenEncrypted: null,
|
||||
codexTokenExpiresAt: null,
|
||||
codexConnectionStatus: "disconnected",
|
||||
} as never);
|
||||
vi.mocked(prisma.appSettings.findUnique).mockResolvedValue({
|
||||
},
|
||||
);
|
||||
operator({
|
||||
adminAiKeyEncrypted: null,
|
||||
} as never);
|
||||
});
|
||||
|
||||
const chain = await resolveProviderChain("user-1");
|
||||
expect(chain.map((c) => c.providerType)).toEqual(["openai-compatible"]);
|
||||
|
||||
// A saved chain without the gateway is the person's word.
|
||||
vi.mocked(prisma.user.findUnique).mockResolvedValueOnce({
|
||||
userRows({
|
||||
...gatewayRow,
|
||||
aiProviderChain: [{ providerType: "local", priority: 1, enabled: true }],
|
||||
} as never);
|
||||
});
|
||||
expect(await resolveProviderChain("user-1")).toEqual([]);
|
||||
});
|
||||
});
|
||||
@@ -167,22 +167,6 @@ describe("runStatusCompletion — capability at the wire", () => {
|
||||
expect(ledgerTotal).toBe(0);
|
||||
});
|
||||
|
||||
it("sends nothing on an empty chain, whatever the single-provider resolver holds", async () => {
|
||||
// The chain carries the person's provider switches; an empty one is no
|
||||
// provider, never a quiet hop to one they switched off.
|
||||
resolveProviderChain.mockResolvedValue([]);
|
||||
resolveProvider.mockResolvedValue({ type: "admin-key" });
|
||||
|
||||
const outcome = await runStatusCompletion(
|
||||
completionArgs({ capability: "statusText" }) as never,
|
||||
);
|
||||
|
||||
expect(outcome).toEqual({ kind: "none", reason: "no_provider" });
|
||||
expect(resolveProvider).not.toHaveBeenCalled();
|
||||
expect(runRawCompletionWithFallback).not.toHaveBeenCalled();
|
||||
expect(ledgerTotal).toBe(0);
|
||||
});
|
||||
|
||||
it("re-checks exactly the resolved chain and refuses before any budget or provider work", async () => {
|
||||
resolveProviderChain.mockResolvedValue([
|
||||
{ providerType: "anthropic", instance: {} },
|
||||
@@ -240,9 +224,13 @@ describe("runStatusCompletion — capability at the wire", () => {
|
||||
});
|
||||
|
||||
it("returns none with reason no_provider when the chain is empty", async () => {
|
||||
// The chain carries the person's provider switches; an empty one is no
|
||||
// provider, never a quiet hop to the single-provider resolver's pick.
|
||||
resolveProviderChain.mockResolvedValue([]);
|
||||
resolveProvider.mockResolvedValue({ type: "admin-key" });
|
||||
const outcome = await runStatusCompletion(completionArgs() as never);
|
||||
expect(outcome).toEqual({ kind: "none", reason: "no_provider" });
|
||||
expect(resolveProvider).not.toHaveBeenCalled();
|
||||
expect(runRawCompletionWithFallback).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user