From 0ca0d367d34d159884a3ca14046d817d22235603 Mon Sep 17 00:00:00 2001 From: Juergen Kunz Date: Wed, 29 Apr 2026 22:00:50 +0000 Subject: [PATCH] test: cover runner token lifecycle --- scenarios/uptimerunner-basic/scenario.ts | 75 ++++++++++++++++++------ 1 file changed, 58 insertions(+), 17 deletions(-) diff --git a/scenarios/uptimerunner-basic/scenario.ts b/scenarios/uptimerunner-basic/scenario.ts index 0609b7d..24ac575 100644 --- a/scenarios/uptimerunner-basic/scenario.ts +++ b/scenarios/uptimerunner-basic/scenario.ts @@ -1,5 +1,6 @@ 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 { RunnerFileStore } from "../../../uptime.link/ts_api/classes/runner-file-store.ts"; import { @@ -64,9 +65,15 @@ const main = async () => { }, ]; - const coordinator = new RunnerCoordinator({ - runners: [{ runnerId, token: runnerToken, labels: ["scenario:basic"] }], + const coordinator = new RunnerCoordinator(); + 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 scheduleResult = scheduler.scheduleDueChecks( createRunnerSchedulesFromMonitors(monitors), @@ -132,6 +139,12 @@ const main = async () => { pollIntervalMs: 1000, }); await assertRejects(() => unauthorizedRunner.runOnce(), Error, "401"); + await assertRunnerTokenLifecycle( + admin, + coordinator, + coordinatorServer.url, + runner, + ); console.log(`[${scenarioName}] Passed`); } finally { @@ -212,26 +225,54 @@ function assertMonitorStatusDerivation( assertEquals(ingestor.getMonitorState("manual-degraded")?.status, "degraded"); } +async function assertRunnerTokenLifecycle( + adminArg: RunnerAdmin, + coordinatorArg: RunnerCoordinator, + instanceUrlArg: string, + staleRunnerArg: UptimeRunner, +): Promise { + 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( coordinatorArg: RunnerCoordinator, targetUrlArg: string, ): Promise { const snapshotPath = await Deno.makeTempFile(); - const store = new RunnerFileStore({ - 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); - }, - }); + const store = RunnerFileStore.fromDenoPath(snapshotPath); try { await store.save(coordinatorArg.getSnapshot());