/*
Unit tests for heading id.pbt covering its documented public contract and regression-sensitive edge cases.
- Do not validate caller-supplied id shape — the kebab typeId contract belongs to the section schema.
- RFC-1196: created — fast-check properties over all prefix-source combinations.
- RFC-1196: review — sectionId from rawProps only, Compass headers on new tests
*/
import { test, expect } from "vitest";
import fc from "fast-check";
import { sectionHeadingId } from "../heading-id.ts";
/**
* RFC-1196 — property guarantees for the shared heading-id resolver.
* Pure function: no DOM, no IO.
*/
const arbTypeId = fc.option(fc.stringMatching(/^[a-z0-9][a-z0-9-]{0,59}$/), { nil: undefined });
// Prefix sources are domain-shaped: sectionNumber is a zero-padded index or "",
// sectionId is an authored kebab blockId — neither can start with '-'.
const arbMaybeEmpty = fc.oneof(
fc.constant(undefined),
fc.constant(""),
fc.stringMatching(/^[a-z0-9][a-z0-9-]{0,39}$/),
);
test("result never starts with '-' — an empty prefix must never leak into the id", () => {
fc.assert(
fc.property(arbTypeId, arbMaybeEmpty, arbMaybeEmpty, (typeId, num, sid) => {
const id = sectionHeadingId(typeId, num, sid);
if (id !== undefined) {
expect(id.startsWith("-"), `id "${id}" must not start with '-'`).toBe(false);
}
}),
);
});
test("falsy prefix sources return the type id unchanged", () => {
fc.assert(
fc.property(arbTypeId, (typeId) => {
expect(sectionHeadingId(typeId, undefined, undefined)).toBe(typeId);
expect(sectionHeadingId(typeId, "", "")).toBe(typeId);
expect(sectionHeadingId(typeId, "", undefined)).toBe(typeId);
expect(sectionHeadingId(typeId, undefined, "")).toBe(typeId);
}),
);
});
test("non-empty sectionNumber always wins over sectionId", () => {
fc.assert(
fc.property(
arbTypeId,
fc.string({ minLength: 1, maxLength: 40 }),
arbMaybeEmpty,
(typeId, num, sid) => {
const id = sectionHeadingId(typeId, num, sid);
if (typeId !== undefined) {
expect(id, `non-empty sectionNumber "${num}" must prefix the type id`).toBe(
`${num}-${typeId}`,
);
}
},
),
);
});
test("empty sectionNumber behaves identically to absent sectionNumber", () => {
fc.assert(
fc.property(arbTypeId, arbMaybeEmpty, (typeId, sid) => {
expect(sectionHeadingId(typeId, "", sid)).toBe(sectionHeadingId(typeId, undefined, sid));
}),
);
});