import { beforeEach, describe, expect, mock, test } from "bun:test"; import { fakeD1 } from "../metering/fake-d1.test-helper"; import { scopedTestState } from "./scoped.test-helper"; let d1 = fakeD1(); const scoped = scopedTestState(); scoped.install(); const SCOPE: Record = { s1: "r1", s2: "site:s2" }; function addUsage(site: string, day: string, account: string | null, requests: number, cpu: number): void { scoped .repo(SCOPE[site] ?? site) .query("INSERT INTO site_usage (site_id, day, account_id, requests, cpu_ms, bytes_out) VALUES (?, ?, ?, ?, ?, 0)") .run(site, day, account, requests, cpu); } const kv = new Map(); const notified: { title: string; recipients: (string | null | undefined)[] }[] = []; const fakeEnv: Record = { get DB() { return d1.binding; }, KV: { get: async (key: string) => kv.get(key) ?? null, put: async (key: string, value: string) => void kv.set(key, value), delete: async (key: string) => void kv.delete(key), }, METERING_PLANS: "", PUBLIC_URL: "https://forge.test", SITES_DAILY_CPU_MS_LIMIT: "0", SITES_DAILY_REQUEST_LIMIT: "0", }; mock.module("cloudflare:workers", () => ({ env: fakeEnv, waitUntil: () => undefined })); mock.module("../notify/deliver", () => ({ notifyUsers: async (input: { title: string; recipients: (string | null | undefined)[] }) => void notified.push(input), })); const platformSites = new Map(); const actualCounters = await import("../counters"); mock.module("../counters", () => ({ ...actualCounters, counterTotals: async (kind: string, _scope: string, key: string) => kind === "platform-sites" ? (platformSites.get(key) ?? [0, 0, 0]) : actualCounters.counterTotals(kind as never, _scope, key), })); const { checkGlobalCeiling, enforceSameDayCap, globalCeilingHit, hostingUsage } = await import("./caps"); const { ownerHostingAllowed, rollupScopeSiteUsage } = await import("./metering"); const { siteCapKey } = await import("../metering/quota"); const { resumeHosting, suspendHosting } = await import("./suspend"); const NOW = Date.parse("2026-10-12T12:00:00Z"); const TODAY = "2026-10-12"; beforeEach(() => { d1 = fakeD1(); kv.clear(); notified.length = 0; fakeEnv.SITES_DAILY_CPU_MS_LIMIT = "0"; fakeEnv.SITES_DAILY_REQUEST_LIMIT = "0"; scoped.reset(); scoped.site({ id: "s1", repo_id: "r1", owner_id: "u1" }); scoped.site({ id: "s2", owner_id: "u1" }); d1.db.run(` INSERT INTO accounts (id, name, kind) VALUES ('u1', 'alice', 'user'), ('admin', 'root', 'user'); INSERT INTO users (account_id, is_admin) VALUES ('admin', 1); INSERT INTO repos VALUES ('r1', 'u1', 'docs'); `); }); describe("same-day hosting caps", () => { test("today's flushed counters stop an owner over a hard cap without waiting for the rollup", async () => { addUsage("s1", TODAY, "u1", 99000, 0); expect(await enforceSameDayCap("u1", NOW)).toBe(false); addUsage("s2", TODAY, "u1", 1000, 0); expect(await enforceSameDayCap("u1", NOW)).toBe(true); expect(kv.get(siteCapKey("u1", "2026-10"))).toBe("compute.site_requests"); }); test("usage already rolled up is not counted twice", async () => { addUsage("s1", "2026-10-11", "u1", 60000, 0); await rollupScopeSiteUsage(SCOPE.s1 ?? "s1", NOW); const usage = await hostingUsage("u1", NOW); expect(usage["compute.site_requests"]).toBe(60000); }); test("an admin suspension stops hosting until resumed, independently of caps", async () => { expect(await ownerHostingAllowed("u1")).toBe(true); await suspendHosting("ALICE"); expect(await ownerHostingAllowed("u1")).toBe(false); await resumeHosting("u1"); expect(await ownerHostingAllowed("u1")).toBe(true); }); }); describe("global daily ceiling", () => { test("is off when both limits are 0", async () => { addUsage("s1", TODAY, "u1", 10, 10); expect(await checkGlobalCeiling(NOW)).toBe(false); expect(await globalCeilingHit(NOW)).toBe(false); }); test("trips once summed CPU crosses the limit and notifies admins once", async () => { fakeEnv.SITES_DAILY_CPU_MS_LIMIT = "1000"; // The platform total every site counter forwards its flushed deltas to (counters-forward.ts). platformSites.set(TODAY, [2, 900, 0]); expect(await checkGlobalCeiling(NOW)).toBe(false); platformSites.set(TODAY, [2, 1000, 0]); expect(await checkGlobalCeiling(NOW)).toBe(true); expect(await checkGlobalCeiling(NOW)).toBe(true); expect(await globalCeilingHit(NOW)).toBe(true); expect(notified).toHaveLength(1); expect(notified[0].recipients).toEqual(["admin"]); }); });