internal/feed/feed_test.go

package feed

import (
	"encoding/xml"
	"os"
	"path/filepath"
	"strings"
	"testing"
	"time"

	"mercemay.top/src/tilstream/internal/render"
)

func samplePosts() []render.Post {
	return []render.Post{
		{
			Slug:  "interfaces-vs-structs",
			Title: "Go interfaces vs structs",
			Date:  time.Date(2024, 12, 28, 10, 0, 0, 0, time.UTC),
			Tags:  []string{"go", "design"},
			Body:  "Short body that mentions empty interfaces once.",
		},
		{
			Slug:  "fsnotify-linux-quirks",
			Title: "fsnotify on Linux",
			Date:  time.Date(2024, 12, 2, 9, 30, 0, 0, time.UTC),
			Tags:  []string{"go", "linux"},
			Body:  "fsnotify can fire twice on editor saves; debounce it.",
		},
	}
}

func TestRSSWrite_ItemCountAndGUID(t *testing.T) {
	dir := t.TempDir()
	dest := filepath.Join(dir, "rss.xml")

	if err := Write(dest, samplePosts(), "https://til.example.com/"); err != nil {
		t.Fatalf("Write returned error: %v", err)
	}

	data, err := os.ReadFile(dest)
	if err != nil {
		t.Fatal(err)
	}
	var parsed rss
	if err := xml.Unmarshal(data, &parsed); err != nil {
		t.Fatalf("unmarshal: %v", err)
	}
	if got, want := len(parsed.Channel.Items), 2; got != want {
		t.Fatalf("item count: got %d want %d", got, want)
	}
	// Commit d9c4f01: GUID should include first category.
	if !strings.HasSuffix(parsed.Channel.Items[0].GUID.Value, ":go") {
		t.Errorf("guid missing category suffix: %q", parsed.Channel.Items[0].GUID.Value)
	}
}

func TestAtomWrite_HasSelfLink(t *testing.T) {
	dir := t.TempDir()
	dest := filepath.Join(dir, "atom.xml")

	err := WriteAtom(dest, samplePosts(), "https://til.example.com", "merce", "me@example.com")
	if err != nil {
		t.Fatal(err)
	}
	body, _ := os.ReadFile(dest)
	if !strings.Contains(string(body), `rel="self"`) {
		t.Fatal("atom feed missing self link")
	}
	if !strings.Contains(string(body), `<name>merce</name>`) {
		t.Fatal("atom feed missing author name")
	}
}

func TestAtomWrite_EmptyPosts(t *testing.T) {
	dir := t.TempDir()
	dest := filepath.Join(dir, "atom.xml")

	if err := WriteAtom(dest, nil, "https://til.example.com", "merce", ""); err != nil {
		t.Fatalf("empty feed should not error: %v", err)
	}
	info, err := os.Stat(dest)
	if err != nil {
		t.Fatal(err)
	}
	if info.Size() == 0 {
		t.Fatal("expected a non-empty file even with zero entries")
	}
}

func TestNewestMtime_FallsBackToNow(t *testing.T) {
	got := newestMtime(nil)
	if got.IsZero() {
		t.Fatal("zero fallback mtime")
	}
	if time.Since(got) > time.Minute {
		t.Fatalf("fallback mtime not recent enough: %v", got)
	}
}

func TestWriteXML_CreatesParentExists(t *testing.T) {
	dir := t.TempDir()
	dest := filepath.Join(dir, "sub", "missing.xml")
	if err := writeXML(dest, atomFeed{Title: "x"}); err == nil {
		t.Fatal("expected error when parent directory does not exist")
	}
}