CD / update-wiki (push) Successful in 8s
quality / commitlint (push) Skipped
quality / changes (push) Successful in 5s
quality / docker-check (push) Skipped
quality / web (push) Successful in 54s
quality / api (push) Successful in 46s
CD / quality (push) Successful in 1m49s
CD / publish (push) Successful in 1m40s
Несколько источников проб сразу и правило агрегации на сервисе вместо XOR Local/Cloudflare. Co-authored-by: Cursor <[email protected]>
215 lines
6.3 KiB
TypeScript
215 lines
6.3 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { buildApp } from "../src/app.js";
|
|
import { loadConfig } from "../src/config.js";
|
|
|
|
async function authHeaders(app: Awaited<ReturnType<typeof buildApp>>) {
|
|
const res = await app.inject({
|
|
method: "POST",
|
|
url: "/api/v1/auth/login",
|
|
payload: { username: "admin", password: "admin" },
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const { token } = res.json() as { token: string };
|
|
return { authorization: `Bearer ${token}` };
|
|
}
|
|
|
|
describe("settings health engine", () => {
|
|
it("GET /api/v1/settings returns env fallbacks for health fields", async () => {
|
|
const app = await buildApp({
|
|
config: {
|
|
...loadConfig(),
|
|
staticDir: null,
|
|
healthCheckCron: "*/30 * * * * *",
|
|
healthDegradedFailures: 3,
|
|
healthDownFailures: 4,
|
|
healthLatencyWarnMs: 1500,
|
|
healthSuccessRecoveries: 5,
|
|
},
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const res = await app.inject({
|
|
method: "GET",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = res.json() as {
|
|
healthCheckCron: string;
|
|
healthDegradedFailures: number;
|
|
healthDownFailures: number;
|
|
healthLatencyWarnMs: number;
|
|
healthSuccessRecoveries: number;
|
|
healthWorkerStatus?: string;
|
|
};
|
|
expect(body.healthCheckCron).toBe("*/30 * * * * *");
|
|
expect(body.healthDegradedFailures).toBe(3);
|
|
expect(body.healthDownFailures).toBe(4);
|
|
expect(body.healthLatencyWarnMs).toBe(1500);
|
|
expect(body.healthSuccessRecoveries).toBe(5);
|
|
expect(body.healthWorkerStatus).toBe("missing");
|
|
await app.close();
|
|
});
|
|
|
|
it("PATCH persists health engine settings", async () => {
|
|
const app = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const res = await app.inject({
|
|
method: "PATCH",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
payload: {
|
|
healthCheckCron: "0 */5 * * * *",
|
|
healthDegradedFailures: 2,
|
|
healthDownFailures: 4,
|
|
healthLatencyWarnMs: 800,
|
|
healthSuccessRecoveries: 3,
|
|
},
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = res.json() as {
|
|
healthCheckCron: string;
|
|
healthDegradedFailures: number;
|
|
healthDownFailures: number;
|
|
healthLatencyWarnMs: number;
|
|
healthSuccessRecoveries: number;
|
|
};
|
|
expect(body.healthCheckCron).toBe("0 */5 * * * *");
|
|
expect(body.healthDegradedFailures).toBe(2);
|
|
expect(body.healthDownFailures).toBe(4);
|
|
expect(body.healthLatencyWarnMs).toBe(800);
|
|
expect(body.healthSuccessRecoveries).toBe(3);
|
|
|
|
const again = await app.inject({
|
|
method: "GET",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
});
|
|
expect(again.json()).toMatchObject({
|
|
healthCheckCron: "0 */5 * * * *",
|
|
healthDegradedFailures: 2,
|
|
healthDownFailures: 4,
|
|
healthLatencyWarnMs: 800,
|
|
healthSuccessRecoveries: 3,
|
|
});
|
|
await app.close();
|
|
});
|
|
|
|
it("PATCH rejects invalid cron", async () => {
|
|
const app = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const res = await app.inject({
|
|
method: "PATCH",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
payload: { healthCheckCron: "not-a-cron" },
|
|
});
|
|
expect(res.statusCode).toBe(400);
|
|
expect(res.json()).toMatchObject({
|
|
error: { code: "VALIDATION_ERROR" },
|
|
});
|
|
await app.close();
|
|
});
|
|
|
|
it("PATCH rejects down < degraded", async () => {
|
|
const app = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const res = await app.inject({
|
|
method: "PATCH",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
payload: {
|
|
healthDegradedFailures: 5,
|
|
healthDownFailures: 2,
|
|
},
|
|
});
|
|
expect(res.statusCode).toBe(400);
|
|
await app.close();
|
|
});
|
|
|
|
it("PATCH worker URL; token is not returned in GET", async () => {
|
|
const app = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const res = await app.inject({
|
|
method: "PATCH",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
payload: {
|
|
healthWorkerUrl: "https://cfdm-health-probe.example.workers.dev",
|
|
healthWorkerToken: "super-secret",
|
|
},
|
|
});
|
|
expect(res.statusCode).toBe(200);
|
|
const body = res.json() as {
|
|
healthWorkerUrl: string;
|
|
healthWorkerTokenSet: boolean;
|
|
healthWorkerToken?: string;
|
|
};
|
|
expect(body.healthWorkerUrl).toBe(
|
|
"https://cfdm-health-probe.example.workers.dev",
|
|
);
|
|
expect(body.healthWorkerTokenSet).toBe(true);
|
|
expect(body.healthWorkerToken).toBeUndefined();
|
|
await app.close();
|
|
});
|
|
|
|
it("GET exposes Globalping flags without the token; PATCH persists locations/limit", async () => {
|
|
const app = await buildApp({
|
|
config: { ...loadConfig(), staticDir: null },
|
|
memory: true,
|
|
});
|
|
const headers = await authHeaders(app);
|
|
const initial = await app.inject({
|
|
method: "GET",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
});
|
|
expect(initial.statusCode).toBe(200);
|
|
const before = initial.json() as {
|
|
globalpingTokenSet?: boolean;
|
|
globalpingLocations?: string;
|
|
globalpingLimit?: number;
|
|
globalpingToken?: string;
|
|
};
|
|
expect(before.globalpingTokenSet).toBe(false);
|
|
expect(before.globalpingLocations).toBe("World");
|
|
expect(before.globalpingLimit).toBe(3);
|
|
expect(before.globalpingToken).toBeUndefined();
|
|
|
|
const patched = await app.inject({
|
|
method: "PATCH",
|
|
url: "/api/v1/settings",
|
|
headers,
|
|
payload: {
|
|
globalpingToken: "gp_secret",
|
|
globalpingLocations: "EU,US",
|
|
globalpingLimit: 5,
|
|
},
|
|
});
|
|
expect(patched.statusCode).toBe(200);
|
|
const body = patched.json() as {
|
|
globalpingTokenSet: boolean;
|
|
globalpingLocations: string;
|
|
globalpingLimit: number;
|
|
globalpingToken?: string;
|
|
};
|
|
expect(body.globalpingTokenSet).toBe(true);
|
|
expect(body.globalpingLocations).toBe("EU,US");
|
|
expect(body.globalpingLimit).toBe(5);
|
|
expect(body.globalpingToken).toBeUndefined();
|
|
await app.close();
|
|
});
|
|
});
|