import { beforeEach, describe, expect, mock, test } from "bun:test"; import { fakeD1 } from "../metering/fake-d1.test-helper"; import { memoryScopes } from "../scoped-db/testing"; let d1 = fakeD1(); let scopes = memoryScopes(); const fakeEnv: Record = { get DB() { return d1.binding; }, KV: { get: async () => null, put: async () => undefined, delete: async () => undefined }, METERING_PLANS: "", PUBLIC_URL: "https://forge.test", }; mock.module("cloudflare:workers", () => ({ DurableObject: class {}, env: fakeEnv, waitUntil: () => undefined })); mock.module("../scoped-db", () => ({ repoDb: (id: string) => scopes.repoDb(id), accountDb: (id: string) => scopes.accountDb(id), stmt: scopes.module.stmt, })); const { holdJobMinutes, releaseJobHold } = await import("./job-hold"); const { setPlan } = await import("../metering/quota"); function reserved(meter: string): number { const row = scopes .raw("account", "u1") .query("SELECT SUM(reserved) AS reserved FROM usage_periods WHERE meter = ?") .get(meter) as { reserved: number | null; } | null; return row?.reserved ?? 0; } beforeEach(async () => { d1 = fakeD1(); scopes = memoryScopes(); d1.db.exec("INSERT INTO accounts (id, name, kind) VALUES ('u1', 'alice', 'user');"); await setPlan("u1", "free", "billing"); }); describe("holdJobMinutes", () => { test("holds the job timeout on its runner-size meter and releases it", async () => { const job = { jobId: "j1", ownerId: "u1", runsOn: ["ubuntu-latest"], timeoutMinutes: 10 }; expect(await holdJobMinutes(job)).toBe(true); expect(reserved("compute.runner")).toBe(600); await releaseJobHold("j1", "u1"); expect(reserved("compute.runner")).toBe(0); }); test("large runners respect their own hard cap instead of the standard meter", async () => { const job = { jobId: "j2", ownerId: "u1", runsOn: ["ubuntu-latest-8-cores"], timeoutMinutes: 10 }; expect(await holdJobMinutes(job)).toBe(false); expect(reserved("compute.runner")).toBe(0); }); test("a timeout larger than the remaining allowance is refused up front", async () => { const job = { jobId: "j3", ownerId: "u1", runsOn: ["ubuntu-latest"], timeoutMinutes: 6 * 60 }; expect(await holdJobMinutes(job)).toBe(false); }); });