Files
MikrotikManager/backend/src/routes/scheduler.test.ts
T

30 lines
3.0 KiB
TypeScript

import assert from "node:assert/strict"
import { test } from "node:test"
import Fastify from "fastify"
import { createSchedulerRoutes } from "./scheduler.js"
import type { SchedulerRunReceipt } from "../services/scheduler-recorded-run.js"
const receipt: SchedulerRunReceipt = { status: "ok", error: null, result: { v: 1, job: "backups", sampledAt: "2026-10-01T00:00:00Z", due: true, created: 1, failures: 0, pruned: 0 } }
async function setup(patch: Partial<Parameters<typeof createSchedulerRoutes>[0]> = {}) {
const app = Fastify(); let calls = 0
await app.register(createSchedulerRoutes({ status: async () => ({ jobs: [] }), runs: async () => ({ runs: [] }), refresh: async () => {}, run: async () => { calls++; return receipt }, ...patch }))
return { app, calls: () => calls }
}
test("manual partial receipt is not ok and retains confirmed backup result", async () => {
const { app } = await setup({ run: async () => ({ ...receipt, status: "error", error: "S3 unavailable" }) })
try { const response = await app.inject({ method: "POST", url: "/scheduler/jobs/backups/run-now" }); assert.equal(response.statusCode, 200); assert.equal(response.json().ok, false); assert.equal(response.json().result.result.created, 1); assert.equal(response.json().result.error, "S3 unavailable") } finally { await app.close() }
})
test("status refresh failure never relabels confirmed execution or runs the job twice", async () => {
const { app, calls } = await setup({ status: async () => { throw Error("status unavailable") } })
try { const response = await app.inject({ method: "POST", url: "/scheduler/jobs/backups/run-now" }); assert.equal(response.statusCode, 200); assert.equal(response.json().ok, true); assert.equal(response.json().status, null); assert.equal(response.json().statusError, "status unavailable"); assert.equal(response.json().result.result.created, 1); assert.equal(calls(), 1) } finally { await app.close() }
})
test("invalid key and running conflict preserve their existing HTTP contracts", async () => {
const { app, calls } = await setup()
try { assert.equal((await app.inject({ method: "POST", url: "/scheduler/jobs/invalid/run-now" })).statusCode, 400); assert.equal(calls(), 0) } finally { await app.close() }
const conflict = await setup({ run: async () => { throw Object.assign(Error("running"), { statusCode: 409 }) } })
try { assert.equal((await conflict.app.inject({ method: "POST", url: "/scheduler/jobs/backups/run-now" })).statusCode, 409) } finally { await conflict.app.close() }
})
test("collector or journal rejection remains explicit without a fabricated receipt", async () => {
const { app } = await setup({ run: async () => { throw Error("journal not confirmed; retry may repeat operations") } })
try { const response = await app.inject({ method: "POST", url: "/scheduler/jobs/backups/run-now" }); assert.equal(response.statusCode, 500); assert.match(response.json().error, /retry may repeat/); assert.equal(response.json().result, undefined) } finally { await app.close() }
})