48 lines · 2.0 KB
import { describe, expect, mock, test } from "bun:test";
import { Database } from "bun:sqlite";

const notified: string[] = [];
mock.module("../repos", () => ({ loadRepoById: async () => null }));
mock.module("./deliver", () => ({
  notifyQuietly: async (input: { title: string }) => void notified.push(input.title),
}));
mock.module("./recipients", () => ({ accountAdmins: async () => ["u"], repoAdmins: async () => ["u"] }));

const { ACCOUNT_SCHEMA } = await import("../scoped-db/schema.gen");
const { ACCOUNT_SCHEMA_EXTRA } = await import("../scoped-db/schema-extra");
const { nextRunnerOffline, reportOfflineRunners } = await import("./runners");

const MINUTE = 60_000;
const NOW = 1_800_000_000_000;

function store() {
  const db = new Database(":memory:");
  for (const statement of [...ACCOUNT_SCHEMA, ...ACCOUNT_SCHEMA_EXTRA]) db.run(statement);
  const sql = {
    exec: (query: string, ...params: Array<string | number | null>) => {
      const rows = db.query(query).all(...params) as never[];
      return { toArray: () => rows };
    },
  };
  return { db, sql };
}

describe("offline runners", () => {
  test("each outage is reported once, and checking in again clears it", async () => {
    const { db, sql } = store();
    db.query(
      "INSERT INTO runners (id, repo_id, owner_id, name, labels, token_hash, created_at, last_seen_at) VALUES ('r', NULL, 'org', 'box', '[]', 'h', 0, ?)",
    ).run(NOW - 2 * MINUTE);
    expect(nextRunnerOffline(sql, NOW)).toBe(NOW + 8 * MINUTE);
    await reportOfflineRunners(sql, NOW + 7 * MINUTE);
    expect(notified).toEqual([]);
    await reportOfflineRunners(sql, NOW + 11 * MINUTE);
    await reportOfflineRunners(sql, NOW + 12 * MINUTE);