mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Merge PR 3015 into release/v3.8.8
This commit is contained in:
@@ -2939,4 +2939,17 @@ export const __testing = {
|
||||
getMiniMaxPlanLabel,
|
||||
inferMiniMaxPlanLabelFromTotals,
|
||||
getOpencodeUsage,
|
||||
getClaudePlanLabel,
|
||||
createQuotaFromUsage,
|
||||
getMiniMaxQuotaResetAt,
|
||||
isMiniMaxTextQuotaModel,
|
||||
getMiniMaxSessionTotal,
|
||||
getMiniMaxWeeklyTotal,
|
||||
createMiniMaxQuotaFromCount,
|
||||
getMiniMaxAuthErrorMessage,
|
||||
getMiniMaxErrorSummary,
|
||||
mapCodeAssistSubscriptionToPlanLabel,
|
||||
mapCodeAssistTierIdToLabel,
|
||||
mapSubscriptionTierStringToPlanLabel,
|
||||
toDisplayLabel,
|
||||
};
|
||||
|
||||
447
tests/unit/usage-utils.test.ts
Normal file
447
tests/unit/usage-utils.test.ts
Normal file
@@ -0,0 +1,447 @@
|
||||
/**
|
||||
* Tests for pure utility functions exported via usage.__testing
|
||||
*
|
||||
* Covers: parseResetTime, formatGitHubQuotaSnapshot, inferGitHubPlanName,
|
||||
* getMiniMaxPlanLabel, inferMiniMaxPlanLabelFromTotals,
|
||||
* extractCodeAssistSubscriptionTier, extractCodeAssistOnboardTierId.
|
||||
*
|
||||
* These are pure functions (no fetch, no DB) — run without DATA_DIR override.
|
||||
*/
|
||||
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
process.env.NODE_ENV = "test";
|
||||
|
||||
const usage = await import("../../open-sse/services/usage.ts");
|
||||
const { __testing } = usage;
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* parseResetTime */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("parseResetTime", () => {
|
||||
it("returns null for null / undefined", () => {
|
||||
assert.equal(__testing.parseResetTime(null), null);
|
||||
assert.equal(__testing.parseResetTime(undefined), null);
|
||||
});
|
||||
|
||||
it("returns null for epoch-zero", () => {
|
||||
assert.equal(__testing.parseResetTime(0), null);
|
||||
assert.equal(__testing.parseResetTime("1970-01-01T00:00:00.000Z"), null);
|
||||
});
|
||||
|
||||
it("parses a number as ms timestamp (value > 1e12)", () => {
|
||||
const ms = 1_700_000_000_000;
|
||||
const out = __testing.parseResetTime(ms);
|
||||
assert.equal(out, new Date(ms).toISOString());
|
||||
});
|
||||
|
||||
it("parses a number as seconds (value < 1e12)", () => {
|
||||
const sec = 1_700_000_000; // < 1e12
|
||||
const out = __testing.parseResetTime(sec);
|
||||
assert.equal(out, new Date(sec * 1000).toISOString());
|
||||
});
|
||||
|
||||
it("parses an ISO date string", () => {
|
||||
const iso = "2026-06-15T12:00:00.000Z";
|
||||
assert.equal(__testing.parseResetTime(iso), new Date(iso).toISOString());
|
||||
});
|
||||
|
||||
it("parses a Date object", () => {
|
||||
const d = new Date("2026-07-01T00:00:00Z");
|
||||
assert.equal(__testing.parseResetTime(d), d.toISOString());
|
||||
});
|
||||
|
||||
it("returns null for non-date values (objects, booleans)", () => {
|
||||
assert.equal(__testing.parseResetTime({}), null);
|
||||
assert.equal(__testing.parseResetTime(true), null);
|
||||
});
|
||||
|
||||
it("returns null for invalid date strings", () => {
|
||||
assert.equal(__testing.parseResetTime("not-a-date"), null);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* formatGitHubQuotaSnapshot */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("formatGitHubQuotaSnapshot", () => {
|
||||
it("returns null for empty object", () => {
|
||||
assert.equal(__testing.formatGitHubQuotaSnapshot({}), null);
|
||||
});
|
||||
|
||||
it("builds quota from snapshot with all fields", () => {
|
||||
const snap = {
|
||||
entitlement: 1000,
|
||||
used: 300,
|
||||
remaining: 700,
|
||||
percent_remaining: 70,
|
||||
};
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap, "2026-07-01T00:00:00.000Z");
|
||||
assert.equal(q.total, 1000);
|
||||
assert.equal(q.used, 300);
|
||||
assert.equal(q.remaining, 700);
|
||||
assert.equal(q.remainingPercentage, 70);
|
||||
assert.equal(q.resetAt, "2026-07-01T00:00:00.000Z");
|
||||
assert.equal(q.unlimited, false);
|
||||
});
|
||||
|
||||
it("uses entitlement when total is missing", () => {
|
||||
const snap = { entitlement: 500, remaining: 0.5, percent_remaining: 50 };
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap);
|
||||
assert.equal(q.total, 500);
|
||||
assert.ok(q.remaining !== undefined);
|
||||
});
|
||||
|
||||
it("detects unlimited plan", () => {
|
||||
const snap = { entitlement: 0, total: 0, unlimited: true };
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap);
|
||||
assert.equal(q.unlimited, true);
|
||||
assert.equal(q.total, 0); // total is 0 when percent_remaining is missing
|
||||
});
|
||||
|
||||
it("clamps negative values to 0", () => {
|
||||
const snap = { used: -10, entitlement: 100, remaining: -5, percent_remaining: -1 };
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap);
|
||||
assert.equal(q.used, 0);
|
||||
assert.equal(q.remaining, 0);
|
||||
assert.equal(q.remainingPercentage, 0);
|
||||
});
|
||||
|
||||
it("computes missing used from total - remaining", () => {
|
||||
const snap = { entitlement: 200, remaining: 50 };
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap);
|
||||
assert.equal(q.used, 150); // 200 - 50
|
||||
});
|
||||
|
||||
it("computes missing remaining from total - used", () => {
|
||||
const snap = { used: 30, entitlement: 100 };
|
||||
const q = __testing.formatGitHubQuotaSnapshot(snap);
|
||||
assert.equal(q.remaining, 70);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* inferGitHubPlanName */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("inferGitHubPlanName", () => {
|
||||
it("detects Copilot Pro+ from combined string", () => {
|
||||
const data = { copilot_plan: "PRO_PLUS" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Pro+");
|
||||
});
|
||||
|
||||
it("detects Copilot Enterprise", () => {
|
||||
const data = { copilot_plan: "ENTERPRISE" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Enterprise");
|
||||
});
|
||||
|
||||
it("detects Copilot Business", () => {
|
||||
const data = { copilot_plan: "BUSINESS" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Business");
|
||||
});
|
||||
|
||||
it("detects Copilot Student", () => {
|
||||
const data = { copilot_plan: "STUDENT" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Student");
|
||||
});
|
||||
|
||||
it("detects Copilot Free", () => {
|
||||
const data = { copilot_plan: "FREE" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Free");
|
||||
});
|
||||
|
||||
it("detects Copilot Pro", () => {
|
||||
const data = { copilot_plan: "PRO" };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Pro");
|
||||
});
|
||||
|
||||
it("infers Pro+ from premiumTotal >= 1400", () => {
|
||||
const data = { copilot_plan: "INDIVIDUAL" };
|
||||
const premium = { used: 0, total: 1500, remaining: 1500, remainingPercentage: 100, unlimited: false };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, premium), "Copilot Pro+");
|
||||
});
|
||||
|
||||
it("infers Enterprise from premiumTotal >= 900", () => {
|
||||
const data = { copilot_plan: "INDIVIDUAL" };
|
||||
const premium = { used: 0, total: 900, remaining: 900, remainingPercentage: 100, unlimited: false };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, premium), "Copilot Enterprise");
|
||||
});
|
||||
|
||||
it("infers Pro when premiumTotal >= 250 and combined has INDIVIDUAL", () => {
|
||||
const data = { copilot_plan: "INDIVIDUAL" };
|
||||
const premium = { used: 0, total: 300, remaining: 300, remainingPercentage: 100, unlimited: false };
|
||||
assert.equal(__testing.inferGitHubPlanName(data, premium), "Copilot Pro");
|
||||
});
|
||||
|
||||
it("returns 'GitHub Copilot' fallback when nothing matches", () => {
|
||||
const data = {};
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "GitHub Copilot");
|
||||
});
|
||||
|
||||
it("falls back to sku label when planText is empty", () => {
|
||||
const data = { access_type_sku: "BUSINESS_SPO" };
|
||||
// "BUSINESS_SPO" upper-cased matches "BUSINESS" check first
|
||||
assert.equal(__testing.inferGitHubPlanName(data, null), "Copilot Business");
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getMiniMaxPlanLabel */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getMiniMaxPlanLabel", () => {
|
||||
it("returns cleaned title from payload", () => {
|
||||
const payload = { current_subscribe_title: "MiniMax Coding Plan Pro" };
|
||||
assert.equal(__testing.getMiniMaxPlanLabel(payload), "Pro");
|
||||
});
|
||||
|
||||
it("removes 'minimax ' prefix and 'coding plan' text", () => {
|
||||
const payload = { plan: "MiniMax Coding Plan Ultra" };
|
||||
assert.equal(__testing.getMiniMaxPlanLabel(payload), "Ultra");
|
||||
});
|
||||
|
||||
it("calls inferMiniMaxPlanLabelFromTotals when no title present", () => {
|
||||
const models = [{ current_interval_total_count: 500, current_weekly_total_count: 200 }];
|
||||
const label = __testing.getMiniMaxPlanLabel({}, models);
|
||||
assert.ok(typeof label === "string");
|
||||
assert.ok(label.length > 0);
|
||||
});
|
||||
|
||||
it("returns 'Coding Plan' fallback when nothing matches", () => {
|
||||
assert.equal(__testing.getMiniMaxPlanLabel({}), "Coding Plan");
|
||||
});
|
||||
|
||||
it("picks first non-empty string from multiple candidate fields", () => {
|
||||
const payload = { combo_title: "", plan_name: "MiniMax Turbo", plan: "" };
|
||||
const label = __testing.getMiniMaxPlanLabel(payload);
|
||||
assert.equal(label, "Turbo");
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* inferMiniMaxPlanLabelFromTotals */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("inferMiniMaxPlanLabelFromTotals", () => {
|
||||
it("returns 'Max' for totals >= 15K", () => {
|
||||
const models = [{ current_interval_total_count: 20_000 }];
|
||||
assert.equal(__testing.inferMiniMaxPlanLabelFromTotals(models), "Max");
|
||||
});
|
||||
|
||||
it("returns 'Plus' for totals >= 4.5K but < 15K", () => {
|
||||
const models = [{ current_interval_total_count: 5_000 }];
|
||||
assert.equal(__testing.inferMiniMaxPlanLabelFromTotals(models), "Plus");
|
||||
});
|
||||
|
||||
it("returns 'Starter' for totals >= 1.5K but < 4.5K", () => {
|
||||
const models = [{ current_interval_total_count: 2_000 }];
|
||||
assert.equal(__testing.inferMiniMaxPlanLabelFromTotals(models), "Starter");
|
||||
});
|
||||
|
||||
it("returns null when models array is empty", () => {
|
||||
assert.equal(__testing.inferMiniMaxPlanLabelFromTotals([]), null);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getMiniMaxQuotaResetAt */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getMiniMaxQuotaResetAt", () => {
|
||||
it("computes resetAt from remainsMs", () => {
|
||||
const model = { remains_time: 3600_000 }; // 1 hour in ms
|
||||
const capturedAt = 1_700_000_000_000;
|
||||
const result = __testing.getMiniMaxQuotaResetAt(model, capturedAt, "remains_time", "remainsTime", "end_time", "endTime");
|
||||
assert.equal(result, new Date(capturedAt + 3600_000).toISOString());
|
||||
});
|
||||
|
||||
it("falls back to endTime when remainsMs is 0", () => {
|
||||
const model = { end_time: 1_800_000_000_000 };
|
||||
const capturedAt = 1_700_000_000_000;
|
||||
const result = __testing.getMiniMaxQuotaResetAt(model, capturedAt, "remains_time", "remainsTime", "end_time", "endTime");
|
||||
assert.equal(result, new Date(1_800_000_000_000).toISOString());
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getMiniMaxSessionTotal / getMiniMaxWeeklyTotal */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getMiniMaxSessionTotal", () => {
|
||||
it("returns current_interval_total_count", () => {
|
||||
assert.equal(__testing.getMiniMaxSessionTotal({ current_interval_total_count: 999 }), 999);
|
||||
});
|
||||
|
||||
it("clamps to 0 for negative", () => {
|
||||
assert.equal(__testing.getMiniMaxSessionTotal({ current_interval_total_count: -5 }), 0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMiniMaxWeeklyTotal", () => {
|
||||
it("returns current_weekly_total_count", () => {
|
||||
assert.equal(__testing.getMiniMaxWeeklyTotal({ current_weekly_total_count: 500 }), 500);
|
||||
});
|
||||
|
||||
it("clamps to 0 for negative", () => {
|
||||
assert.equal(__testing.getMiniMaxWeeklyTotal({ current_weekly_total_count: -1 }), 0);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* createMiniMaxQuotaFromCount */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("createMiniMaxQuotaFromCount", () => {
|
||||
it("computes used = total - count when countMeansRemaining", () => {
|
||||
const q = __testing.createMiniMaxQuotaFromCount(100, 30, null, true);
|
||||
assert.equal(q.used, 70);
|
||||
assert.equal(q.remaining, 30);
|
||||
});
|
||||
|
||||
it("treats count as used when countMeansRemaining is false", () => {
|
||||
const q = __testing.createMiniMaxQuotaFromCount(100, 30, null, false);
|
||||
assert.equal(q.used, 30);
|
||||
assert.equal(q.remaining, 70);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* createQuotaFromUsage */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("createQuotaFromUsage", () => {
|
||||
it("builds UsageQuota with correct math", () => {
|
||||
const q = __testing.createQuotaFromUsage(200, 1000, "2026-07-01T00:00:00.000Z");
|
||||
assert.equal(q.used, 200);
|
||||
assert.equal(q.total, 1000);
|
||||
assert.equal(q.remaining, 800);
|
||||
assert.equal(q.remainingPercentage, 80);
|
||||
assert.equal(q.resetAt, "2026-07-01T00:00:00.000Z");
|
||||
assert.equal(q.unlimited, false);
|
||||
});
|
||||
|
||||
it("clamps used to total", () => {
|
||||
const q = __testing.createQuotaFromUsage(9999, 100, null);
|
||||
assert.equal(q.used, 100);
|
||||
assert.equal(q.remaining, 0);
|
||||
assert.equal(q.remainingPercentage, 0);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* isMiniMaxTextQuotaModel */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("isMiniMaxTextQuotaModel", () => {
|
||||
it("returns true for minimax-m prefixed models", () => {
|
||||
assert.equal(__testing.isMiniMaxTextQuotaModel("minimax-m1"), true);
|
||||
assert.equal(__testing.isMiniMaxTextQuotaModel("Minimax-M2.5"), true);
|
||||
});
|
||||
|
||||
it("returns true for coding-plan prefixed models", () => {
|
||||
assert.equal(__testing.isMiniMaxTextQuotaModel("Coding-Plan-1"), true);
|
||||
});
|
||||
|
||||
it("returns false for other models", () => {
|
||||
assert.equal(__testing.isMiniMaxTextQuotaModel("tts-1"), false);
|
||||
assert.equal(__testing.isMiniMaxTextQuotaModel("") as boolean, false);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getMiniMaxAuthErrorMessage */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getMiniMaxAuthErrorMessage", () => {
|
||||
it("returns token plan message when message includes 'token plan'", () => {
|
||||
const msg = __testing.getMiniMaxAuthErrorMessage("invalid token plan key");
|
||||
assert.ok(msg.includes("Token Plan API key"));
|
||||
});
|
||||
|
||||
it("returns token plan message when message includes 'coding plan'", () => {
|
||||
const msg = __testing.getMiniMaxAuthErrorMessage("coding plan inactive");
|
||||
assert.ok(msg.includes("Token Plan API key"));
|
||||
});
|
||||
|
||||
it("returns token plan message when message includes 'active period'", () => {
|
||||
const msg = __testing.getMiniMaxAuthErrorMessage("active period ended");
|
||||
assert.ok(msg.includes("Token Plan API key"));
|
||||
});
|
||||
|
||||
it("returns generic message for unknown errors", () => {
|
||||
const msg = __testing.getMiniMaxAuthErrorMessage("something else");
|
||||
assert.ok(msg.includes("access denied"));
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getMiniMaxErrorSummary */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getMiniMaxErrorSummary", () => {
|
||||
it("returns status-only when message is empty", () => {
|
||||
const s = __testing.getMiniMaxErrorSummary(500, "");
|
||||
assert.equal(s, "MiniMax usage endpoint error (500).");
|
||||
});
|
||||
|
||||
it("includes message when under 160 chars", () => {
|
||||
const s = __testing.getMiniMaxErrorSummary(403, "forbidden");
|
||||
assert.equal(s, "MiniMax usage endpoint error (403): forbidden");
|
||||
});
|
||||
|
||||
it("truncates message over 160 chars", () => {
|
||||
const long = "x".repeat(200);
|
||||
const s = __testing.getMiniMaxErrorSummary(429, long);
|
||||
assert.ok(s.length < 200);
|
||||
assert.ok(s.endsWith("..."));
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* getClaudePlanLabel */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("getClaudePlanLabel", () => {
|
||||
it("returns first valid candidate", () => {
|
||||
assert.equal(__testing.getClaudePlanLabel(null, "Pro Plan", "Free"), "Pro Plan");
|
||||
});
|
||||
|
||||
it("skips null/undefined/empty candidates", () => {
|
||||
assert.equal(__testing.getClaudePlanLabel(null, "", "Pro", undefined), "Pro");
|
||||
});
|
||||
|
||||
it("ignores 'claude code' or 'unknown'", () => {
|
||||
assert.equal(__testing.getClaudePlanLabel("Claude Code"), null);
|
||||
assert.equal(__testing.getClaudePlanLabel("Unknown"), null);
|
||||
});
|
||||
|
||||
it("returns null when all candidates are filtered out", () => {
|
||||
assert.equal(__testing.getClaudePlanLabel("claude code", "unknown"), null);
|
||||
assert.equal(__testing.getClaudePlanLabel(), null);
|
||||
});
|
||||
});
|
||||
|
||||
/* ------------------------------------------------------------------ */
|
||||
/* extractCodeAssist helpers */
|
||||
/* ------------------------------------------------------------------ */
|
||||
describe("extractCodeAssistOnboardTierId", () => {
|
||||
it("extracts tier id from paidTier", () => {
|
||||
const sub = { paidTier: { id: "pro-tier" } };
|
||||
assert.equal(__testing.extractCodeAssistOnboardTierId(sub), "pro-tier");
|
||||
});
|
||||
|
||||
it("extracts tier id from currentTier when paidTier is absent", () => {
|
||||
const sub = { currentTier: { id: "starter-tier" } };
|
||||
assert.equal(__testing.extractCodeAssistOnboardTierId(sub), "starter-tier");
|
||||
});
|
||||
|
||||
it("returns 'legacy-tier' when no tier data is present", () => {
|
||||
assert.equal(__testing.extractCodeAssistOnboardTierId({}), "legacy-tier");
|
||||
});
|
||||
});
|
||||
|
||||
describe("extractCodeAssistSubscriptionTier", () => {
|
||||
it("reads tier name from paidTier.name", () => {
|
||||
const info = { paidTier: { name: "ULTRA" } };
|
||||
assert.equal(__testing.extractCodeAssistSubscriptionTier(info), "ULTRA");
|
||||
});
|
||||
|
||||
it("reads tier from currentTier.name", () => {
|
||||
const info = { currentTier: { name: "Pro" } };
|
||||
assert.equal(__testing.extractCodeAssistSubscriptionTier(info), "Pro");
|
||||
});
|
||||
|
||||
it("returns null when nothing matches", () => {
|
||||
assert.equal(__testing.extractCodeAssistSubscriptionTier({}), null);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user