fix: preserve blank-name backups and open linked bill editors

This commit is contained in:
Lucas Winther
2026-09-30 01:04:08 +02:00
parent fc14341f1e
commit 581ede9b07
4 changed files with 92 additions and 14 deletions
+23 -10
View File
@@ -6,6 +6,7 @@ import {
ImportValidationError,
exportFilename,
} from "./json.ts";
import { addRecurringCost, updatePost, updateRecurringCost } from "../store/actions.ts";
import { createSeedDataset } from "../domain/seed.ts";
import { monthView, yearView } from "../domain/views.ts";
import type { Dataset } from "../domain/types.ts";
@@ -164,21 +165,33 @@ describe("import rejects invalid folder graph", () => {
expect(() => parse(data)).toThrow(ImportValidationError);
});
test("rejects a blank post name", () => {
test.each([
["", "Rent"], [" ", "Rent"], ["Games", ""], ["Games", " "],
])("blank item names (%j, %j) survive saved dataset round trips and v7 import", (postName, billName) => {
const data = populated();
data.posts[0]!.name = " ";
expect(() => parse(data)).toThrow(ImportValidationError);
});
test("rejects a blank recurring-cost name", () => {
const data = populated();
data.recurring.push({
id: "rent", name: " ", order: 3, folderId: null, archived: false,
const bill = addRecurringCost(data, {
name: "Rent", folderId: null, archived: false,
amount: { amount: 100, currency: "DKK" }, startDate: "2026-01",
recurrence: { kind: "everyNMonths", n: 1 }, anchoring: "calendar",
splitMode: "percent", splits: [{ postId: data.posts[0]!.id, value: 100, absorbsRemainder: true }],
});
expect(() => parse(data)).toThrow(ImportValidationError);
updatePost(data, data.posts[0]!.id, { name: postName });
updateRecurringCost(data, bill.id, { name: billName });
expect(parseDatasetJson(exportDatasetJson(data))).toEqual(data);
const legacy = JSON.parse(exportDatasetJson(data));
legacy.settings.schemaVersion = 7;
delete legacy.folders;
for (const item of [...legacy.posts, ...legacy.recurring]) delete item.folderId;
const imported = parseDatasetJson(JSON.stringify(legacy));
expect(imported.posts[0]!.name).toBe(postName);
expect(imported.recurring[0]!.name).toBe(billName);
expect(imported.folders).toEqual([]);
});
test.each(["", " "])("still rejects blank folder names %j", name => {
const data = populated();
data.folders = [{ ...folder("home", null, data.posts.length), name }];
expect(() => parse(data)).toThrow(/empty name/);
});
});