// ingestion/internal/frontmatter/scan_test.go package frontmatter import ( "os" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func writeTree(t *testing.T, files map[string]string) string { t.Helper() root := t.TempDir() for name, content := range files { path := filepath.Join(root, name) require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) require.NoError(t, os.WriteFile(path, []byte(content), 0o644)) } return root } const ( brokenDoc = "---\ntitle: Broken: title here\nwing: homelab\n---\n\nbody\n" validDoc = "---\ntitle: 'Fine: quoted'\n---\n\nbody\n" ) func TestScan_ReportsOnlyBrokenFiles(t *testing.T) { root := writeTree(t, map[string]string{ "wiki/a/broken.md": brokenDoc, "wiki/a/valid.md": validDoc, "wiki/b/plain.md": "# no frontmatter\n\nbody: with colon\n", "wiki/a/notes.txt": brokenDoc, // not markdown — must be ignored }) found, err := Scan(root, false) require.NoError(t, err) require.Len(t, found, 1) assert.Equal(t, "wiki/a/broken.md", found[0].Path) assert.False(t, found[0].Repaired) assert.Error(t, found[0].Err) // Dry run must not touch the file. got, err := os.ReadFile(filepath.Join(root, "wiki/a/broken.md")) require.NoError(t, err) assert.Equal(t, brokenDoc, string(got)) } func TestScan_FixWritesRepairedFilesAndLeavesTheRestAlone(t *testing.T) { root := writeTree(t, map[string]string{ "wiki/a/broken.md": brokenDoc, "wiki/a/valid.md": validDoc, }) found, err := Scan(root, true) require.NoError(t, err) require.Len(t, found, 1) assert.True(t, found[0].Repaired) assert.NoError(t, found[0].Err) repaired, err := os.ReadFile(filepath.Join(root, "wiki/a/broken.md")) require.NoError(t, err) require.NoError(t, Lint(repaired), "the repaired file must parse") assert.Contains(t, string(repaired), "title: 'Broken: title here'") assert.Contains(t, string(repaired), "wing: homelab", "untouched keys must survive") untouched, err := os.ReadFile(filepath.Join(root, "wiki/a/valid.md")) require.NoError(t, err) assert.Equal(t, validDoc, string(untouched)) // Second pass finds nothing — the repair is idempotent and complete. again, err := Scan(root, true) require.NoError(t, err) assert.Empty(t, again) } func TestScan_ReportsUnfixableWithoutWriting(t *testing.T) { unfixable := "---\ntitle: fine\n\tbad: tab-indented\n - stray\n---\n\nbody\n" root := writeTree(t, map[string]string{"wiki/x.md": unfixable}) found, err := Scan(root, true) require.NoError(t, err) require.Len(t, found, 1) assert.False(t, found[0].Repaired) assert.Error(t, found[0].Err) got, err := os.ReadFile(filepath.Join(root, "wiki/x.md")) require.NoError(t, err) assert.Equal(t, unfixable, string(got), "an unfixable file must be left exactly as it was") } func TestScan_ReportsUndecodableFilesInsteadOfFailingTheRun(t *testing.T) { root := writeTree(t, map[string]string{"wiki/ok.md": validDoc}) require.NoError(t, os.WriteFile(filepath.Join(root, "wiki/bad-bytes.md"), []byte{'-', '-', '-', '\n', 0xff, 0xfe, '\n', '-', '-', '-', '\n'}, 0o644)) found, err := Scan(root, false) require.NoError(t, err, "one unreadable file must not abort the whole scan") require.Len(t, found, 1) assert.Equal(t, "wiki/bad-bytes.md", found[0].Path) }