summaryrefslogtreecommitdiff
path: root/internal/store/ics_feeds_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/store/ics_feeds_test.go')
-rw-r--r--internal/store/ics_feeds_test.go144
1 files changed, 144 insertions, 0 deletions
diff --git a/internal/store/ics_feeds_test.go b/internal/store/ics_feeds_test.go
new file mode 100644
index 0000000..5b8889c
--- /dev/null
+++ b/internal/store/ics_feeds_test.go
@@ -0,0 +1,144 @@
+package store
+
+import (
+ "database/sql"
+ "errors"
+ "path/filepath"
+ "testing"
+
+ _ "github.com/mattn/go-sqlite3"
+)
+
+func setupICSStore(t *testing.T) *Store {
+ t.Helper()
+ db, err := sql.Open("sqlite3", filepath.Join(t.TempDir(), "ics.db"))
+ if err != nil {
+ t.Fatalf("open: %v", err)
+ }
+ db.SetMaxOpenConns(1)
+ if _, err := db.Exec(`
+ CREATE TABLE ics_feeds (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL,
+ url TEXT NOT NULL UNIQUE,
+ enabled INTEGER NOT NULL DEFAULT 1,
+ last_fetched_at DATETIME,
+ last_error TEXT,
+ event_count INTEGER NOT NULL DEFAULT 0,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+ );`); err != nil {
+ t.Fatalf("schema: %v", err)
+ }
+ return &Store{db: db}
+}
+
+func TestICSFeeds_AddAndList(t *testing.T) {
+ s := setupICSStore(t)
+ defer func() { _ = s.Close() }()
+
+ if err := s.AddICSFeed("Seahawks", "https://example.com/sea.ics"); err != nil {
+ t.Fatalf("add: %v", err)
+ }
+ feeds, err := s.GetICSFeeds()
+ if err != nil {
+ t.Fatalf("list: %v", err)
+ }
+ if len(feeds) != 1 || feeds[0].Name != "Seahawks" || !feeds[0].Enabled {
+ t.Fatalf("got %+v", feeds)
+ }
+ if feeds[0].LastFetchedAt != nil {
+ t.Error("a feed that has never been fetched should have no timestamp")
+ }
+}
+
+// Re-adding the same URL should update and re-enable rather than error, since
+// "add it again" is the obvious way to try to fix a disabled feed.
+func TestICSFeeds_ReAddingUpdatesAndReEnables(t *testing.T) {
+ s := setupICSStore(t)
+ defer func() { _ = s.Close() }()
+
+ _ = s.AddICSFeed("Old name", "https://example.com/sea.ics")
+ feeds, _ := s.GetICSFeeds()
+ _ = s.SetICSFeedEnabled(feeds[0].ID, false)
+
+ if err := s.AddICSFeed("Seahawks", "https://example.com/sea.ics"); err != nil {
+ t.Fatalf("re-add: %v", err)
+ }
+ feeds, _ = s.GetICSFeeds()
+ if len(feeds) != 1 {
+ t.Fatalf("expected 1 feed, got %d", len(feeds))
+ }
+ if feeds[0].Name != "Seahawks" || !feeds[0].Enabled {
+ t.Errorf("got %+v, want renamed and re-enabled", feeds[0])
+ }
+}
+
+func TestICSFeeds_DisabledAreExcludedFromFetchList(t *testing.T) {
+ s := setupICSStore(t)
+ defer func() { _ = s.Close() }()
+
+ _ = s.AddICSFeed("On", "https://example.com/on.ics")
+ _ = s.AddICSFeed("Off", "https://example.com/off.ics")
+ all, _ := s.GetICSFeeds()
+ for _, f := range all {
+ if f.Name == "Off" {
+ _ = s.SetICSFeedEnabled(f.ID, false)
+ }
+ }
+
+ enabled, err := s.GetEnabledICSFeeds()
+ if err != nil {
+ t.Fatalf("enabled: %v", err)
+ }
+ if len(enabled) != 1 || enabled[0].Name != "On" {
+ t.Errorf("got %+v, want only the enabled feed", enabled)
+ }
+}
+
+// A failure must be recorded, and a later success must clear it — otherwise a
+// feed that recovers still shows as broken forever.
+func TestICSFeeds_RecordResultClearsPreviousError(t *testing.T) {
+ s := setupICSStore(t)
+ defer func() { _ = s.Close() }()
+
+ _ = s.AddICSFeed("Seahawks", "https://example.com/sea.ics")
+ feeds, _ := s.GetICSFeeds()
+ id := feeds[0].ID
+
+ if err := s.RecordICSFeedResult(id, 0, errors.New("feed returned HTTP 404")); err != nil {
+ t.Fatalf("record failure: %v", err)
+ }
+ feeds, _ = s.GetICSFeeds()
+ if feeds[0].LastError == "" {
+ t.Fatal("expected the failure to be recorded")
+ }
+ if feeds[0].LastFetchedAt == nil {
+ t.Error("a failed fetch is still a fetch attempt and should be timestamped")
+ }
+
+ if err := s.RecordICSFeedResult(id, 21, nil); err != nil {
+ t.Fatalf("record success: %v", err)
+ }
+ feeds, _ = s.GetICSFeeds()
+ if feeds[0].LastError != "" {
+ t.Errorf("a successful fetch should clear the error, got %q", feeds[0].LastError)
+ }
+ if feeds[0].EventCount != 21 {
+ t.Errorf("EventCount = %d, want 21", feeds[0].EventCount)
+ }
+}
+
+func TestICSFeeds_Delete(t *testing.T) {
+ s := setupICSStore(t)
+ defer func() { _ = s.Close() }()
+
+ _ = s.AddICSFeed("Seahawks", "https://example.com/sea.ics")
+ feeds, _ := s.GetICSFeeds()
+ if err := s.DeleteICSFeed(feeds[0].ID); err != nil {
+ t.Fatalf("delete: %v", err)
+ }
+ feeds, _ = s.GetICSFeeds()
+ if len(feeds) != 0 {
+ t.Errorf("got %d feeds after delete", len(feeds))
+ }
+}