import { describe, it, expect } from "vitest"; import { taxonomyToComboboxCategories, findTaxonomyCategory, } from "./migrationTargets"; import { getTaxonomyV1, type TaxonomyNode, } from "../../services/categoryTaxonomyService"; function node( id: number, name: string, children: TaxonomyNode[] = [], type: "expense" | "income" | "transfer" = "expense", ): TaxonomyNode { return { id, name, i18n_key: `categoriesSeed.test.${id}`, type, color: "#123456", sort_order: 1, children, }; } describe("taxonomyToComboboxCategories (leaves only)", () => { it("returns [] for empty input", () => { expect(taxonomyToComboboxCategories([])).toEqual([]); }); it("keeps only leaves, dropping every intermediate parent, in DFS order", () => { const roots = [ node(1000, "Revenus", [ node(1010, "Emploi", [node(1011, "Paie"), node(1012, "Prime")]), ]), node(1100, "Alimentation", [node(1111, "Épicerie")]), ]; const out = taxonomyToComboboxCategories(roots); // parents 1000/1010/1100 dropped; leaves kept in depth-first reading order expect(out.map((c) => c.id)).toEqual([1011, 1012, 1111]); }); it("flattens leaves: parent_id undefined + running sort_order (flat list)", () => { const roots = [ node(1000, "Revenus", [ node(1010, "Emploi", [node(1011, "Paie"), node(1012, "Prime")]), ]), node(1100, "Alimentation", [node(1111, "Épicerie")]), ]; const out = taxonomyToComboboxCategories(roots); expect(out.every((c) => c.parent_id === undefined)).toBe(true); expect(out.map((c) => c.sort_order)).toEqual([0, 1, 2]); }); it("marks every returned row inputable (all leaves)", () => { const roots = [ node(1700, "Loisirs", [ node(1710, "Divertissement", [node(1711, "Cinéma")]), ]), ]; const out = taxonomyToComboboxCategories(roots); expect(out.map((c) => c.id)).toEqual([1711]); // 1700 + 1710 parents dropped expect(out.every((c) => c.is_inputable)).toBe(true); }); it("copies id/name/type/color/i18n_key and defaults is_active + created_at", () => { const [c] = taxonomyToComboboxCategories([ node(1111, "Épicerie", [], "expense"), ]); expect(c).toMatchObject({ id: 1111, name: "Épicerie", type: "expense", color: "#123456", i18n_key: "categoriesSeed.test.1111", is_active: true, is_inputable: true, }); expect(typeof c.created_at).toBe("string"); }); it("adapts the real v1 taxonomy: 112 leaves, ids unique, NO non-leaf parent offered", () => { const out = taxonomyToComboboxCategories(getTaxonomyV1().roots); expect(out.length).toBe(112); // leaves only (was 150 incl. 38 parents) const ids = new Set(out.map((c) => c.id)); expect(ids.size).toBe(out.length); // The non-leaf parent 1710 "Divertissement" must NOT be a selectable option. expect(out.find((c) => c.id === 1710)).toBeUndefined(); // A known leaf is present and inputable. expect(out.find((c) => c.id === 1111)!.is_inputable).toBe(true); expect(out.every((c) => c.is_inputable)).toBe(true); }); }); describe("findTaxonomyCategory", () => { const roots = [ node(1700, "Loisirs", [ node(1710, "Divertissement", [node(1711, "Cinéma")]), ]), ]; it("resolves a non-leaf parent by id (to display a current target)", () => { const c = findTaxonomyCategory(roots, 1710); expect(c).toMatchObject({ id: 1710, name: "Divertissement", is_inputable: false, }); expect(c!.parent_id).toBeUndefined(); }); it("resolves a leaf too", () => { expect(findTaxonomyCategory(roots, 1711)).toMatchObject({ id: 1711, is_inputable: true, }); }); it("returns null for an unknown id", () => { expect(findTaxonomyCategory(roots, 9999)).toBeNull(); }); it("finds the real low-confidence default target 1710 in the bundled taxonomy", () => { const c = findTaxonomyCategory(getTaxonomyV1().roots, 1710); expect(c).toMatchObject({ id: 1710, is_inputable: false }); }); });