Files
diegosouzapw--omniroute/tests/unit/batch-deletion-route-logic.test.ts
2026-07-13 13:39:12 +08:00

102 lines
4.0 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert";
// Tests for the business logic embedded in DELETE route handlers.
// These verify every code path without importing Next.js route modules
// (which pull in pino/thread-stream — broken on Node 26).
const TERMINAL = ["completed", "failed", "cancelled", "expired"];
function scopeCheck(
isSessionAuth: boolean,
recordApiKeyId: string | null | undefined,
apiKeyId: string | null
): boolean {
if (isSessionAuth) return true;
if (recordApiKeyId === null || recordApiKeyId === undefined) return apiKeyId !== null;
return recordApiKeyId === apiKeyId;
}
function canDeleteBatch(status: string): boolean {
return TERMINAL.includes(status);
}
test("scopeCheck — session auth always passes", () => {
assert.strictEqual(scopeCheck(true, "key-1", "key-1"), true);
assert.strictEqual(scopeCheck(true, "key-1", "different-key"), true);
assert.strictEqual(scopeCheck(true, null, null), true);
assert.strictEqual(scopeCheck(true, undefined, null), true);
});
test("scopeCheck — null record ApiKeyId requires an authenticated API key", () => {
assert.strictEqual(scopeCheck(false, null, null), false);
assert.strictEqual(scopeCheck(false, null, "any-key"), true);
assert.strictEqual(scopeCheck(false, undefined, null), false);
assert.strictEqual(scopeCheck(false, undefined, "any-key"), true);
});
test("scopeCheck — matching apiKeyId passes", () => {
assert.strictEqual(scopeCheck(false, "key-1", "key-1"), true);
});
test("scopeCheck — mismatched apiKeyId fails", () => {
assert.strictEqual(scopeCheck(false, "key-1", null), false);
assert.strictEqual(scopeCheck(false, "key-1", "key-2"), false);
});
test("batch deletion only allowed for terminal statuses", () => {
for (const s of ["completed", "failed", "cancelled", "expired"]) {
assert.strictEqual(canDeleteBatch(s), true, `${s} should be deletable`);
}
for (const s of ["validating", "in_progress", "finalizing", "cancelling"]) {
assert.strictEqual(canDeleteBatch(s), false, `${s} should NOT be deletable`);
}
});
test("delete completed auth — requires session or API key", () => {
// Simulates the check in delete-completed/route.ts:
// if (!scope.isSessionAuth && !scope.apiKeyId) → 401
function needsAuth(isSessionAuth: boolean, apiKeyId: string | null): boolean {
return !isSessionAuth && !apiKeyId;
}
assert.strictEqual(needsAuth(true, null), false, "session auth → OK");
assert.strictEqual(needsAuth(true, "key-1"), false, "session auth + key → OK");
assert.strictEqual(needsAuth(false, "key-1"), false, "API key → OK");
assert.strictEqual(needsAuth(false, null), true, "no auth → 401");
});
test("response JSON shape for single batch deletion", () => {
const id = "batch_test123";
const body = { id, object: "batch", deleted: true };
assert.strictEqual(body.id, id);
assert.strictEqual(body.object, "batch");
assert.strictEqual(body.deleted, true);
});
test("response JSON shape for delete-completed", () => {
const body = { deleted: true, deletedBatches: 3, deletedFiles: 5 };
assert.strictEqual(body.deleted, true);
assert.strictEqual(body.deletedBatches, 3);
assert.strictEqual(body.deletedFiles, 5);
});
test("response JSON shape for 404 error", () => {
const body = { error: { message: "Batch not found", type: "invalid_request_error" } };
assert.strictEqual(body.error.message, "Batch not found");
assert.strictEqual(body.error.type, "invalid_request_error");
});
test("response JSON shape for 409 error", () => {
const body = {
error: { message: "Only terminal batches can be deleted", type: "invalid_request_error" },
};
assert.strictEqual(body.error.message, "Only terminal batches can be deleted");
assert.strictEqual(body.error.type, "invalid_request_error");
});
test("response JSON shape for 401 error", () => {
const body = { error: { message: "Authentication required", type: "invalid_request_error" } };
assert.strictEqual(body.error.message, "Authentication required");
assert.strictEqual(body.error.type, "invalid_request_error");
});