feat(domain): add seed dataset and domain barrel
Add SCHEMA_VERSION and createSeedDataset to initialize first-run state with three budget envelopes (Video Games, Food, Events and Social), newId() for generating unique identifiers, and complete the domain export barrel with all pure domain modules. Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 5
parent
564a5f4079
commit
438d53d016
@@ -1 +1,11 @@
|
||||
export const DOMAIN_READY = true;
|
||||
|
||||
export * from "./types.ts";
|
||||
export * from "./money.ts";
|
||||
export * from "./months.ts";
|
||||
export * from "./fx.ts";
|
||||
export * from "./allocation.ts";
|
||||
export * from "./charges.ts";
|
||||
export * from "./fold.ts";
|
||||
export * from "./views.ts";
|
||||
export * from "./seed.ts";
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
import { test, expect } from "bun:test";
|
||||
import { createSeedDataset, SCHEMA_VERSION, newId } from "./seed.ts";
|
||||
import { monthView } from "./views.ts";
|
||||
|
||||
test("seeds exactly the three named posts, in order", () => {
|
||||
const data = createSeedDataset("2026-09");
|
||||
expect(data.posts.map((p) => p.name)).toEqual([
|
||||
"Video Games",
|
||||
"Food",
|
||||
"Events and Social",
|
||||
]);
|
||||
expect(data.posts.map((p) => p.order)).toEqual([0, 1, 2]);
|
||||
});
|
||||
|
||||
test("seeded posts are active with a zero fixed standing rule", () => {
|
||||
const data = createSeedDataset("2026-09");
|
||||
for (const post of data.posts) {
|
||||
expect(post.archived).toBe(false);
|
||||
expect(post.standingRule).toEqual({
|
||||
kind: "fixed",
|
||||
amount: { amount: 0, currency: "DKK" },
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
test("the fold starts at the given month and DKK is the base currency", () => {
|
||||
const data = createSeedDataset("2026-09");
|
||||
expect(data.settings.foldStartMonth).toBe("2026-09");
|
||||
expect(data.settings.baseCurrency).toBe("DKK");
|
||||
expect(data.settings.schemaVersion).toBe(SCHEMA_VERSION);
|
||||
});
|
||||
|
||||
test("seeds a month record with zero income and no purchases", () => {
|
||||
const data = createSeedDataset("2026-09");
|
||||
expect(data.months.map((m) => m.id)).toEqual(["2026-09"]);
|
||||
expect(data.purchases).toEqual([]);
|
||||
});
|
||||
|
||||
test("the seed dataset renders a valid month view", () => {
|
||||
const view = monthView(createSeedDataset("2026-09"), "2026-09");
|
||||
expect(view.rows).toHaveLength(3);
|
||||
expect(view.income).toBe(0);
|
||||
expect(view.unallocated).toBe(0);
|
||||
});
|
||||
|
||||
test("newId returns distinct ids", () => {
|
||||
expect(newId()).not.toBe(newId());
|
||||
});
|
||||
@@ -0,0 +1,38 @@
|
||||
import type { Dataset, MonthId, Post } from "./types.ts";
|
||||
|
||||
export const SCHEMA_VERSION = 1;
|
||||
|
||||
const SEED_POST_NAMES = ["Video Games", "Food", "Events and Social"] as const;
|
||||
|
||||
export function newId(): string {
|
||||
return crypto.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* The first-run dataset. Takes the start month as an argument because the
|
||||
* domain layer must not read the ambient clock.
|
||||
*/
|
||||
export function createSeedDataset(startMonth: MonthId): Dataset {
|
||||
const posts: Post[] = SEED_POST_NAMES.map((name, order) => ({
|
||||
id: newId(),
|
||||
name,
|
||||
order,
|
||||
archived: false,
|
||||
currency: "DKK",
|
||||
standingRule: { kind: "fixed", amount: { amount: 0, currency: "DKK" } },
|
||||
}));
|
||||
|
||||
return {
|
||||
settings: {
|
||||
baseCurrency: "DKK",
|
||||
foldStartMonth: startMonth,
|
||||
schemaVersion: SCHEMA_VERSION,
|
||||
},
|
||||
fxRates: [],
|
||||
posts,
|
||||
months: [
|
||||
{ id: startMonth, income: { amount: 0, currency: "DKK" }, ruleOverrides: {} },
|
||||
],
|
||||
purchases: [],
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user