Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 10 additions & 23 deletions packages/commands/src/commands/usage/token-plan.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,24 +5,18 @@ import { printQuotaBox, readNumber } from "./quota-box.ts";
const TOKEN_PLAN_USAGE_API = "zeldaHttp.apikeyMgr./tokenplan/personal/api/v2/usage";

interface TokenPlanUsage {
per5HourPercentage?: number;
per5HourResetTime?: number;
per1WeekPercentage?: number;
per1WeekResetTime?: number;
per1MonthPercentage?: number;
per1MonthResetTime?: number;
}

function readUsage(result: unknown): TokenPlanUsage {
const response = unwrapResponse(result as Record<string, unknown>);
const usage: TokenPlanUsage = {};

const per5HourPercentage = readNumber(response.per5HourPercentage);
if (per5HourPercentage !== undefined) usage.per5HourPercentage = per5HourPercentage;
const per5HourResetTime = readNumber(response.per5HourResetTime);
if (per5HourResetTime !== undefined) usage.per5HourResetTime = per5HourResetTime;
const per1WeekPercentage = readNumber(response.per1WeekPercentage);
if (per1WeekPercentage !== undefined) usage.per1WeekPercentage = per1WeekPercentage;
const per1WeekResetTime = readNumber(response.per1WeekResetTime);
if (per1WeekResetTime !== undefined) usage.per1WeekResetTime = per1WeekResetTime;
const per1MonthPercentage = readNumber(response.per1MonthPercentage);
if (per1MonthPercentage !== undefined) usage.per1MonthPercentage = per1MonthPercentage;
const per1MonthResetTime = readNumber(response.per1MonthResetTime);
if (per1MonthResetTime !== undefined) usage.per1MonthResetTime = per1MonthResetTime;

return usage;
}
Expand All @@ -32,18 +26,11 @@ function printView(usage: TokenPlanUsage, generatedAt: number): void {
"Token Plan Usage",
[
{
label: "5-hour quota",
label: "Monthly quota",
emptyMessage:
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per5HourPercentage,
resetTime: usage.per5HourResetTime,
},
{
label: "1-week quota",
emptyMessage:
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per1WeekPercentage,
resetTime: usage.per1WeekResetTime,
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
percentage: usage.per1MonthPercentage,
resetTime: usage.per1MonthResetTime,
},
],
generatedAt,
Expand Down
18 changes: 5 additions & 13 deletions packages/commands/tests/e2e/usage-token-plan.e2e.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,28 +50,20 @@ describe.skipIf(!isConsoleE2EReady())("e2e: usage token-plan(Console)", () =
if (isConsoleAuthFailure(result)) return;
expect(result.exitCode, result.stderr).toBe(0);
const data = parseStdoutJson<{
per5HourPercentage?: number;
per5HourResetTime?: number;
per1WeekPercentage?: number;
per1WeekResetTime?: number;
per1MonthPercentage?: number;
per1MonthResetTime?: number;
}>(result.stdout);
const fields = [
data.per5HourPercentage,
data.per5HourResetTime,
data.per1WeekPercentage,
data.per1WeekResetTime,
];
const fields = [data.per1MonthPercentage, data.per1MonthResetTime];
for (const field of fields) {
if (field !== undefined) expect(field).toBeTypeOf("number");
}
});

test("usage token-plan 默认渲染生成时间与两个额度窗口", async () => {
test("usage token-plan 默认渲染生成时间与月额度窗口", async () => {
const result = await runCommandE2e(USAGE_ROUTES, ["usage", "token-plan"]);
if (isConsoleAuthFailure(result)) return;
expect(result.exitCode, result.stderr).toBe(0);
expect(result.stdout).toContain("Generated at:");
expect(result.stdout).toContain("5-hour quota");
expect(result.stdout).toContain("1-week quota");
expect(result.stdout).toContain("Monthly quota");
});
});
82 changes: 23 additions & 59 deletions packages/commands/tests/token-plan-usage.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,18 +32,11 @@ async function runTokenPlan(response: Record<string, unknown>, output?: string):
} as never);
}

