Files
OmniRoute/tests/unit/proxy-registry-status-schema.test.ts
Dizzle aaf0777a98 fix(proxies): keep the stored status when a write does not send one (#13577)
A write that does not send a proxy `status` (subscription refresh, bulk import, PATCH) no longer turns a disabled or dead proxy back on; the stored status is kept. Real DB-backed tests through `upsertProxy`, the subscription sync against a local feed and `handleProxyUpdate`.

Validated first on the combined board of all 38 PRs of this batch (10 merged as-is, 28 after the maintainer rework) on top of release/v3.8.51 c0f92ec: typecheck:core, check:open-sse-typecheck and check:dashboard-typecheck clean; ESLint clean on every changed file; file-size (rebaselined for the combined growth), complexity, cognitive-complexity, changelog-integrity, docs-counts, docs-sync, migration-numbering and i18n new-key gates green; 735 focused node:test cases with the only batch-caused failure (a flag-count assertion) fixed. Then re-validated alone on the fresh release tip right before this merge: ESLint on the changed files, typecheck:core, check:open-sse-typecheck, the file-size/complexity/changelog gates and this PR's own tests.

Thanks @maxmad64bis!
2026-09-15 11:05:01 -03:00

96 lines
3.5 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
// zod 4 applies .default() even under .partial(), so a default status on the shared
// proxy field schema rewrote the stored status on every update or import that simply
// omitted it. These tests pin "a status is written only when the caller sends one".
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-proxy-status-schema-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = "test-secret";
const core = await import("../../src/lib/db/core.ts");
const proxiesDb = await import("../../src/lib/db/proxies.ts");
const schemas = await import("../../src/shared/validation/schemas/proxy.ts");
const statuses = await import("../../src/shared/constants/proxyRegistryStatus.ts");
const { handleProxyUpdate } = await import("../../src/lib/api/proxyRegistryRouteHandlers.ts");
function resetStorage() {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
fs.mkdirSync(TEST_DATA_DIR, { recursive: true });
}
test.after(() => {
core.resetDbInstance();
fs.rmSync(TEST_DATA_DIR, { recursive: true, force: true, maxRetries: 5, retryDelay: 100 });
});
test("update schema does not invent a status the client did not send", () => {
const parsed = schemas.updateProxyRegistrySchema.parse({ id: "row-1", name: "renamed" });
assert.equal("status" in parsed, false);
});
test("bulk import schema does not invent a status the client did not send", () => {
const parsed = schemas.bulkImportProxiesSchema.parse({
items: [{ name: "n", host: "proxy.example.com", port: 8080 }],
});
assert.equal("status" in parsed.items[0], false);
});
test("an explicit status is still validated against the enumeration", () => {
const bad = schemas.bulkImportProxiesSchema.safeParse({
items: [{ name: "n", host: "proxy.example.com", port: 8080, status: "bogus" }],
});
assert.equal(bad.success, false);
const good = schemas.updateProxyRegistrySchema.parse({ id: "row-2", status: "inactive" });
assert.equal(good.status, "inactive");
});
test("isProxyRegistryStatus accepts only the enumerated statuses", () => {
for (const value of ["active", "inactive", "dead"]) {
assert.equal(statuses.isProxyRegistryStatus(value), true, value);
}
for (const value of ["", "error", "ACTIVE", undefined, null, 1]) {
assert.equal(statuses.isProxyRegistryStatus(value), false, String(value));
}
});
test("renaming a dead proxy through the update handler keeps it dead", async () => {
resetStorage();
const created = await proxiesDb.createProxy({
name: "before",
type: "http",
host: "10.1.0.1",
port: 8080,
});
await proxiesDb.updateProxy(created.id, { status: "dead" });
const response = await handleProxyUpdate(
new Request("http://localhost/api/v1/management/proxies", {
method: "PATCH",
headers: { "content-type": "application/json" },
body: JSON.stringify({ id: created.id, name: "after" }),
})
);
assert.equal(response.status, 200);
const row = await proxiesDb.getProxyById(created.id);
assert.equal(row?.name, "after");
assert.equal(row?.status, "dead");
});
test("creating a proxy without a status still creates it active", async () => {
resetStorage();
const created = await proxiesDb.createProxy({
name: "fresh",
type: "http",
host: "10.1.0.2",
port: 8080,
});
assert.equal((await proxiesDb.getProxyById(created.id))?.status, "active");
});