fix: address dashboard and finance review findings

Co-Authored-By: Paperclip <noreply@paperclip.ing>
This commit is contained in:
Devin FoleyandPaperclip committed 2026-10-03 21:20:22 -07:00
1 parent 85c8b8f72a
commit f63b9e3243
12 files changed
+145 -45

No files matched your search

+29
View File
@@ -0,0 +1,29 @@
import { Command } from "commander";
import { afterEach, describe, expect, it, vi } from "vitest";
import { registerCostCommands } from "../commands/client/cost.js";
function program() {
const result = new Command().exitOverride().configureOutput({ writeOut: () => {}, writeErr: () => {} });
registerCostCommands(result);
return result;
}
const context = ["--company-id", "test-company", "--api-base", "http://paperclip.test", "--api-key", "test-key", "--json"];
afterEach(() => { vi.unstubAllGlobals(); vi.restoreAllMocks(); });
describe("cost report date options", () => {
it.each([["cost", "summary", "costs/summary"], ["finance", "events", "costs/finance-events"]])("forwards bounds and all-time for %s %s", async (group, name, path) => {
const fetch = vi.fn().mockImplementation(() => Promise.resolve(Response.json({})));
vi.stubGlobal("fetch", fetch);
vi.spyOn(console, "log").mockImplementation(() => {});
await program().parseAsync([group, name, ...context, "--all-time"], { from: "user" });
expect(fetch.mock.calls[0][0]).toBe(`http://paperclip.test/api/companies/test-company/${path}?period=all`);
await program().parseAsync([group, name, ...context, "--from", "2026-09-01", "--to", "2026-09-02"], { from: "user" });
expect(fetch.mock.calls[1][0]).toBe(`http://paperclip.test/api/companies/test-company/${path}?from=2026-09-01&to=2026-09-02`);
});
it.each([["accounting", "health"], ["accounting", "inspect"], ["accounting", "invoices"], ["budget", "overview"], ["cost", "window-spend"], ["cost", "quota-windows"]])("rejects unsupported date flags for %s %s", async (group, name) => {
const fetch = vi.fn(); vi.stubGlobal("fetch", fetch);
for (const args of [["--all-time"], ["--from", "2026-09-01"], ["--to", "2026-09-02"]]) {
await expect(program().parseAsync([group, name, ...context, ...args], { from: "user" })).rejects.toThrow("unknown option");
}
expect(fetch).not.toHaveBeenCalled();
});
});
+13 -13
View File
@@ -36,7 +36,7 @@ export function registerCostCommands(program: Command): void {
["window-spend", "costs/window-spend"],
["quota-windows", "costs/quota-windows"],
] as const) {
addCompanyGet(cost, name, `Get ${name} cost data`, path);
addCompanyGet(cost, name, `Get ${name} cost data`, path, name !== "window-spend" && name !== "quota-windows");
}
addCommonClientOptions(
@@ -80,10 +80,10 @@ export function registerCostCommands(program: Command): void {
const finance = program.command("finance").description("Finance event and summary operations");
addCompanyPostJson(finance, "event:create", "Record a finance event", "finance-events");
addCompanyGet(finance, "events", "List finance events", "costs/finance-events");
addCompanyGet(finance, "summary", "Get finance summary", "costs/finance-summary");
addCompanyGet(finance, "by-biller", "Get finance summary by biller", "costs/finance-by-biller");
addCompanyGet(finance, "by-kind", "Get finance summary by kind", "costs/finance-by-kind");
addCompanyGet(finance, "events", "List finance events", "costs/finance-events", true);
addCompanyGet(finance, "summary", "Get finance summary", "costs/finance-summary", true);
addCompanyGet(finance, "by-biller", "Get finance summary by biller", "costs/finance-by-biller", true);
addCompanyGet(finance, "by-kind", "Get finance summary by kind", "costs/finance-by-kind", true);
const budget = program.command("budget").description("Budget policy and incident operations");
addCompanyGet(budget, "overview", "Get budget overview", "budgets/overview");
@@ -147,16 +147,16 @@ export function registerCostCommands(program: Command): void {
);
}
function addCompanyGet(parent: Command, name: string, description: string, path: string): void {
addCommonClientOptions(
parent
.command(name)
.description(description)
.option("-C, --company-id <id>", "Company ID")
function addCompanyGet(parent: Command, name: string, description: string, path: string, dateRange = false): void {
const command = parent.command(name).description(description).option("-C, --company-id <id>", "Company ID");
if (dateRange) {
command
.option("--from <iso-date>", "Inclusive start date (default: current UTC month)")
.option("--to <iso-date>", "Inclusive end date")
.option("--all-time", "Report all recorded history")
.action(async (opts: CompanyOptions) => {
.option("--all-time", "Report all recorded history");
}
addCommonClientOptions(
command.action(async (opts: CompanyOptions) => {
try {
const ctx = resolveCommandContext(opts, { requireCompany: true });
const params = new URLSearchParams();