feat(freebuff): enhance Freebucks handling and pricing logic across services

This commit is contained in:
MUH. IQRAM BAHRING
2026-09-07 22:26:28 +08:00
parent eedfdf982b
commit 9c17c5f96c
9 changed files with 352 additions and 19 deletions
+109
View File
@@ -119,6 +119,115 @@ describe("getUsageForProvider(freebuff)", () => {
});
});
it("reports the Freebucks daily pool under each priced model for metered accounts (no rateLimitsByModel)", async () => {
proxyAwareFetch.mockResolvedValueOnce(
jsonResponse({
status: "none",
accessTier: "limited",
rateLimitsByModel: {},
freebucks: {
balance: 10,
daily: { limit: 25, spent: 15, remaining: 10, resetAt: "2026-09-08T07:00:00.000Z" },
wallet: { balance: 0, monthlyBonus: 0 },
spend: { limitUsd: 4, resetAt: "2026-09-08T07:00:00.000Z" },
monthly: { limitUsd: 10, spentUsd: 3.5, remainingUsd: 6.5, resetAt: "2026-10-01T07:00:00.000Z" },
planId: null,
prices: { "deepseek/deepseek-v4-flash": 15, "z-ai/glm-5.3-flash": 5 },
},
}),
);
const usage = await getUsageForProvider({
provider: "freebuff",
accessToken: "tok-1",
});
expect(usage.message).toBeUndefined();
expect(usage.quotas["deepseek/deepseek-v4-flash"]).toMatchObject({
used: 15,
total: 25,
resetAt: "2026-09-08T07:00:00.000Z",
recurring: true,
unlimited: false,
price: 15,
displayName: "DeepSeek V4 Flash",
});
expect(usage.quotas["z-ai/glm-5.3-flash"]).toMatchObject({
used: 15,
total: 25,
price: 5,
displayName: "GLM 5.3 Flash",
});
// No session pools → only the freebucks-derived rows exist.
expect(Object.keys(usage.quotas)).toEqual([
"deepseek/deepseek-v4-flash",
"z-ai/glm-5.3-flash",
]);
// Account summary rides the response for the card header (server data,
// nothing hardcoded client-side).
expect(usage.freebucks).toEqual({
balance: 10,
daily: {
limit: 25,
spent: 15,
remaining: 10,
resetAt: "2026-09-08T07:00:00.000Z",
},
wallet: { balance: 0 },
monthly: { remainingUsd: 6.5, limitUsd: 10, resetAt: "2026-10-01T07:00:00.000Z" },
});
});
it("folds the server's announced priceChanges into the live price (promos expire without a client release)", async () => {
proxyAwareFetch.mockResolvedValueOnce(
jsonResponse({
status: "none",
accessTier: "full",
rateLimitsByModel: {},
freebucks: {
balance: 20,
daily: { limit: 25, spent: 5, remaining: 20, resetAt: "2026-09-08T07:00:00.000Z" },
wallet: { balance: 0, monthlyBonus: 0 },
planId: null,
prices: { "upstage/solar-pro4": 0 },
priceNotices: { "upstage/solar-pro4": "0 Freebucks · Labor Day weekend (through Sep 7 PT)" },
priceChanges: [
{
at: "2026-01-01T00:00:00.000Z", // already due
modelId: "upstage/solar-pro4",
price: 5,
tagline: "Limited-time trial",
},
],
},
}),
);
const usage = await getUsageForProvider({
provider: "freebuff",
accessToken: "tok-1",
});
// The due change is folded in: price 5 + new tagline, schedule emptied.
expect(usage.quotas["upstage/solar-pro4"]).toMatchObject({
price: 5,
priceNote: "Limited-time trial",
displayName: "Solar Pro 4",
});
});
it("attaches no price to legacy session-quota rows", async () => {
proxyAwareFetch.mockResolvedValueOnce(jsonResponse(PRE_JOIN));
const usage = await getUsageForProvider({
provider: "freebuff",
accessToken: "tok-1",
});
expect(usage.quotas["deepseek/deepseek-v4-flash"].price).toBeUndefined();
expect(usage.freebucks).toBeUndefined();
});
it("surfaces a re-login message on 401", async () => {
proxyAwareFetch.mockResolvedValueOnce(jsonResponse({ error: "unauthorized" }, 401));