package store import ( "context" "errors" "path/filepath" "testing" "time" ) func open(t *testing.T) *Store { t.Helper() s, err := Open(filepath.Join(t.TempDir(), "test.db")) if err != nil { t.Fatal(err) } t.Cleanup(func() { s.Close() }) return s } var t0 = time.Date(2026, 10, 21, 22, 14, 3, 0, time.UTC) func TestRoundTrip(t *testing.T) { ctx := context.Background() s := open(t) p := &Page{UID: NewUID(), Source: "folder", Path: "a.md", Slug: "a", OriginalDate: "2026-06-21", FirstSeenAt: t0} if err := s.InsertPage(ctx, p); err != nil { t.Fatal(err) } snap := &Snapshot{PageID: p.ID, ObservedAt: t0, RawHash: "r1", ContentHash: "c1", Raw: "x", FrontmatterJSON: "{}", Note: "n"} if err := s.InsertSnapshot(ctx, snap); err != nil { t.Fatal(err) } snap2 := &Snapshot{PageID: p.ID, ObservedAt: t0.Add(time.Hour), RawHash: "r2", ContentHash: "c2", Raw: "y", FrontmatterJSON: "{}"} if err := s.InsertSnapshot(ctx, snap2); err != nil { t.Fatal(err) } if snap.Seq != 1 || snap2.Seq != 2 { t.Fatalf("seq %d %d", snap.Seq, snap2.Seq) } last, err := s.LastSnapshot(ctx, p.ID) if err != nil || last.RawHash != "r2" || !last.ObservedAt.Equal(t0.Add(time.Hour)) { t.Fatalf("last snapshot %+v %v", last, err) } base := &Revision{PageID: p.ID, Number: 1, SnapshotID: snap.ID, PublishedAt: t0, UpdatedAt: t0} pub := &Revision{PageID: p.ID, Number: 2, SnapshotID: snap2.ID, Note: "changed", Kind: "position", PublishedAt: t0.Add(time.Hour), UpdatedAt: t0.Add(time.Hour)} for _, r := range []*Revision{base, pub} { if err := s.InsertRevision(ctx, r); err != nil { t.Fatal(err) } } lr, err := s.LastRevision(ctx, p.ID) if err != nil || lr.Number != 2 || lr.Note != "changed" { t.Fatalf("last revision %+v %v", lr, err) } recent, err := s.RecentRevisions(ctx, 10, time.Time{}) if err != nil || len(recent) != 1 || recent[0].Number != 2 || recent[0].ID != pub.ID { t.Fatalf("recent %+v %v", recent, err) } now := t0.Add(2 * time.Hour) pub.RetractedAt = &now if err := s.UpdateRevision(ctx, pub); err != nil { t.Fatal(err) } if lr, _ := s.LastRevision(ctx, p.ID); lr.Number != 1 { t.Fatalf("retracted revision still last: %d", lr.Number) } if max, _ := s.MaxRevisionNumber(ctx, p.ID); max != 2 { t.Fatalf("max %d", max) } if recent, _ := s.RecentRevisions(ctx, 10, time.Time{}); len(recent) != 0 { t.Fatal("retracted revisions must leave the feed") } } func TestLookupsAndTx(t *testing.T) { ctx := context.Background() s := open(t) if _, err := s.PageByPath(ctx, "folder", "missing.md"); !errors.Is(err, ErrNotFound) { t.Fatalf("want ErrNotFound, got %v", err) } // A dry-run transaction leaves nothing behind. err := s.Tx(ctx, true, func(q *Queries) error { return q.InsertPage(ctx, &Page{UID: NewUID(), Source: "folder", Path: "dry.md", Slug: "dry", FirstSeenAt: t0}) }) if err != nil { t.Fatal(err) } if pages, _ := s.Pages(ctx, "", true); len(pages) != 0 { t.Fatal("dry run committed") } if err := s.SetMeta(ctx, "k", "v1"); err != nil { t.Fatal(err) } s.SetMeta(ctx, "k", "v2") if v, _ := s.Meta(ctx, "k"); v != "v2" { t.Fatalf("meta %q", v) } } func TestReopenKeepsSchema(t *testing.T) { path := filepath.Join(t.TempDir(), "re.db") s, err := Open(path) if err != nil { t.Fatal(err) } s.Close() s, err = Open(path) if err != nil { t.Fatal(err) } defer s.Close() if err := s.Backup(context.Background(), filepath.Join(filepath.Dir(path), "copy.db")); err != nil { t.Fatal(err) } }