package docs import ( "os" "path/filepath" "strings" "testing" "time" ) var now = time.Date(2026, 10, 23, 20, 45, 12, 345_000_000, time.UTC) // TestNewTemplates checks that fresh records fail only on their markers and // become valid once those are replaced. func TestNewTemplates(t *testing.T) { for _, tt := range []struct { kind, path string markers int }{ {"adr", "docs/adr/20261023T204512345Z-use-x-y.md", 6}, {"plan", "docs/plans/20261023T204512345Z-use-x-y.md", 3}, } { t.Run(tt.kind, func(t *testing.T) { root := t.TempDir() path, err := New(root, tt.kind, `Use "X" & Y`, now) must(t, err) if path != tt.path { t.Fatalf("path = %s", path) } res, err := Check(root) must(t, err) if len(res.Problems) != tt.markers { t.Fatalf("got %v, want %d marker problems", res.Problems, tt.markers) } for _, p := range res.Problems { if !strings.Contains(p.Msg, marker) { t.Errorf("not a marker problem: %s", p) } } data, err := os.ReadFile(filepath.Join(root, path)) must(t, err) must(t, os.WriteFile(filepath.Join(root, path), []byte(strings.ReplaceAll(string(data), marker, "x")), 0o644)) res, err = Check(root) must(t, err) expect(t, res.Problems, nil) }) } } func TestNewCollisions(t *testing.T) { root := t.TempDir() first, err := New(root, "adr", "Same", now) must(t, err) second, err := New(root, "adr", "Same", now) must(t, err) // The same stem in the other directory also advances the timestamp. third, err := New(root, "plan", "Same", now) must(t, err) want := []string{ "docs/adr/20261023T204512345Z-same.md", "docs/adr/20261023T204512346Z-same.md", "docs/plans/20261023T204512347Z-same.md", } for i, got := range []string{first, second, third} { if got != want[i] { t.Errorf("file %d = %s, want %s", i, got, want[i]) } } // Advancing across midnight keeps the date equal to the filename. root = t.TempDir() _, err = New(root, "adr", "Edge", time.Date(2026, 10, 23, 23, 59, 59, 999_000_000, time.UTC)) must(t, err) got, err := New(root, "adr", "Edge", time.Date(2026, 10, 23, 23, 59, 59, 999_000_000, time.UTC)) must(t, err) if got != "docs/adr/20261024T000000000Z-edge.md" { t.Fatalf("got %s", got) } res, err := Check(root) must(t, err) for _, p := range res.Problems { if !strings.Contains(p.Msg, marker) { t.Errorf("unexpected %s", p) } } } func TestNewRejectsBadTitles(t *testing.T) { for _, title := range []string{"", " ", "a\nb", "!!!", "Drop C #", "#", "C ##"} { if path, err := New(t.TempDir(), "adr", title, now); err == nil { t.Errorf("title %q created %s", title, path) } } } func TestSlugify(t *testing.T) { for in, want := range map[string]string{"Use X, Y & Z!": "use-x-y-z", " --Hello-- ": "hello", "Ünï": "n"} { if got := slugify(in); got != want { t.Errorf("slugify(%q) = %q, want %q", in, got, want) } } if got := slugify(strings.Repeat("ab ", 40)); len(got) != maxSlug || strings.HasSuffix(got, "-") { t.Errorf("long slug = %q", got) } }