package index
import (
"encoding/json"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"mercemay.top/src/tilstream/internal/render"
)
func sample() []render.Post {
return []render.Post{
{Slug: "bravo", Title: "Bravo", Date: day("2024-12-01"), Tags: []string{"go", "lambda"}, Body: "Bravo notes about *emphasis* and `code` blocks."},
{Slug: "alpha", Title: "Alpha", Date: day("2024-11-20"), Tags: []string{"go"}, Body: "# Alpha\n\nSome [anchor](http://x) text."},
{Slug: "charlie", Title: "Charlie", Date: day("2024-10-02"), Tags: []string{"hugo"}, Body: "draft-only content", Draft: true},
}
}
func day(s string) time.Time {
t, err := time.Parse("2006-01-02", s)
if err != nil {
panic(err)
}
return t
}
func TestBuild_SortsAndDropsDrafts(t *testing.T) {
t.Parallel()
docs := Build(sample())
want := []Doc{
{ID: "alpha", Title: "Alpha", Tags: []string{"go"}, Body: "# alpha some anchor text."},
{ID: "bravo", Title: "Bravo", Tags: []string{"go", "lambda"}, Body: "bravo notes about emphasis and blocks."},
}
if diff := cmp.Diff(want, docs); diff != "" {
t.Fatalf("Build(-want +got):\n%s", diff)
}
}
func TestBuild_NormalisesCodeFences(t *testing.T) {
t.Parallel()
posts := []render.Post{{
Slug: "z", Title: "Z",
Body: "pre\n```go\nsecret := 42\n```\npost",
}}
docs := Build(posts)
if strings.Contains(docs[0].Body, "secret") {
t.Fatalf("code fence body should be stripped: %q", docs[0].Body)
}
if !strings.Contains(docs[0].Body, "pre") || !strings.Contains(docs[0].Body, "post") {
t.Fatalf("surrounding text dropped: %q", docs[0].Body)
}
}
func TestWriteJSON_RoundTrip(t *testing.T) {
t.Parallel()
dir := t.TempDir()
path := filepath.Join(dir, "search.json")
if err := WriteJSON(path, Build(sample())); err != nil {
t.Fatalf("WriteJSON: %v", err)
}
t.Cleanup(func() { _ = os.Remove(path) })
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var rt []Doc
if err := json.Unmarshal(raw, &rt); err != nil {
t.Fatalf("search.json is not valid JSON: %v", err)
}
if len(rt) != 2 {
t.Fatalf("expected 2 docs on disk, got %d", len(rt))
}
}
func TestTagCounts(t *testing.T) {
t.Parallel()
counts := TagCounts(sample())
want := map[string]int{"go": 2, "lambda": 1}
if diff := cmp.Diff(want, counts); diff != "" {
t.Fatalf("TagCounts(-want +got):\n%s", diff)
}
}
func TestRelated_PrefersOverlapThenDate(t *testing.T) {
t.Parallel()
posts := []render.Post{
{Slug: "a", Tags: []string{"go", "lambda"}, Date: day("2024-12-01")},
{Slug: "b", Tags: []string{"go"}, Date: day("2024-11-20")},
{Slug: "c", Tags: []string{"lambda"}, Date: day("2024-10-02")},
{Slug: "d", Tags: []string{"hugo"}, Date: day("2024-09-01")},
}
got := Related(posts[0], posts, 5)
if len(got) != 2 {
t.Fatalf("expected 2 related posts, got %d: %+v", len(got), got)
}
if got[0].Slug != "b" {
t.Fatalf("higher overlap should rank first, got order: %s, %s", got[0].Slug, got[1].Slug)
}
}
func TestRelated_DropsDrafts(t *testing.T) {
t.Parallel()
posts := []render.Post{
{Slug: "a", Tags: []string{"go"}, Date: day("2024-12-01")},
{Slug: "b", Tags: []string{"go"}, Date: day("2024-11-20"), Draft: true},
}
got := Related(posts[0], posts, 5)
if len(got) != 0 {
t.Fatalf("drafts must not appear in related: %+v", got)
}
}
func TestNormalise_StripsCurlyQuotes(t *testing.T) {
t.Parallel()
got := normalise(`He said “hello” and ‘goodbye’.`)
if strings.ContainsAny(got, `“”‘’`) {
t.Fatalf("curly quotes leaked: %q", got)
}
}