function makeUsageResponse(
per5HourPercentage?: number,
per1WeekPercentage = per5HourPercentage,
): Record<string, unknown> {
function makeUsageResponse(per1MonthPercentage?: number): Record<string, unknown> {
const usage: Record<string, number> = {};
if (per5HourPercentage !== undefined) {
usage.per5HourPercentage = per5HourPercentage;
if (per5HourPercentage !== 0) usage.per5HourResetTime = 1_786_000_000_000;
}
if (per1WeekPercentage !== undefined) {
usage.per1WeekPercentage = per1WeekPercentage;
if (per1WeekPercentage !== 0) usage.per1WeekResetTime = 1_786_100_000_000;
if (per1MonthPercentage !== undefined) {
usage.per1MonthPercentage = per1MonthPercentage;
if (per1MonthPercentage !== 0) usage.per1MonthResetTime = 1_786_000_000_000;
}

return wrapResponse(usage);
Expand Down Expand Up @@ -86,91 +79,62 @@ describe("usage token-plan view", () => {
expect(renderedOutput).not.toContain("\u2588".repeat(11));
});

test("accepts missing reset times when the quota usage is zero", async () => {
test("accepts a missing reset time when the quota usage is zero", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0));

expect(output.join("")).toContain("Resets: not applicable (no usage yet)");
});

test("allows one unused quota window without masking another reset time", async () => {
test("renders the monthly reset time when usage is present", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0, 0.5));
await runTokenPlan(makeUsageResponse(0.5));

const renderedOutput = output.join("");
expect(renderedOutput).toContain("Resets: not applicable (no usage yet)");
expect(renderedOutput).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
expect(output.join("")).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
});

test("renders missing quota windows as possibly unlimited", async () => {
test("renders a missing monthly quota as possibly unlimited", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse());

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
);
});

test("renders only the missing quota window as possibly unlimited", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(undefined, 0.5));

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).not.toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
expect(output.join("")).toContain(
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toMatch(/Resets: \d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/);
});

test("renders a window with a missing percentage as possibly unlimited even when its reset time is present", async () => {
test("renders the monthly window as possibly unlimited when its percentage is missing even if the reset time is present", async () => {
const output = captureStdout();

await runTokenPlan(wrapResponse({ per5HourResetTime: 1_786_000_000_000 }));
await runTokenPlan(wrapResponse({ per1MonthResetTime: 1_786_000_000_000 }));

expect(output.join("")).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
});

test("treats non-numeric quota fields as absent instead of failing", async () => {
const output = captureStdout();

await runTokenPlan(
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: Number.NaN }),
);
await runTokenPlan(wrapResponse({ per1MonthPercentage: "not-a-number" }));

const renderedOutput = output.join("");
expect(renderedOutput).toContain(
"The 5-hour limit may be unlimited; verify in the Bailian Token Plan console.",
);
expect(renderedOutput).toContain(
"The 1-week limit may be unlimited; verify in the Bailian Token Plan console.",
expect(output.join("")).toContain(
"The monthly limit may be unlimited; verify in the Bailian Token Plan console.",
);
});
});

describe("usage token-plan json", () => {
test("outputs the four core usage fields with --output json", async () => {
test("outputs the monthly usage fields with --output json", async () => {
const output = captureStdout();

await runTokenPlan(makeUsageResponse(0.5, 0.25), "json");
await runTokenPlan(makeUsageResponse(0.5), "json");

expect(JSON.parse(output.join(""))).toEqual({
per5HourPercentage: 0.5,
per5HourResetTime: 1_786_000_000_000,
per1WeekPercentage: 0.25,
per1WeekResetTime: 1_786_100_000_000,
per1MonthPercentage: 0.5,
per1MonthResetTime: 1_786_000_000_000,
});
});

Expand All @@ -186,10 +150,10 @@ describe("usage token-plan json", () => {
const output = captureStdout();

await runTokenPlan(
wrapResponse({ per5HourPercentage: "not-a-number", per1WeekPercentage: 0 }),
wrapResponse({ per1MonthPercentage: "not-a-number", per1MonthResetTime: 1_786_000_000_000 }),
"json",
);

expect(JSON.parse(output.join(""))).toEqual({ per1WeekPercentage: 0 });
expect(JSON.parse(output.join(""))).toEqual({ per1MonthResetTime: 1_786_000_000_000 });
});
});
Loading