From 567d63544edafd874b3ce2df3c64ea275e3f5f2d Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Wed, 23 Sep 2026 21:57:35 +0300 Subject: [PATCH 1/3] factory: claim #27 by runner-47545-1790189855395 From 2b212d8ce8c10d99fe935cc124151c7f410c5d50 Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Wed, 23 Sep 2026 22:00:47 +0300 Subject: [PATCH 2/3] factory: build #27 --- src/csv.ts | 13 +++++-------- tests/cli.test.ts | 5 +++++ tests/csv.test.ts | 26 ++++++++++++++++++++++++++ tests/routes.test.ts | 1 + 4 files changed, 37 insertions(+), 8 deletions(-) create mode 100644 tests/csv.test.ts diff --git a/src/csv.ts b/src/csv.ts index bbb9f3a..0b95855 100644 --- a/src/csv.ts +++ b/src/csv.ts @@ -1,4 +1,5 @@ import type { Database } from "bun:sqlite"; +import { formatCents } from "./money/index.ts"; interface ExpenseRow { createdAt: string; @@ -26,19 +27,15 @@ function fetchExpenseRows(db: Database, groupId: string): ExpenseRow[] { } /** - * Build the CSV export for a group's expenses. - * - * SEEDED DEFECT (issue #3): the amount column prints the raw integer cents - * (e.g. "1000") instead of a formatted amount ("10.00"). Baseline tests - * only check the header row and the row count, not the amount column's - * formatting, so this ships undetected. The fix should reuse - * `formatCents` from src/money (see .claude/skills/handling-money). + * Build the CSV export for a group's expenses. The amount column is + * formatted as a decimal ("10.00") here, at the output boundary. */ export function exportCsv(db: Database, groupId: string): string { const rows = fetchExpenseRows(db, groupId); const header = "date,payer,description,amount\n"; const lines = rows.map( - (r) => `${r.createdAt},${csvEscape(r.payerName)},${csvEscape(r.description)},${r.amountCents}`, + (r) => + `${r.createdAt},${csvEscape(r.payerName)},${csvEscape(r.description)},${formatCents(r.amountCents)}`, ); return header + lines.join("\n") + (lines.length > 0 ? "\n" : ""); } diff --git a/tests/cli.test.ts b/tests/cli.test.ts index b87c924..6e7d784 100644 --- a/tests/cli.test.ts +++ b/tests/cli.test.ts @@ -38,6 +38,11 @@ describe("splitbill export", () => { const output = run(["export", "flat-4b"], setupDb()); expect(output.split("\n")[0]).toBe("date,payer,description,amount"); }); + + test("formats the amount column as a decimal", () => { + const output = run(["export", "flat-4b"], setupDb()); + expect(output.split("\n").some((line) => line.endsWith(",45.00"))).toBe(true); + }); }); describe("splitbill with no command", () => { diff --git a/tests/csv.test.ts b/tests/csv.test.ts new file mode 100644 index 0000000..5b8e4c9 --- /dev/null +++ b/tests/csv.test.ts @@ -0,0 +1,26 @@ +import { describe, expect, test } from "bun:test"; +import { Database } from "bun:sqlite"; +import { initSchema } from "../src/db.ts"; +import { seedDemoData } from "../src/seed.ts"; +import { exportCsv } from "../src/csv.ts"; + +function setupDb(): Database { + const db = new Database(":memory:"); + initSchema(db); + seedDemoData(db); + return db; +} + +describe("exportCsv", () => { + test("formats the amount column as a decimal via formatCents", () => { + const lines = exportCsv(setupDb(), "flat-4b").trim().split("\n"); + const amounts = lines.slice(1).map((line) => line.split(",").at(-1)); + expect(amounts.sort()).toEqual(["12.00", "30.00", "45.00"]); + }); + + test("keeps the header row and one line per expense", () => { + const lines = exportCsv(setupDb(), "flat-4b").trim().split("\n"); + expect(lines[0]).toBe("date,payer,description,amount"); + expect(lines.length).toBe(4); // header + 3 seeded Flat 4B expenses + }); +}); diff --git a/tests/routes.test.ts b/tests/routes.test.ts index 25cac54..9f241ab 100644 --- a/tests/routes.test.ts +++ b/tests/routes.test.ts @@ -137,5 +137,6 @@ describe("GET /api/groups/:id/export.csv", () => { const lines = text.trim().split("\n"); expect(lines[0]).toBe("date,payer,description,amount"); expect(lines.length).toBe(4); // header + 3 seeded Flat 4B expenses + expect(lines.some((line) => line.endsWith(",45.00"))).toBe(true); }); }); From bf78c9c31df90ae8c14bb35b0e3f43e341f4a95f Mon Sep 17 00:00:00 2001 From: Param Harrison Date: Wed, 23 Sep 2026 22:05:46 +0300 Subject: [PATCH 3/3] factory: build #27 --- tests/csv.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/csv.test.ts b/tests/csv.test.ts index 5b8e4c9..e59a9f1 100644 --- a/tests/csv.test.ts +++ b/tests/csv.test.ts @@ -3,6 +3,7 @@ import { Database } from "bun:sqlite"; import { initSchema } from "../src/db.ts"; import { seedDemoData } from "../src/seed.ts"; import { exportCsv } from "../src/csv.ts"; +import { createExpense } from "../src/expenses.ts"; function setupDb(): Database { const db = new Database(":memory:"); @@ -18,6 +19,20 @@ describe("exportCsv", () => { expect(amounts.sort()).toEqual(["12.00", "30.00", "45.00"]); }); + test("keeps a trailing zero cent in the amount column", () => { + const db = setupDb(); + createExpense(db, { + groupId: "flat-4b", + payerId: "flat-priya-id", + amountCents: 4550, + description: "Water bill", + splitAmong: ["flat-priya-id", "flat-imran-id", "flat-sara-id"], + createdAt: "2026-09-10T09:00:00.000Z", // after the seeded Flat 4B expenses + }); + const lines = exportCsv(db, "flat-4b").trim().split("\n"); + expect(lines.at(-1)).toEndWith(",Water bill,45.50"); + }); + test("keeps the header row and one line per expense", () => { const lines = exportCsv(setupDb(), "flat-4b").trim().split("\n"); expect(lines[0]).toBe("date,payer,description,amount");