package api import ( "strings" "testing" "github.com/stretchr/testify/assert" ) func TestParseFrontmatterSplitsHeaderAndBody(t *testing.T) { src := "---\nwing: jepa-fx\nhall: facts\ncreated_at: 2026-01-01T00:00:00Z\n---\n# Title\n\nbody text\n" fm, body := parseFrontmatter(src) assert.Equal(t, "jepa-fx", fm.get("wing")) assert.Equal(t, "facts", fm.get("hall")) assert.Equal(t, "2026-01-01T00:00:00Z", fm.get("created_at")) assert.Equal(t, "# Title\n\nbody text\n", body) } func TestParseFrontmatterNoHeader(t *testing.T) { src := "# Just a body\n\nno frontmatter here\n" fm, body := parseFrontmatter(src) assert.Empty(t, fm.lines) assert.Equal(t, src, body) } func TestFrontmatterSetOverridesExistingKey(t *testing.T) { fm, _ := parseFrontmatter("---\nwing: a\nupdated_at: old\n---\nbody\n") fm.set("updated_at", "new") assert.Equal(t, "new", fm.get("updated_at")) // No duplicate key. assert.Equal(t, 1, strings.Count(fm.render(), "updated_at:")) } func TestFrontmatterSetAppendsNewKey(t *testing.T) { fm, _ := parseFrontmatter("---\nwing: a\n---\nbody\n") fm.set("supersedes", "abc123") out := fm.render() assert.Contains(t, out, "wing: a") assert.Contains(t, out, "supersedes: abc123") } func TestFrontmatterRenderPreservesCustomFields(t *testing.T) { src := "---\nwing: a\nhall: facts\ncustom_field: keep-me\ntags: [x, y]\n---\nbody\n" fm, _ := parseFrontmatter(src) fm.set("updated_at", "2026-06-22T00:00:00Z") out := fm.render() assert.Contains(t, out, "custom_field: keep-me") assert.Contains(t, out, "tags: [x, y]") assert.Contains(t, out, "updated_at: 2026-06-22T00:00:00Z") } func TestFrontmatterRenderRoundTrips(t *testing.T) { src := "---\nwing: a\nhall: facts\n---\n" fm, _ := parseFrontmatter(src) assert.Equal(t, src, fm.render()) } func TestParseFrontmatterIndentedListItemWithColonIsNotMisparsedAsKey(t *testing.T) { // hyperguild#128: an indented YAML block-list item that happens to // contain a colon (e.g. a title with a subtitle) is a continuation // line, never a top-level key — strings.Cut doesn't know that just // because TrimSpace(key) happens to be non-empty. Misparsing it as a // key/value pair and re-rendering strips its indentation, de-indenting // it out of the list it belongs to. Same bug class already found and // fixed in the sibling internal/wiki package's parseFrontmatterBlock // (hyperguild#126 review round 3) — this package was the original the // reimplementation was modeled on and had the gap too. src := "---\naliases:\n - 'Chapter 3: The Reckoning'\ntitle: 'X'\n---\nbody\n" fm, _ := parseFrontmatter(src) fm.set("updated_at", "2026-09-23T00:00:00Z") out := fm.render() assert.Contains(t, out, " - 'Chapter 3: The Reckoning'", "the indented list item must survive verbatim, not get re-emitted as a de-indented fake key") assert.NotContains(t, out, "\n- 'Chapter 3", "must not be split into a fake key/value pair at column 0 (as opposed to surviving indented, asserted above)") } func TestFrontmatterRenderEmptyValueKeyStaysBareNoTrailingSpace(t *testing.T) { // Companion to the indented-list-item fix: "aliases:" (a key with // nothing after the colon, the YAML block-list header) must round-trip // bare, not as "aliases: " with a trailing space — a different byte // sequence than the original, breaking this type's whole reason for // being a line-preserving view rather than a YAML re-marshal. // render() returns only the frontmatter block (see // TestFrontmatterRenderRoundTrips), no trailing body. src := "---\naliases:\n - one\n - two\n---\n" fm, _ := parseFrontmatter(src) assert.Equal(t, src, fm.render()) }