Files
paseo/packages/app/src/usage/model.test.ts
T
Mohamed Boudra e3682ba18c Usage fetches only when shown, and can be turned off (#8181)
* fix(usage): fetch only when shown and add a host-wide off switch

* fix(usage): restore policy defaults and isolate usage test state

* refactor(usage): give the sidebar footer sole ownership of its summary
2026-10-30 14:32:19 +07:00

420 lines
14 KiB
TypeScript

import { afterEach, describe, expect, it } from "vitest";
import { i18n } from "@/i18n/i18next";
import {
resolveUsageReport,
displayPercent,
resolveAgentUsageView,
replaceReport,
resolveUsageRefresh,
resolveUsageHostId,
resolveUsageModalHostId,
resolveUsageView,
settleReports,
upsertReport,
usageWindowRowLabel,
type UsageHost,
type UsageQueryState,
} from "./model";
import type { UsageReportEntry, UsageWindow } from "./types";
function entry(input: {
windows?: UsageWindow[];
planLabel?: string;
icon?: string;
sourceId?: string;
}): UsageReportEntry {
return {
id: `${input.sourceId ?? "fixture"}:account-1`,
account: {},
fetchedAt: "2026-01-01T00:00:00.000Z",
sourceId: input.sourceId ?? "fixture",
sourceLabel: "Fixture source",
...(input.icon ? { icon: input.icon } : {}),
report: {
status: "available",
...(input.planLabel ? { planLabel: input.planLabel } : {}),
windows: input.windows ?? [],
},
};
}
function ready(data: UsageReportEntry[]): UsageQueryState {
return { data, error: null, isFetching: false };
}
describe("resolveUsageView", () => {
it("asks for a host update when the host lacks usage sources", () => {
expect(
resolveUsageView({
hostLabel: "Laptop",
isConnected: true,
supportsUsage: false,
query: ready([entry({})]),
}),
).toEqual({ kind: "unavailable", message: "Update Laptop to see usage" });
});
it("asks for a connection before anything else", () => {
expect(
resolveUsageView({
hostLabel: "Laptop",
isConnected: false,
supportsUsage: false,
query: undefined,
}),
).toEqual({ kind: "unavailable", message: "Connect to Laptop to see usage" });
});
it("localizes unavailable host messages while retaining the host name", async () => {
await i18n.changeLanguage("ko");
expect(
resolveUsageView({
hostLabel: "Laptop",
isConnected: false,
supportsUsage: false,
query: undefined,
}),
).toEqual({ kind: "unavailable", message: "Laptop에 연결하여 제공업체 사용량을 확인하세요" });
});
it("moves from loading to ready to error", () => {
const base = { hostLabel: "Laptop", isConnected: true, supportsUsage: true };
expect(resolveUsageView({ ...base, query: undefined })).toEqual({ kind: "loading" });
expect(
resolveUsageView({ ...base, query: { data: [], error: null, isFetching: true } }),
).toEqual({ kind: "ready", reports: [], isRefreshing: true });
expect(
resolveUsageView({
...base,
query: { data: undefined, error: new Error("boom"), isFetching: false },
}),
).toEqual({ kind: "error", message: "boom" });
});
});
afterEach(async () => {
await i18n.changeLanguage("en");
});
const hosts: UsageHost[] = [
{ serverId: "offline", label: "Offline", isConnected: false, supportsUsage: true },
{ serverId: "old", label: "Old", isConnected: true, supportsUsage: false },
{ serverId: "a", label: "Alpha", isConnected: true, supportsUsage: true },
{ serverId: "b", label: "Beta", isConnected: true, supportsUsage: true },
];
describe("resolveUsageHostId", () => {
const choose = (pickedServerId: string | null, activeServerId: string | null) =>
resolveUsageHostId({ pickedServerId, activeServerId, hosts });
it("reads the picked host over the active workspace's host", () => {
expect(choose("a", "b")).toBe("a");
});
it("reads the active workspace's host when nothing usable is picked", () => {
expect(choose(null, "b")).toBe("b");
expect(choose("offline", "b")).toBe("b");
expect(choose("old", "b")).toBe("b");
});
it("falls back to the first connected host that reports usage", () => {
expect(choose(null, null)).toBe("a");
expect(choose(null, "offline")).toBe("a");
expect(choose(null, "old")).toBe("a");
});
it("has no host when none reports usage", () => {
expect(
resolveUsageHostId({
pickedServerId: "old",
activeServerId: "old",
hosts: hosts.slice(0, 2),
}),
).toBeNull();
});
});
describe("resolveUsageModalHostId", () => {
it("shows the picked host while it stays connected, even one that cannot report usage", () => {
expect(resolveUsageModalHostId({ pickedServerId: "old", activeServerId: "b", hosts })).toBe(
"old",
);
expect(resolveUsageModalHostId({ pickedServerId: "offline", activeServerId: "b", hosts })).toBe(
"b",
);
});
it("otherwise shows the host the sidebar row reads", () => {
for (const pickedServerId of [null, "a", "offline"]) {
for (const activeServerId of ["b", null, "old", "offline"]) {
const choice = { pickedServerId, activeServerId, hosts };
expect(resolveUsageModalHostId(choice)).toBe(resolveUsageHostId(choice));
}
}
});
it("shows the first connected host when none reports usage, so the modal says to update it", () => {
expect(
resolveUsageModalHostId({
pickedServerId: null,
activeServerId: null,
hosts: hosts.slice(0, 2),
}),
).toBe("old");
expect(
resolveUsageModalHostId({ pickedServerId: null, activeServerId: null, hosts: [] }),
).toBeNull();
});
});
describe("usageWindowRowLabel", () => {
it("names the window, its percent and its reset after what pinning it pins", () => {
expect(
usageWindowRowLabel({
pinLabel: "Pin Claude Session",
value: "31%",
trailing: "resets in 2h",
}),
).toBe("Pin Claude Session, 31% · resets in 2h");
expect(usageWindowRowLabel({ pinLabel: "Pin Codex Weekly", value: "—", trailing: null })).toBe(
"Pin Codex Weekly, —",
);
});
});
describe("displayPercent", () => {
const window = (input: Partial<UsageWindow>): UsageWindow => ({
id: "weekly",
label: "Weekly",
...input,
});
it("reads the share used or the share left", () => {
expect(displayPercent(window({ usedPct: 31 }), "used")).toBe(31);
expect(displayPercent(window({ usedPct: 31 }), "remaining")).toBe(69);
expect(displayPercent(window({ remainingPct: 40 }), "used")).toBe(60);
expect(displayPercent(window({ usedPct: 50, remainingPct: 45 }), "remaining")).toBe(45);
});
it("has no percent when the window reports none", () => {
expect(displayPercent(window({}), "remaining")).toBeNull();
});
});
describe("resolveUsageRefresh", () => {
it("is pending while a refresh runs, even after an earlier failure", () => {
expect(resolveUsageRefresh({ isPending: true, error: new Error("boom") })).toBe("pending");
});
it("is failed after a refresh errors", () => {
expect(resolveUsageRefresh({ isPending: false, error: new Error("boom") })).toBe("failed");
});
it("is idle before and after a successful refresh", () => {
expect(resolveUsageRefresh({ isPending: false, error: null })).toBe("idle");
});
});
describe("replaceReport", () => {
const alpha = entry({ sourceId: "alpha", planLabel: "Old" });
const beta = entry({ sourceId: "beta" });
it("swaps only the refreshed report, in place", () => {
const refreshed = entry({ sourceId: "alpha", planLabel: "New" });
expect(replaceReport([alpha, beta], alpha.id, refreshed)).toEqual([refreshed, beta]);
});
it("drops a report the daemon no longer knows", () => {
expect(replaceReport([alpha, beta], alpha.id, null)).toEqual([beta]);
});
});
describe("upsertReport", () => {
const alpha = entry({ sourceId: "alpha", planLabel: "Old" });
const beta = entry({ sourceId: "beta" });
it("starts a list from the first streamed report", () => {
expect(upsertReport(undefined, alpha)).toEqual([alpha]);
});
it("appends a report the list does not have yet", () => {
expect(upsertReport([alpha], beta)).toEqual([alpha, beta]);
});
it("replaces a report the list already has, in place", () => {
const streamed = entry({ sourceId: "alpha", planLabel: "New" });
expect(upsertReport([alpha, beta], streamed)).toEqual([streamed, beta]);
});
it("keeps a copy fetched after the streamed one, such as a Refresh that landed first", () => {
const refreshed = { ...alpha, planLabel: "Refreshed", fetchedAt: "2026-01-01T00:05:00.000Z" };
expect(upsertReport([refreshed, beta], alpha)).toEqual([refreshed, beta]);
});
});
describe("settleReports", () => {
const alpha = entry({ sourceId: "alpha", planLabel: "Old" });
const beta = entry({ sourceId: "beta" });
it("takes the finished list, dropping reports the host no longer has", () => {
const streamed = entry({ sourceId: "alpha", planLabel: "New" });
expect(settleReports([alpha, beta], [streamed])).toEqual([streamed]);
});
it("keeps a report refreshed while the list streamed over its older copy in the list", () => {
const refreshed = { ...alpha, planLabel: "Refreshed", fetchedAt: "2026-01-01T00:05:00.000Z" };
expect(settleReports([refreshed, beta], [alpha, beta])).toEqual([refreshed, beta]);
});
it("keeps the displayed order when providers finish a refresh in reverse order", () => {
const refreshed = entry({ sourceId: "alpha", planLabel: "New" });
const streamed = upsertReport(upsertReport([alpha, beta], beta), refreshed);
expect(settleReports(streamed, [beta, refreshed])).toEqual([refreshed, beta]);
});
it("drops missing reports and appends new ones without moving surviving reports", () => {
const gamma = entry({ sourceId: "gamma" });
const delta = entry({ sourceId: "delta" });
expect(settleReports([alpha, beta, gamma], [delta, gamma, alpha])).toEqual([
alpha,
gamma,
delta,
]);
});
it("takes the finished list as is when nothing was shown yet", () => {
expect(settleReports(undefined, [alpha, beta])).toEqual([alpha, beta]);
});
});
describe("resolveAgentUsageView", () => {
const report = entry({ sourceId: "claude" });
const idle: UsageQueryState = { data: undefined, error: null, isFetching: false };
it("shows nothing on a host that cannot report usage", () => {
expect(resolveAgentUsageView({ canReport: false, query: idle })).toEqual({ kind: "none" });
});
it("loads until the first report streams in", () => {
expect(
resolveAgentUsageView({ canReport: true, query: { ...idle, isFetching: true } }),
).toEqual({ kind: "loading" });
});
it("shows each report as it streams in, before the request finishes", () => {
expect(
resolveAgentUsageView({
canReport: true,
query: { data: [report], error: null, isFetching: true },
}),
).toEqual({ kind: "ready", reports: [report] });
});
it("shows nothing for an agent with no account to report", () => {
expect(resolveAgentUsageView({ canReport: true, query: { ...idle, data: [] } })).toEqual({
kind: "none",
});
});
it("says why a failed request has nothing to show", () => {
expect(
resolveAgentUsageView({
canReport: true,
query: { ...idle, error: new Error("Unknown agent") },
}),
).toEqual({ kind: "error", message: "Unable to load usage: Unknown agent" });
});
it("says the request failed instead of showing the reports from before it", () => {
expect(
resolveAgentUsageView({
canReport: true,
query: { data: [report], error: new Error("Unknown agent"), isFetching: false },
}),
).toEqual({ kind: "error", message: "Unable to load usage: Unknown agent" });
});
});
it("resolves retained usage and successful time without inventing old-daemon history", () => {
const success = entry({ windows: [{ id: "weekly", label: "Weekly", usedPct: 42 }] });
const failure: UsageReportEntry = {
...success,
fetchedAt: "2026-01-01T00:06:00.000Z",
report: { status: "error", error: "Rate limited" },
lastSuccessful: {
fetchedAt: success.fetchedAt,
report: success.report as Extract<UsageReportEntry["report"], { status: "available" }>,
},
};
expect(resolveUsageReport(failure, i18n.t)).toEqual({
windows: success.report.status === "available" ? success.report.windows : [],
balances: [],
details: [],
planLabel: null,
successfulAt: success.fetchedAt,
retained: true,
messages: ["Rate limited"],
});
expect(resolveUsageReport({ ...failure, lastSuccessful: undefined }, i18n.t)).toEqual({
windows: [],
balances: [],
details: [],
planLabel: null,
successfulAt: null,
retained: false,
messages: ["Rate limited"],
});
expect(resolveUsageReport(success, i18n.t)).toEqual({
windows: success.report.status === "available" ? success.report.windows : [],
balances: [],
details: [],
planLabel: null,
successfulAt: success.fetchedAt,
retained: false,
messages: [],
});
});
it("orders failed polls by attempt time even when successful data is older", () => {
const success = entry({});
if (success.report.status !== "available") throw new Error("fixture must be available");
const failure: UsageReportEntry = {
...success,
fetchedAt: "2026-01-01T00:06:00.000Z",
report: { status: "error", error: "Rate limited" },
lastSuccessful: { fetchedAt: success.fetchedAt, report: success.report },
};
expect(upsertReport([failure], success)).toEqual([failure]);
expect(settleReports([failure], [success])).toEqual([failure]);
expect(upsertReport([success], failure)).toEqual([failure]);
});
it("available and retained usage show the same successful completion time", () => {
const success: UsageReportEntry = { ...entry({}), successfulAt: "2026-01-01T00:00:15.000Z" };
if (success.report.status !== "available") throw new Error("fixture must be available");
expect(resolveUsageReport(success, i18n.t).successfulAt).toBe("2026-01-01T00:00:15.000Z");
const failure: UsageReportEntry = {
...success,
successfulAt: undefined,
fetchedAt: "2026-01-01T00:06:00.000Z",
report: { status: "error", error: "Rate limited" },
lastSuccessful: { fetchedAt: success.successfulAt!, report: success.report },
};
expect(resolveUsageReport(failure, i18n.t).successfulAt).toBe(
resolveUsageReport(success, i18n.t).successfulAt,
);
expect(upsertReport([failure], success)).toEqual([failure]);
});
it("disabled host shows off even with cached reports", () => {
expect(
resolveUsageView({
hostLabel: "Laptop",
isConnected: true,
supportsUsage: true,
enabled: false,
query: { data: [], error: null, isFetching: false },
}),
).toMatchObject({ kind: "off" });
});