test: cover runner token lifecycle

This commit is contained in:
2026-04-29 22:00:50 +00:00
parent 6ebc0d96bb
commit 0ca0d367d3
+58 -17
View File
@@ -1,5 +1,6 @@
import { assert, assertEquals, assertRejects } from "jsr:@std/assert@^1.0.0"; import { assert, assertEquals, assertRejects } from "jsr:@std/assert@^1.0.0";
import { RunnerAdmin } from "../../../uptime.link/ts_api/classes/runner-admin.ts";
import { RunnerCoordinator } from "../../../uptime.link/ts_api/classes/runner-coordinator.ts"; import { RunnerCoordinator } from "../../../uptime.link/ts_api/classes/runner-coordinator.ts";
import { RunnerFileStore } from "../../../uptime.link/ts_api/classes/runner-file-store.ts"; import { RunnerFileStore } from "../../../uptime.link/ts_api/classes/runner-file-store.ts";
import { import {
@@ -64,9 +65,15 @@ const main = async () => {
}, },
]; ];
const coordinator = new RunnerCoordinator({ const coordinator = new RunnerCoordinator();
runners: [{ runnerId, token: runnerToken, labels: ["scenario:basic"] }], const admin = new RunnerAdmin(coordinator);
const registration = admin.registerRunner({
runnerId,
token: runnerToken,
labels: ["scenario:basic"],
}); });
assertEquals(registration.runner.runnerId, runnerId);
assertEquals(registration.token, runnerToken);
const scheduler = new RunnerScheduler(coordinator, { now: () => 1000 }); const scheduler = new RunnerScheduler(coordinator, { now: () => 1000 });
const scheduleResult = scheduler.scheduleDueChecks( const scheduleResult = scheduler.scheduleDueChecks(
createRunnerSchedulesFromMonitors(monitors), createRunnerSchedulesFromMonitors(monitors),
@@ -132,6 +139,12 @@ const main = async () => {
pollIntervalMs: 1000, pollIntervalMs: 1000,
}); });
await assertRejects(() => unauthorizedRunner.runOnce(), Error, "401"); await assertRejects(() => unauthorizedRunner.runOnce(), Error, "401");
await assertRunnerTokenLifecycle(
admin,
coordinator,
coordinatorServer.url,
runner,
);
console.log(`[${scenarioName}] Passed`); console.log(`[${scenarioName}] Passed`);
} finally { } finally {
@@ -212,26 +225,54 @@ function assertMonitorStatusDerivation(
assertEquals(ingestor.getMonitorState("manual-degraded")?.status, "degraded"); assertEquals(ingestor.getMonitorState("manual-degraded")?.status, "degraded");
} }
async function assertRunnerTokenLifecycle(
adminArg: RunnerAdmin,
coordinatorArg: RunnerCoordinator,
instanceUrlArg: string,
staleRunnerArg: UptimeRunner,
): Promise<void> {
const rotated = adminArg.rotateRunnerToken(runnerId, "rotated-token");
assertEquals(rotated.token, "rotated-token");
assertEquals(adminArg.listRunners()[0].tokenPreview, "rotate...oken");
coordinatorArg.enqueueCheck({
id: "post-rotate-assumption",
type: "assumption",
assumedStatus: "ok",
metadata: {
monitorId: "post-rotate",
},
});
await assertRejects(() => staleRunnerArg.runOnce(), Error, "401");
const rotatedRunner = new UptimeRunner({
instanceUrl: instanceUrlArg,
runnerId,
token: rotated.token,
});
const rotatedResult = await rotatedRunner.runOnce();
assertEquals(rotatedResult.results.length, 1);
assertEquals(rotatedResult.results[0].status, "ok");
adminArg.disableRunner(runnerId);
assertEquals(adminArg.listRunners()[0].enabled, false);
await assertRejects(() => rotatedRunner.runOnce(), Error, "401");
adminArg.enableRunner(runnerId);
adminArg.setRunnerLabels(runnerId, ["scenario:basic", "role:external"]);
assertEquals(
adminArg.listRunners()[0].labels.includes("role:external"),
true,
);
}
async function assertSnapshotPersistence( async function assertSnapshotPersistence(
coordinatorArg: RunnerCoordinator, coordinatorArg: RunnerCoordinator,
targetUrlArg: string, targetUrlArg: string,
): Promise<void> { ): Promise<void> {
const snapshotPath = await Deno.makeTempFile(); const snapshotPath = await Deno.makeTempFile();
const store = new RunnerFileStore({ const store = RunnerFileStore.fromDenoPath(snapshotPath);
readTextFile: async () => {
try {
return await Deno.readTextFile(snapshotPath);
} catch (error) {
if (error instanceof Deno.errors.NotFound) {
return undefined;
}
throw error;
}
},
writeTextFile: async (contentArg) => {
await Deno.writeTextFile(snapshotPath, contentArg);
},
});
try { try {
await store.save(coordinatorArg.getSnapshot()); await store.save(coordinatorArg.getSnapshot());