import { describe, test, expect } from "bun:test"; import { parseProfileToml } from "./c14_sama_profile.ts"; describe("c14_sama_profile — parseProfileToml", () => { test("parses the minimum required top-level keys", () => { const p = parseProfileToml(` sama_version = "2.0" profile = "tdd-md" [layers.0] prefixes = ["c31_"] [layers.1] prefixes = ["c32_"] [layers.2] prefixes = ["c13_"] [layers.3] prefixes = ["c11_"] `); expect(p.samaVersion).toBe("2.0"); expect(p.profile).toBe("tdd-md"); }); test("a flat-prefix layer maps to a single synthetic sublayer named 'default'", () => { const p = parseProfileToml(` sama_version = "2.0" profile = "x" [layers.0] prefixes = ["c31_"] [layers.1] prefixes = [] [layers.2] prefixes = [] [layers.3] prefixes = [] `); expect(p.layers[0].sublayers).toHaveLength(1); expect(p.layers[0].sublayers[0]).toEqual({ name: "default", prefix: "c31_", index: 0 }); }); test("a subdivided layer carries sublayer index = position in the array", () => { const p = parseProfileToml(` sama_version = "2.0" profile = "x" [layers.0] prefixes = [] [layers.1] sublayers = [ { name = "logic", prefix = "c32_" }, { name = "render", prefix = "c51_" }, ] [layers.2] prefixes = [] [layers.3] prefixes = [] `); expect(p.layers[1].sublayers).toHaveLength(2); expect(p.layers[1].sublayers[0]).toEqual({ name: "logic", prefix: "c32_", index: 0 }); expect(p.layers[1].sublayers[1]).toEqual({ name: "render", prefix: "c51_", index: 1 }); }); test("comments are stripped", () => { const p = parseProfileToml(` # leading comment sama_version = "2.0" # trailing comment profile = "x" [layers.0] prefixes = ["c31_"] # another [layers.1] prefixes = [] [layers.2] prefixes = [] [layers.3] prefixes = [] `); expect(p.samaVersion).toBe("2.0"); expect(p.layers[0].sublayers[0]!.prefix).toBe("c31_"); }); test("missing top-level keys throws a clear error", () => { expect(() => parseProfileToml(`profile = "x"\n[layers.0]\n[layers.1]\n[layers.2]\n[layers.3]\n`)) .toThrow(/sama_version/); expect(() => parseProfileToml(`sama_version = "2.0"\n[layers.0]\n[layers.1]\n[layers.2]\n[layers.3]\n`)) .toThrow(/profile/); }); test("missing a required layer section throws a clear error", () => { expect(() => parseProfileToml(` sama_version = "2.0" profile = "x" [layers.0] prefixes = [] [layers.1] prefixes = [] [layers.2] prefixes = [] `)).toThrow(/layers\.3/); }); describe("v2.1 dialect flags (§6.1–6.3)", () => { const minimalProfile = (extra: string = ""): string => ` sama_version = "2.1" profile = "x" ${extra} [layers.0] prefixes = ["a_"] [layers.1] prefixes = ["b_"] [layers.2] prefixes = ["c_"] [layers.3] prefixes = ["d_"] `; test("absent dialect flags → ProfileSpec leaves them undefined (≡ v2.0 defaults)", () => { const p = parseProfileToml(minimalProfile()); expect(p.layout).toBeUndefined(); expect(p.tests).toBeUndefined(); expect(p.atomicExemption).toBeUndefined(); }); test("layout = \"prefix\" parses to ProfileSpec.layout = \"prefix\"", () => { const p = parseProfileToml(minimalProfile(`layout = "prefix"`)); expect(p.layout).toBe("prefix"); }); test("layout = \"directory\" parses to ProfileSpec.layout = \"directory\"", () => { const p = parseProfileToml(minimalProfile(`layout = "directory"`)); expect(p.layout).toBe("directory"); }); test("layout with an unknown value throws a clear error referencing §6 and the allowed set", () => { expect(() => parseProfileToml(minimalProfile(`layout = "nonsense"`))).toThrow( /layout.*nonsense.*prefix.*directory.*§6/s, ); }); test("tests = \"sibling\" parses to ProfileSpec.tests = \"sibling\"", () => { const p = parseProfileToml(minimalProfile(`tests = "sibling"`)); expect(p.tests).toBe("sibling"); }); test("tests = \"inline\" parses to ProfileSpec.tests = \"inline\"", () => { const p = parseProfileToml(minimalProfile(`tests = "inline"`)); expect(p.tests).toBe("inline"); }); test("tests with an unknown value throws a clear error", () => { expect(() => parseProfileToml(minimalProfile(`tests = "elsewhere"`))).toThrow( /tests.*elsewhere.*sibling.*inline/s, ); }); test("atomic_exemption = \"none\" parses to ProfileSpec.atomicExemption = \"none\"", () => { const p = parseProfileToml(minimalProfile(`atomic_exemption = "none"`)); expect(p.atomicExemption).toBe("none"); }); test("atomic_exemption = \"declarative\" parses to ProfileSpec.atomicExemption = \"declarative\"", () => { const p = parseProfileToml(minimalProfile(`atomic_exemption = "declarative"`)); expect(p.atomicExemption).toBe("declarative"); }); test("atomic_exemption with an unknown value throws a clear error", () => { expect(() => parseProfileToml(minimalProfile(`atomic_exemption = "behavioural"`))).toThrow( /atomic_exemption.*behavioural.*none.*declarative/s, ); }); test("all three dialect flags can co-occur (the ripgrep rebuild profile shape)", () => { const p = parseProfileToml(minimalProfile(` layout = "directory" tests = "inline" atomic_exemption = "declarative" `)); expect(p.layout).toBe("directory"); expect(p.tests).toBe("inline"); expect(p.atomicExemption).toBe("declarative"); }); test("dialect flags do not interfere with layer parsing", () => { const p = parseProfileToml(minimalProfile(` layout = "directory" tests = "inline" atomic_exemption = "declarative" `)); expect(p.layers[0].sublayers.map((s) => s.prefix)).toEqual(["a_"]); expect(p.layers[1].sublayers.map((s) => s.prefix)).toEqual(["b_"]); expect(p.layers[2].sublayers.map((s) => s.prefix)).toEqual(["c_"]); expect(p.layers[3].sublayers.map((s) => s.prefix)).toEqual(["d_"]); }); }); test("parses the actual repo profile file", () => { // Inline copy of the real-repo profile to keep this test // hermetic — no filesystem read. If sama.profile.toml's shape // ever drifts, this test pins what the parser supports. const real = ` sama_version = "2.0" profile = "tdd-md" [layers.0] prefixes = ["c31_"] [layers.1] sublayers = [ { name = "logic", prefix = "c32_" }, { name = "render", prefix = "c51_" }, ] [layers.2] sublayers = [ { name = "data", prefix = "c13_" }, { name = "io", prefix = "c14_" }, ] [layers.3] sublayers = [ { name = "handlers", prefix = "c21_" }, { name = "server", prefix = "c11_" }, ] `; const p = parseProfileToml(real); expect(p.profile).toBe("tdd-md"); expect(p.layers[0].sublayers.map((s) => s.prefix)).toEqual(["c31_"]); expect(p.layers[1].sublayers.map((s) => s.name)).toEqual(["logic", "render"]); expect(p.layers[2].sublayers.map((s) => s.name)).toEqual(["data", "io"]); expect(p.layers[3].sublayers.map((s) => s.name)).toEqual(["handlers", "server"]); }); });