import { describe, expect, test } from "bun:test";
import { HttpError } from "~/server/errors";
import { httpErrorAdapter } from "./http-error-adapter";
import { paymentDetail, upsellFromResponse, useUpsell } from "./upsell";
describe("402 payment required", () => {
test("the detail survives the server-to-client adapter", () => {
const error = new HttpError(402, "Usage limit reached for AI calls", {
code: "payment_required",
meter: "ai.calls",
reason: "plan_limit",
manageable: true,
account: { name: "acme", kind: "org" },
billingPath: "/orgs/acme/billing",
accountId: "internal-id",
});
const wire = httpErrorAdapter.toSerializable(error);
const back = httpErrorAdapter.fromSerializable(wire as never);
expect(paymentDetail(back)).toEqual({
message: "Usage limit reached for AI calls",
meter: "ai.calls",
reason: "plan_limit",
manageable: true,
account: { name: "acme", kind: "org" },
billingPath: "/orgs/acme/billing",
});
expect(JSON.stringify(wire)).not.toContain("internal-id");
});
test("other errors are not upsells", () => {
expect(paymentDetail(new HttpError(403, "nope"))).toBeNull();
expect(paymentDetail(new HttpError(402, "no detail"))).toBeNull();
expect(paymentDetail(new HttpError(402, "other", { meter: "x" }))).toBeNull();
expect(paymentDetail(new Error("plain"))).toBeNull();
});
test("plain JSON 402 responses open the upsell", () => {
expect(upsellFromResponse(413, { error: "too big" })).toBe(false);
expect(
upsellFromResponse(402, {