85 lines · 3.6 KB
import { beforeEach, describe, expect, mock, test } from "bun:test";
import { fakeD1 } from "./fake-d1.test-helper";
import { memoryScopes } from "../scoped-db/testing";

let d1 = fakeD1();
const fakeEnv: Record<string, unknown> = {
  get DB() {
    return d1.binding;
  },
  KV: { get: async () => null, put: async () => undefined, delete: async () => undefined },
  METERING_PLANS: JSON.stringify({ free: { hardCaps: { "ai.calls": 0 } } }),
  PUBLIC_URL: "https://forge.test",
};
mock.module("cloudflare:workers", () => ({ env: fakeEnv, waitUntil: () => undefined }));
let scopes = memoryScopes();
mock.module("../scoped-db", () => ({
  repoDb: (id: string) => scopes.repoDb(id),
  accountDb: (id: string) => scopes.accountDb(id),
  stmt: scopes.module.stmt,
}));
const accountSql = (id: string) => scopes.raw("account", id);

const { assertWithinQuota, planBlockReason, QuotaExceededError, setPlan, setSpendingLimit } = await import("./quota");
const { withBillingContext } = await import("./access");
const { HttpError } = await import("../errors");

beforeEach(() => {
  d1 = fakeD1();
  scopes = memoryScopes();
  d1.db
    .exec(`INSERT INTO accounts (id, name, kind) VALUES ('u1', 'alice', 'user'), ('u2', 'bob', 'user'), ('o1', 'acme', 'org');
    INSERT INTO org_members VALUES ('o1', 'u1', 'owner', 0), ('o1', 'u2', 'member', 0);`);
});

describe("out-of-budget errors", () => {
  test("a hard cap is a 402 naming the meter, reason and account", async () => {
    const error = await assertWithinQuota("o1", "ai.calls", 1).catch((caught: unknown) => caught);
    expect(error).toBeInstanceOf(QuotaExceededError);
    expect(error).not.toBeInstanceOf(HttpError);
    const http = (error as InstanceType<typeof QuotaExceededError>).toHttpError();