ai-chatbot/tests/integration/agent-credential-health.test.ts

51 lines
2.8 KiB
TypeScript

import assert from "node:assert/strict";
import { randomUUID } from "node:crypto";
import { after, before, test } from "node:test";
import { eq } from "drizzle-orm";
import { db } from "@/lib/db";
import { aiModel, aiProvider } from "@/lib/db/schema";
import { recordAiModelHealthFailure, reviveAiModelsFromCooldown } from "@/lib/ai/runtime/model-health";
const database = new URL(process.env.POSTGRES_URL || "http://missing");
if (database.hostname !== "127.0.0.1" || !/_test$/.test(database.pathname)) throw new Error("Use a disposable local database");
const providerId = randomUUID();
const authModelId = randomUUID();
const transientModelId = randomUUID();
before(async () => {
await db.insert(aiProvider).values({ id: providerId, name: "Synthetic credential test", type: "custom", baseUrl: "https://invalid.example", apiKey: "synthetic-unused-test-value" });
await db.insert(aiModel).values([
{ id: authModelId, providerId, modelId: "auth-test", displayName: "Auth test" },
{ id: transientModelId, providerId, modelId: "transient-test", displayName: "Transient test" },
]);
});
after(async () => {
await db.delete(aiProvider).where(eq(aiProvider.id, providerId));
await db.$client.end({ timeout: 2 });
});
test("a confirmed authentication rejection disables the invalid model immediately", async () => {
await recordAiModelHealthFailure({ model: { id: authModelId }, error: Object.assign(new Error("Invalid Authentication"), { statusCode: 401 }) });
const [model] = await db.select().from(aiModel).where(eq(aiModel.id, authModelId));
assert.equal(model.isEnabled, false);
assert.equal(model.healthStatus, "auto_disabled");
assert.equal(model.healthConsecutiveFailures, 1);
assert.match(model.healthAutoDisabledReason || "", /^provider_authentication_failed/);
});
test("waiting for cooldown cannot reactivate a model with invalid credentials", async () => {
await db.update(aiModel).set({ healthAutoDisabledAt: new Date(Date.now() - 3 * 60 * 60_000) }).where(eq(aiModel.id, authModelId));
assert.deepEqual(await reviveAiModelsFromCooldown({ modelId: authModelId }), []);
const [model] = await db.select().from(aiModel).where(eq(aiModel.id, authModelId));
assert.equal(model.isEnabled, false);
});
test("temporary outages can still enter the normal observation pool after cooldown", async () => {
for (let i = 0; i < 3; i++) await recordAiModelHealthFailure({ model: { id: transientModelId }, error: Object.assign(new Error("Service unavailable"), { statusCode: 503 }) });
await db.update(aiModel).set({ healthAutoDisabledAt: new Date(Date.now() - 3 * 60 * 60_000) }).where(eq(aiModel.id, transientModelId));
const revived = await reviveAiModelsFromCooldown({ modelId: transientModelId });
assert.equal(revived.length, 1);
assert.equal(revived[0].isEnabled, true);
assert.equal(revived[0].healthStatus, "degraded");
});