main
  1package flux
  2
  3import (
  4	"os"
  5	"path/filepath"
  6	"testing"
  7	"time"
  8)
  9
 10func TestStoreMergeDeduplicates(t *testing.T) {
 11	s := &Store{
 12		Entries: []Entry{
 13			{ID: "a", Title: "First", Date: time.Now()},
 14			{ID: "b", Title: "Second", Date: time.Now()},
 15		},
 16	}
 17
 18	added := s.Merge([]Entry{
 19		{ID: "b", Title: "Second (dup)", Date: time.Now()},
 20		{ID: "c", Title: "Third", Date: time.Now()},
 21	})
 22
 23	if added != 1 {
 24		t.Errorf("Merge() added = %d, want 1", added)
 25	}
 26	if len(s.Entries) != 3 {
 27		t.Errorf("len(Entries) = %d, want 3", len(s.Entries))
 28	}
 29}
 30
 31func TestStoreSort(t *testing.T) {
 32	now := time.Now()
 33	s := &Store{
 34		Entries: []Entry{
 35			{ID: "old", Date: now.Add(-48 * time.Hour)},
 36			{ID: "new", Date: now},
 37			{ID: "mid", Date: now.Add(-24 * time.Hour)},
 38		},
 39	}
 40
 41	s.Sort()
 42
 43	if s.Entries[0].ID != "new" {
 44		t.Errorf("first entry = %s, want new", s.Entries[0].ID)
 45	}
 46	if s.Entries[2].ID != "old" {
 47		t.Errorf("last entry = %s, want old", s.Entries[2].ID)
 48	}
 49}
 50
 51func TestStoreLatest(t *testing.T) {
 52	s := &Store{
 53		Entries: []Entry{
 54			{ID: "1"}, {ID: "2"}, {ID: "3"}, {ID: "4"}, {ID: "5"},
 55		},
 56	}
 57
 58	latest := s.Latest(3)
 59	if len(latest) != 3 {
 60		t.Errorf("Latest(3) = %d entries, want 3", len(latest))
 61	}
 62
 63	all := s.Latest(100)
 64	if len(all) != 5 {
 65		t.Errorf("Latest(100) = %d entries, want 5", len(all))
 66	}
 67}
 68
 69func TestStoreByTag(t *testing.T) {
 70	s := &Store{
 71		Entries: []Entry{
 72			{ID: "1", Tags: []string{"go", "nixos"}},
 73			{ID: "2", Tags: []string{"nixos"}},
 74			{ID: "3", Tags: []string{"emacs"}},
 75		},
 76	}
 77
 78	nixos := s.ByTag("nixos")
 79	if len(nixos) != 2 {
 80		t.Errorf("ByTag(nixos) = %d, want 2", len(nixos))
 81	}
 82
 83	tags := s.AllTags()
 84	if tags["nixos"] != 2 {
 85		t.Errorf("AllTags()[nixos] = %d, want 2", tags["nixos"])
 86	}
 87}
 88
 89func TestStoreSaveLoad(t *testing.T) {
 90	dir := t.TempDir()
 91	path := filepath.Join(dir, "test.json")
 92
 93	original := &Store{
 94		Entries: []Entry{
 95			{ID: "test-1", Kind: KindNote, Title: "Test", Date: time.Date(2026, 4, 1, 0, 0, 0, 0, time.UTC)},
 96		},
 97	}
 98
 99	if err := original.Save(path); err != nil {
100		t.Fatalf("Save: %v", err)
101	}
102
103	loaded, err := LoadStore(path)
104	if err != nil {
105		t.Fatalf("LoadStore: %v", err)
106	}
107
108	if len(loaded.Entries) != 1 {
109		t.Fatalf("loaded %d entries, want 1", len(loaded.Entries))
110	}
111	if loaded.Entries[0].ID != "test-1" {
112		t.Errorf("loaded ID = %s, want test-1", loaded.Entries[0].ID)
113	}
114}
115
116func TestLoadStoreNotExist(t *testing.T) {
117	s, err := LoadStore("/nonexistent/path.json")
118	if err != nil {
119		t.Fatalf("LoadStore nonexistent: %v", err)
120	}
121	if len(s.Entries) != 0 {
122		t.Errorf("expected empty store, got %d entries", len(s.Entries))
123	}
124}
125
126func TestStoreByYear(t *testing.T) {
127	s := &Store{
128		Entries: []Entry{
129			{ID: "1", Date: time.Date(2026, 3, 1, 0, 0, 0, 0, time.UTC)},
130			{ID: "2", Date: time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC)},
131			{ID: "3", Date: time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC)},
132		},
133	}
134
135	byYear := s.ByYear()
136	if len(byYear[2026]) != 2 {
137		t.Errorf("2026 entries = %d, want 2", len(byYear[2026]))
138	}
139	if len(byYear[2025]) != 1 {
140		t.Errorf("2025 entries = %d, want 1", len(byYear[2025]))
141	}
142}
143
144func TestRenderOutputs(t *testing.T) {
145	dir := t.TempDir()
146	store := &Store{
147		Entries: []Entry{
148			{ID: "note-1", Kind: KindNote, Title: "Test Note", Body: "Hello world", Tags: []string{"test"}, Date: time.Date(2026, 4, 1, 12, 0, 0, 0, time.UTC), Source: "manual"},
149			{ID: "github-pr-test/repo#1", Kind: KindGitHubPR, Title: "Fix thing", URL: "https://github.com/test/repo/pull/1", Date: time.Date(2026, 3, 15, 10, 0, 0, 0, time.UTC), Source: "github", Metadata: map[string]string{"repo": "test/repo", "number": "1", "state": "merged"}},
150		},
151	}
152	store.Sort()
153
154	cfg := Config{
155		Title:       "Test Flux",
156		Description: "Test",
157		Author:      "Test",
158		BaseURL:     "https://example.com/flux",
159		SiteURL:     "https://example.com",
160		OutputDir:   dir,
161	}
162
163	if err := RenderJSONFeed(cfg, store.Entries); err != nil {
164		t.Fatalf("RenderJSONFeed: %v", err)
165	}
166	if _, err := os.Stat(filepath.Join(dir, "feed.json")); err != nil {
167		t.Errorf("feed.json not created: %v", err)
168	}
169
170	if err := RenderAtomFeed(cfg, store.Entries); err != nil {
171		t.Fatalf("RenderAtomFeed: %v", err)
172	}
173	if _, err := os.Stat(filepath.Join(dir, "feed.xml")); err != nil {
174		t.Errorf("feed.xml not created: %v", err)
175	}
176
177	if err := RenderHTML(cfg, store); err != nil {
178		t.Fatalf("RenderHTML: %v", err)
179	}
180	if _, err := os.Stat(filepath.Join(dir, "index.html")); err != nil {
181		t.Errorf("index.html not created: %v", err)
182	}
183	if _, err := os.Stat(filepath.Join(dir, "2026.html")); err != nil {
184		t.Errorf("2026.html not created: %v", err)
185	}
186	if _, err := os.Stat(filepath.Join(dir, "tags", "test.html")); err != nil {
187		t.Errorf("tags/test.html not created: %v", err)
188	}
189}