import { describe, expect, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Schema from "effect/Schema";import * as TestClock from "effect/testing/TestClock";import { HttpClient, HttpClientResponse } from "effect/unstable/http"; import { creditRedeemRequestId, makeCliproxyApi } from "./cliproxyApi.ts"; const config = { kind: "cliproxy", url: "http://hub.test:8317", managementKey: "management-secret", enabled: true,} as const;const accounts = [ { id: "first.json", auth_index: "a", provider: "codex", email: "first@example.com", id_token: { chatgpt_account_id: "account-a" }, }, { id: "second.json", auth_index: "b", provider: "codex", email: "second@example.com", id_token: { chatgpt_account_id: "account-b" }, },];const credit = (id: string, expires_at = "2099-01-01T00:00:00Z") => ({ id, expires_at, status: "available", reset_type: "codex_rate_limits",});const RequestBody = Schema.Struct({ auth_index: Schema.String, method: Schema.optional(Schema.String), url: Schema.optional(Schema.String), header: Schema.optional(Schema.Record(Schema.String, Schema.String)), data: Schema.optional(Schema.String),});type RequestBody = typeof RequestBody.Type;const decodeRequest = Schema.decodeUnknownSync(Schema.fromJsonString(RequestBody));const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown)); function fixture( options: { accounts?: Array<(typeof accounts)[number] & { disabled?: boolean }>; upstream?: (request: RequestBody) => { status: number; body: unknown }; cooldownStatus?: number; } = {},) { const requests: Array<{ path: string; body?: RequestBody }> = []; const http = HttpClient.make((request) => Effect.sync(() => { expect(request.headers.authorization).toBe("Bearer management-secret"); const path = new URL(request.url).pathname; const body = request.body._tag === "Uint8Array" ? decodeRequest(new TextDecoder().decode(request.body.body)) : undefined; requests.push({ path, ...(body ? { body } : {}) }); if (path.endsWith("/auth-files")) return HttpClientResponse.fromWeb( request, Response.json({ files: options.accounts ?? accounts }), ); if (path.endsWith("/reset-quota")) return HttpClientResponse.fromWeb( request, Response.json({}, { status: options.cooldownStatus ?? 200 }), ); expect(path).toBe("/v0/management/api-call"); expect(body?.header?.Authorization).toBe("Bearer $TOKEN$"); const upstream = options.upstream?.(body!) ?? { status: 200, body: body?.url?.endsWith("/consume") ? { code: "reset" } : body?.url?.endsWith("/rate-limit-reset-credits") ? { credits: [ credit("later", "2099-02-01T00:00:00Z"), credit("first"), credit("expired", "2000-01-01T00:00:00Z"), { ...credit("used"), status: "redeemed" }, ], } : { plan_type: "pro", rate_limit: { secondary_window: { used_percent: 78, reset_at: 4070908800, limit_window_seconds: 604800, }, }, }, }; return HttpClientResponse.fromWeb( request, Response.json({ status_code: upstream.status, body: encodeJson(upstream.body) }), ); }), ); return { requests, api: makeCliproxyApi.pipe(Effect.provideService(HttpClient.HttpClient, http)), };} describe("CLIProxyAPI built-in management API", () => { it.effect( "reads both accounts and their earliest unexpired credits without plugin endpoints", () => Effect.gen(function* () { yield* TestClock.setTime(1788710400000); const test = fixture(); const api = yield* test.api; const result = yield* api.readAccounts(config); expect(result.map((account) => account.usageLimits.resetCredits)).toEqual([ { availableCount: 2, nextCreditId: "first", nextExpiresAt: "2099-01-01T00:00:00.000Z" }, { availableCount: 2, nextCreditId: "first", nextExpiresAt: "2099-01-01T00:00:00.000Z" }, ]); expect(result[0]?.usageLimits.windows).toMatchObject([ { id: "secondary", usedPercent: 78, kind: "weekly" }, ]); const calls = test.requests.filter((request) => request.body?.url); expect(calls.map((request) => request.body?.auth_index).sort()).toEqual([ "a", "a", "b", "b", ]); expect( calls.find((request) => request.body?.auth_index === "b")?.body?.header?.[ "Chatgpt-Account-Id" ], ).toBe("account-b"); }), ); it.effect("keeps usage when the credits endpoint fails", () => Effect.gen(function* () { const test = fixture({ upstream: (request) => request.url?.endsWith("rate-limit-reset-credits") ? { status: 503, body: { token: "do-not-publish" } } : { status: 200, body: { rate_limit: { primary_window: { used_percent: 12 } } } }, }); const api = yield* test.api; const result = yield* api.readAccounts(config); expect(result[0]?.usageLimits.windows[0]?.usedPercent).toBe(12); expect(result[0]?.usageLimits.resetCredits).toBeUndefined(); }), ); it.effect("isolates a failed account and never publishes upstream error bodies", () => Effect.gen(function* () { const test = fixture({ upstream: (request) => request.auth_index === "a" ? { status: 401, body: { token: "do-not-publish" } } : { status: 200, body: request.url?.endsWith("rate-limit-reset-credits") ? { credits: [] } : { rate_limit: { primary_window: { used_percent: 12 } } }, }, }); const api = yield* test.api; const result = yield* api.readAccounts(config); expect(result[0]?.usageLimits.unavailable?.reason).toBe("probeFailed"); expect(result[1]?.usageLimits.windows[0]?.usedPercent).toBe(12); expect(encodeJson(result)).not.toContain("do-not-publish"); }), ); it.effect("maps Claude scoped windows without a scheduler plugin", () => Effect.gen(function* () { const test = fixture({ accounts: [{ ...accounts[0]!, provider: "claude" }], upstream: () => ({ status: 200, body: { five_hour: { utilization: 10, resets_at: null }, seven_day: { utilization: 50, resets_at: "2099-01-01T00:00:00Z" }, limits: [ { kind: "weekly_scoped", percent: 80, resets_at: null, scope: { model: { display_name: "Fable" } }, }, ], }, }), }); const api = yield* test.api; const result = yield* api.readAccounts(config); expect( result[0]?.usageLimits.windows.map((window) => [window.id, window.usedPercent]), ).toEqual([ ["five_hour", 10], ["seven_day", 50], ["seven_day_fable", 80], ]); }), ); it.effect("pins redemption to the displayed credit and clears only that account's cooldown", () => Effect.gen(function* () { const test = fixture(); const api = yield* test.api; expect(yield* api.consume(config, "second.json", "credit-b")).toEqual({ outcome: "reset" }); expect(yield* api.consume(config, "second.json", "credit-b")).toEqual({ outcome: "reset" }); const redemptions = test.requests.filter((request) => request.body?.url?.endsWith("/consume"), ); expect(redemptions).toHaveLength(2); expect(redemptions[0]?.body?.data).toBe(redemptions[1]?.body?.data); expect(redemptions[0]?.body?.data).toBe( encodeJson({ redeem_request_id: creditRedeemRequestId("account-b", "credit-b"), credit_id: "credit-b", }), ); expect( test.requests .filter((request) => request.path.endsWith("/reset-quota")) .map((request) => request.body?.auth_index), ).toEqual(["b", "b"]); }), ); for (const [code, outcome] of [ ["nothing_to_reset", "nothingToReset"], ["no_credit", "noCredit"], ["already_redeemed", "alreadyRedeemed"], ] as const) { it.effect(`reports ${code} accurately`, () => Effect.gen(function* () { const test = fixture({ upstream: () => ({ status: 200, body: { code } }) }); const api = yield* test.api; expect(yield* api.consume(config, "first.json", "credit")).toEqual({ outcome }); expect(test.requests.some((request) => request.path.endsWith("/reset-quota"))).toBe( code === "already_redeemed", ); }), ); } it.effect("reports redemption success even if cooldown clearing fails", () => Effect.gen(function* () { const api = yield* fixture({ cooldownStatus: 404 }).api; const result = yield* api.consume(config, "first.json", "credit"); expect(result.outcome).toBe("reset"); expect(result.warning).toContain("cooldown"); }), ); it.effect("skips disabled accounts and rejects redemption on them", () => Effect.gen(function* () { const test = fixture({ accounts: [{ ...accounts[0]!, disabled: true }] }); const api = yield* test.api; expect(yield* api.readAccounts(config)).toEqual([]); expect((yield* api.consume(config, "first.json", "credit").pipe(Effect.result))._tag).toBe( "Failure", ); expect(test.requests.every((request) => request.path.endsWith("/auth-files"))).toBe(true); }), ); it.effect("keeps the same redemption id after an uncertain upstream failure", () => Effect.gen(function* () { let attempts = 0; const test = fixture({ upstream: () => ++attempts === 1 ? { status: 503, body: {} } : { status: 200, body: { code: "already_redeemed" } }, }); const api = yield* test.api; expect((yield* api.consume(config, "first.json", "credit").pipe(Effect.result))._tag).toBe( "Failure", ); expect(yield* api.consume(config, "first.json", "credit")).toEqual({ outcome: "alreadyRedeemed", }); const data = test.requests .filter((request) => request.body?.url?.endsWith("/consume")) .map((request) => request.body?.data); expect(data[0]).toBe(data[1]); expect(test.requests.filter((request) => request.path.endsWith("/reset-quota"))).toHaveLength( 1, ); }), ); it.effect("rejects unknown accounts without forwarding a redemption", () => Effect.gen(function* () { const test = fixture(); const api = yield* test.api; const result = yield* api.consume(config, "missing.json", "credit").pipe(Effect.result); expect(result._tag).toBe("Failure"); expect(test.requests).toHaveLength(1); }), );});
import { describe, expect, it } from "@effect/vitest";
import * as Effect from "effect/Effect";
import * as Schema from "effect/Schema";
import * as TestClock from "effect/testing/TestClock";
import { HttpClient, HttpClientResponse } from "effect/unstable/http";
import { creditRedeemRequestId, makeCliproxyApi } from "./cliproxyApi.ts";
const config = {
kind: "cliproxy",
url: "http://hub.test:8317",
managementKey: "management-secret",
enabled: true,
} as const;
const accounts = [
{
id: "first.json",
auth_index: "a",
provider: "codex",
email: "first@example.com",
id_token: { chatgpt_account_id: "account-a" },
},
{
id: "second.json",
auth_index: "b",
provider: "codex",
email: "second@example.com",
id_token: { chatgpt_account_id: "account-b" },
},
];
const credit = (id: string, expires_at = "2099-01-01T00:00:00Z") => ({
id,
expires_at,
status: "available",
reset_type: "codex_rate_limits",
});
const RequestBody = Schema.Struct({
auth_index: Schema.String,
method: Schema.optional(Schema.String),
url: Schema.optional(Schema.String),
header: Schema.optional(Schema.Record(Schema.String, Schema.String)),
data: Schema.optional(Schema.String),
});
type RequestBody = typeof RequestBody.Type;
const decodeRequest = Schema.decodeUnknownSync(Schema.fromJsonString(RequestBody));
const encodeJson = Schema.encodeSync(Schema.fromJsonString(Schema.Unknown));
function fixture(
options: {
accounts?: Array<(typeof accounts)[number] & { disabled?: boolean }>;
upstream?: (request: RequestBody) => { status: number; body: unknown };
cooldownStatus?: number;
} = {},
) {
const requests: Array<{ path: string; body?: RequestBody }> = [];
const http = HttpClient.make((request) =>
Effect.sync(() => {
expect(request.headers.authorization).toBe("Bearer management-secret");
const path = new URL(request.url).pathname;
const body =
request.body._tag === "Uint8Array"
? decodeRequest(new TextDecoder().decode(request.body.body))
: undefined;
requests.push({ path, ...(body ? { body } : {}) });
if (path.endsWith("/auth-files"))
return HttpClientResponse.fromWeb(
request,
Response.json({ files: options.accounts ?? accounts }),
);
if (path.endsWith("/reset-quota"))
return HttpClientResponse.fromWeb(
request,
Response.json({}, { status: options.cooldownStatus ?? 200 }),
);
expect(path).toBe("/v0/management/api-call");
expect(body?.header?.Authorization).toBe("Bearer $TOKEN$");
const upstream = options.upstream?.(body!) ?? {
status: 200,
body: body?.url?.endsWith("/consume")
? { code: "reset" }
: body?.url?.endsWith("/rate-limit-reset-credits")
? {
credits: [
credit("later", "2099-02-01T00:00:00Z"),
credit("first"),
credit("expired", "2000-01-01T00:00:00Z"),
{ ...credit("used"), status: "redeemed" },
],
}
: {
plan_type: "pro",
rate_limit: {
secondary_window: {
used_percent: 78,
reset_at: 4070908800,
limit_window_seconds: 604800,
},
},
},
};
return HttpClientResponse.fromWeb(
request,
Response.json({ status_code: upstream.status, body: encodeJson(upstream.body) }),
);
}),
);
return {
requests,
api: makeCliproxyApi.pipe(Effect.provideService(HttpClient.HttpClient, http)),
};
}
describe("CLIProxyAPI built-in management API", () => {
it.effect(
"reads both accounts and their earliest unexpired credits without plugin endpoints",
() =>
Effect.gen(function* () {
yield* TestClock.setTime(1788710400000);
const test = fixture();
const api = yield* test.api;
const result = yield* api.readAccounts(config);
expect(result.map((account) => account.usageLimits.resetCredits)).toEqual([
{ availableCount: 2, nextCreditId: "first", nextExpiresAt: "2099-01-01T00:00:00.000Z" },
{ availableCount: 2, nextCreditId: "first", nextExpiresAt: "2099-01-01T00:00:00.000Z" },
]);
expect(result[0]?.usageLimits.windows).toMatchObject([
{ id: "secondary", usedPercent: 78, kind: "weekly" },
]);
const calls = test.requests.filter((request) => request.body?.url);
expect(calls.map((request) => request.body?.auth_index).sort()).toEqual([
"a",
"a",
"b",
"b",
]);
expect(
calls.find((request) => request.body?.auth_index === "b")?.body?.header?.[
"Chatgpt-Account-Id"
],
).toBe("account-b");
}),
);
it.effect("keeps usage when the credits endpoint fails", () =>
Effect.gen(function* () {
const test = fixture({
upstream: (request) =>
request.url?.endsWith("rate-limit-reset-credits")
? { status: 503, body: { token: "do-not-publish" } }
: { status: 200, body: { rate_limit: { primary_window: { used_percent: 12 } } } },
});
const api = yield* test.api;
const result = yield* api.readAccounts(config);
expect(result[0]?.usageLimits.windows[0]?.usedPercent).toBe(12);
expect(result[0]?.usageLimits.resetCredits).toBeUndefined();
}),
);
it.effect("isolates a failed account and never publishes upstream error bodies", () =>
Effect.gen(function* () {
const test = fixture({
upstream: (request) =>
request.auth_index === "a"
? { status: 401, body: { token: "do-not-publish" } }
: {
status: 200,
body: request.url?.endsWith("rate-limit-reset-credits")
? { credits: [] }
: { rate_limit: { primary_window: { used_percent: 12 } } },
},
});
const api = yield* test.api;
const result = yield* api.readAccounts(config);
expect(result[0]?.usageLimits.unavailable?.reason).toBe("probeFailed");
expect(result[1]?.usageLimits.windows[0]?.usedPercent).toBe(12);
expect(encodeJson(result)).not.toContain("do-not-publish");
}),
);
it.effect("maps Claude scoped windows without a scheduler plugin", () =>
Effect.gen(function* () {
const test = fixture({
accounts: [{ ...accounts[0]!, provider: "claude" }],
upstream: () => ({
status: 200,
body: {
five_hour: { utilization: 10, resets_at: null },
seven_day: { utilization: 50, resets_at: "2099-01-01T00:00:00Z" },
limits: [
{
kind: "weekly_scoped",
percent: 80,
resets_at: null,
scope: { model: { display_name: "Fable" } },
},
],
},
}),
});
const api = yield* test.api;
const result = yield* api.readAccounts(config);
expect(
result[0]?.usageLimits.windows.map((window) => [window.id, window.usedPercent]),
).toEqual([
["five_hour", 10],
["seven_day", 50],
["seven_day_fable", 80],
]);
}),
);
it.effect("pins redemption to the displayed credit and clears only that account's cooldown", () =>
Effect.gen(function* () {
const test = fixture();
const api = yield* test.api;
expect(yield* api.consume(config, "second.json", "credit-b")).toEqual({ outcome: "reset" });
expect(yield* api.consume(config, "second.json", "credit-b")).toEqual({ outcome: "reset" });
const redemptions = test.requests.filter((request) =>
request.body?.url?.endsWith("/consume"),
);
expect(redemptions).toHaveLength(2);
expect(redemptions[0]?.body?.data).toBe(redemptions[1]?.body?.data);
expect(redemptions[0]?.body?.data).toBe(
encodeJson({
redeem_request_id: creditRedeemRequestId("account-b", "credit-b"),
credit_id: "credit-b",
}),
);
expect(
test.requests
.filter((request) => request.path.endsWith("/reset-quota"))
.map((request) => request.body?.auth_index),
).toEqual(["b", "b"]);
}),
);
for (const [code, outcome] of [
["nothing_to_reset", "nothingToReset"],
["no_credit", "noCredit"],
["already_redeemed", "alreadyRedeemed"],
] as const) {
it.effect(`reports ${code} accurately`, () =>
Effect.gen(function* () {
const test = fixture({ upstream: () => ({ status: 200, body: { code } }) });
const api = yield* test.api;
expect(yield* api.consume(config, "first.json", "credit")).toEqual({ outcome });
expect(test.requests.some((request) => request.path.endsWith("/reset-quota"))).toBe(
code === "already_redeemed",
);
}),
);
}
it.effect("reports redemption success even if cooldown clearing fails", () =>
Effect.gen(function* () {
const api = yield* fixture({ cooldownStatus: 404 }).api;
const result = yield* api.consume(config, "first.json", "credit");
expect(result.outcome).toBe("reset");
expect(result.warning).toContain("cooldown");
}),
);
it.effect("skips disabled accounts and rejects redemption on them", () =>
Effect.gen(function* () {
const test = fixture({ accounts: [{ ...accounts[0]!, disabled: true }] });
const api = yield* test.api;
expect(yield* api.readAccounts(config)).toEqual([]);
expect((yield* api.consume(config, "first.json", "credit").pipe(Effect.result))._tag).toBe(
"Failure",
);
expect(test.requests.every((request) => request.path.endsWith("/auth-files"))).toBe(true);
}),
);
it.effect("keeps the same redemption id after an uncertain upstream failure", () =>
Effect.gen(function* () {
let attempts = 0;
const test = fixture({
upstream: () =>
++attempts === 1
? { status: 503, body: {} }
: { status: 200, body: { code: "already_redeemed" } },
});
const api = yield* test.api;
expect((yield* api.consume(config, "first.json", "credit").pipe(Effect.result))._tag).toBe(
"Failure",
);
expect(yield* api.consume(config, "first.json", "credit")).toEqual({
outcome: "alreadyRedeemed",
});
const data = test.requests
.filter((request) => request.body?.url?.endsWith("/consume"))
.map((request) => request.body?.data);
expect(data[0]).toBe(data[1]);
expect(test.requests.filter((request) => request.path.endsWith("/reset-quota"))).toHaveLength(
1,
);
}),
);
it.effect("rejects unknown accounts without forwarding a redemption", () =>
Effect.gen(function* () {
const test = fixture();
const api = yield* test.api;
const result = yield* api.consume(config, "missing.json", "credit").pipe(Effect.result);
expect(result._tag).toBe("Failure");
expect(test.requests).toHaveLength(1);
}),
);
});