From 5fd63165027bbcc40c11e09d13e7f11ce0e7a643 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 23 Mar 2026 01:16:13 +0000 Subject: [PATCH 1/2] =?UTF-8?q?test:=20finops=20formatting=20=E2=80=94=20f?= =?UTF-8?q?ormatBytes=20and=20truncateQuery=20edge=20cases?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Documents 5 confirmed bugs in finops-formatting.ts that affect user-facing output: negative bytes, NaN, fractional bytes, whitespace-only queries, and truncation exceeding maxLen. Co-Authored-By: Claude Opus 4.6 (1M context) https://claude.ai/code/session_01AfyN3rkDU7fn9G5SpZSM77 --- .../altimate/tools/finops-formatting.test.ts | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 packages/opencode/test/altimate/tools/finops-formatting.test.ts diff --git a/packages/opencode/test/altimate/tools/finops-formatting.test.ts b/packages/opencode/test/altimate/tools/finops-formatting.test.ts new file mode 100644 index 0000000000..016afcefd6 --- /dev/null +++ b/packages/opencode/test/altimate/tools/finops-formatting.test.ts @@ -0,0 +1,79 @@ +import { describe, test, expect } from "bun:test" +import { formatBytes, truncateQuery } from "../../../src/altimate/tools/finops-formatting" + +describe("formatBytes: normal cases", () => { + test("zero returns 0 B", () => { + expect(formatBytes(0)).toBe("0 B") + }) + + test("exact unit boundaries", () => { + expect(formatBytes(1)).toBe("1 B") + expect(formatBytes(1024)).toBe("1.00 KB") + expect(formatBytes(1024 * 1024)).toBe("1.00 MB") + expect(formatBytes(1024 * 1024 * 1024)).toBe("1.00 GB") + }) + + test("non-boundary values", () => { + expect(formatBytes(500)).toBe("500 B") + expect(formatBytes(1536)).toBe("1.50 KB") + }) +}) + +describe("formatBytes: edge cases that expose bugs", () => { + test("negative bytes produces NaN/undefined (known bug)", () => { + // Users see "NaN undefined" when finops tools compute negative deltas + // (e.g. comparing two periods where usage decreased) + const result = formatBytes(-100) + expect(result).toContain("NaN") + }) + + test("fractional bytes produces undefined unit (known bug)", () => { + // Math.floor(Math.log(0.5) / Math.log(1024)) = -1, units[-1] is undefined + const result = formatBytes(0.5) + expect(result).toContain("undefined") + }) + + test("NaN input produces NaN output (known bug)", () => { + const result = formatBytes(NaN) + expect(result).toContain("NaN") + }) +}) + +describe("truncateQuery: normal cases", () => { + test("empty/falsy input returns (empty)", () => { + expect(truncateQuery("", 10)).toBe("(empty)") + }) + + test("short text returned as-is", () => { + expect(truncateQuery("SELECT 1", 50)).toBe("SELECT 1") + }) + + test("long text truncated with ellipsis", () => { + const long = "SELECT * FROM very_long_table_name WHERE id = 1" + const result = truncateQuery(long, 20) + expect(result.length).toBeLessThanOrEqual(20) + expect(result).toEndWith("...") + }) + + test("multiline collapsed to single line", () => { + const sql = "SELECT *\n FROM table\n WHERE id = 1" + expect(truncateQuery(sql, 100)).toBe("SELECT * FROM table WHERE id = 1") + }) +}) + +describe("truncateQuery: edge cases that expose bugs", () => { + test("whitespace-only returns empty string instead of (empty) (known bug)", () => { + // " " is truthy so the `if (!text)` guard is skipped. + // After `.replace(/\s+/g, " ").trim()` it becomes "". + // The length check `0 <= 10` passes, returning the empty string directly. + expect(truncateQuery(" ", 10)).toBe("") + }) + + test("maxLen smaller than 3 produces string longer than maxLen (known bug)", () => { + // slice(0, 2-3) = slice(0, -1) keeps most of the string, then "..." is appended + const result = truncateQuery("hello world", 2) + expect(result.length).toBeGreaterThan(2) + // Actual output is "hello worl..." (13 chars) — far exceeds the 2-char limit + expect(result).toBe("hello worl...") + }) +}) From a3fe7ef18d71d74e63a8bb4f8c3b47ee273c89c8 Mon Sep 17 00:00:00 2001 From: anandgupta42 Date: Sun, 22 Mar 2026 18:40:43 -0700 Subject: [PATCH 2/2] fix: resolve 5 bugs in `formatBytes` and `truncateQuery` MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `formatBytes`: - Guard `NaN`/`Infinity` with `Number.isFinite()` → returns "0 B" - Use `Math.abs()` for log calculation to handle negative byte deltas - Clamp unit index with `Math.max(0, Math.min(...))` to prevent `units[-1]` (fractional bytes) and overflow beyond PB `truncateQuery`: - Check post-trim result for empty string → return "(empty)" - Guard `maxLen <= 0` to prevent `slice(0, negative)` leaking content - Guard `maxLen < 4` to hard-truncate without ellipsis Tests updated from documenting bugs to asserting correct behavior. Co-Authored-By: Claude Opus 4.6 (1M context) --- .../src/altimate/tools/finops-formatting.ts | 7 ++- .../altimate/tools/finops-formatting.test.ts | 49 +++++++++---------- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/packages/opencode/src/altimate/tools/finops-formatting.ts b/packages/opencode/src/altimate/tools/finops-formatting.ts index 7996fd1937..c88aa5579a 100644 --- a/packages/opencode/src/altimate/tools/finops-formatting.ts +++ b/packages/opencode/src/altimate/tools/finops-formatting.ts @@ -1,7 +1,9 @@ export function formatBytes(bytes: number): string { if (bytes === 0) return "0 B" + if (!Number.isFinite(bytes)) return "0 B" + const abs = Math.abs(bytes) const units = ["B", "KB", "MB", "GB", "TB", "PB"] - const i = Math.floor(Math.log(bytes) / Math.log(1024)) + const i = Math.max(0, Math.min(Math.floor(Math.log(abs) / Math.log(1024)), units.length - 1)) const value = bytes / Math.pow(1024, i) return `${value.toFixed(i === 0 ? 0 : 2)} ${units[i]}` } @@ -9,6 +11,9 @@ export function formatBytes(bytes: number): string { export function truncateQuery(text: string, maxLen: number): string { if (!text) return "(empty)" const oneLine = text.replace(/\s+/g, " ").trim() + if (!oneLine) return "(empty)" + if (maxLen <= 0) return "" + if (maxLen < 4) return oneLine.slice(0, maxLen) if (oneLine.length <= maxLen) return oneLine return oneLine.slice(0, maxLen - 3) + "..." } diff --git a/packages/opencode/test/altimate/tools/finops-formatting.test.ts b/packages/opencode/test/altimate/tools/finops-formatting.test.ts index 016afcefd6..6598177d2c 100644 --- a/packages/opencode/test/altimate/tools/finops-formatting.test.ts +++ b/packages/opencode/test/altimate/tools/finops-formatting.test.ts @@ -19,23 +19,23 @@ describe("formatBytes: normal cases", () => { }) }) -describe("formatBytes: edge cases that expose bugs", () => { - test("negative bytes produces NaN/undefined (known bug)", () => { - // Users see "NaN undefined" when finops tools compute negative deltas - // (e.g. comparing two periods where usage decreased) - const result = formatBytes(-100) - expect(result).toContain("NaN") +describe("formatBytes: edge cases", () => { + test("negative bytes displays with sign", () => { + expect(formatBytes(-100)).toBe("-100 B") + expect(formatBytes(-1536)).toBe("-1.50 KB") }) - test("fractional bytes produces undefined unit (known bug)", () => { - // Math.floor(Math.log(0.5) / Math.log(1024)) = -1, units[-1] is undefined - const result = formatBytes(0.5) - expect(result).toContain("undefined") + test("fractional bytes clamps to B unit", () => { + expect(formatBytes(0.5)).toBe("1 B") }) - test("NaN input produces NaN output (known bug)", () => { - const result = formatBytes(NaN) - expect(result).toContain("NaN") + test("NaN input returns 0 B", () => { + expect(formatBytes(NaN)).toBe("0 B") + }) + + test("Infinity input returns 0 B", () => { + expect(formatBytes(Infinity)).toBe("0 B") + expect(formatBytes(-Infinity)).toBe("0 B") }) }) @@ -61,19 +61,18 @@ describe("truncateQuery: normal cases", () => { }) }) -describe("truncateQuery: edge cases that expose bugs", () => { - test("whitespace-only returns empty string instead of (empty) (known bug)", () => { - // " " is truthy so the `if (!text)` guard is skipped. - // After `.replace(/\s+/g, " ").trim()` it becomes "". - // The length check `0 <= 10` passes, returning the empty string directly. - expect(truncateQuery(" ", 10)).toBe("") +describe("truncateQuery: edge cases", () => { + test("whitespace-only returns (empty)", () => { + expect(truncateQuery(" ", 10)).toBe("(empty)") + }) + + test("maxLen smaller than 4 hard-truncates without ellipsis", () => { + expect(truncateQuery("hello world", 2)).toBe("he") + expect(truncateQuery("hello world", 3)).toBe("hel") }) - test("maxLen smaller than 3 produces string longer than maxLen (known bug)", () => { - // slice(0, 2-3) = slice(0, -1) keeps most of the string, then "..." is appended - const result = truncateQuery("hello world", 2) - expect(result.length).toBeGreaterThan(2) - // Actual output is "hello worl..." (13 chars) — far exceeds the 2-char limit - expect(result).toBe("hello worl...") + test("maxLen zero or negative returns empty string", () => { + expect(truncateQuery("hello", 0)).toBe("") + expect(truncateQuery("hello", -5)).toBe("") }) })