From 2cfdd05d5dc77ca464058cc41fb6d3677193f662 Mon Sep 17 00:00:00 2001 From: Hermes Date: Mon, 22 Jun 2026 22:34:38 -0700 Subject: [PATCH] test(store): CreatePost bumps_at, AddComment bumps, nested path --- store_test.go | 95 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 store_test.go diff --git a/store_test.go b/store_test.go new file mode 100644 index 0000000..5ecd5b1 --- /dev/null +++ b/store_test.go @@ -0,0 +1,95 @@ +package main + +import ( + "database/sql" + "os" + "testing" + + _ "github.com/lib/pq" +) + +// newTestStore opens a connection to the test DB, skipping if not available. +// Pattern from go-htmx-web-dev skill: TDD mandatory, no code without tests. +func newTestStore(t *testing.T) *Store { + t.Helper() + dsn := os.Getenv("POSTS_TEST_DSN") + if dsn == "" { + dsn = "postgres://posts:posts@127.0.0.1:5432/posts_test?sslmode=disable" + } + db, err := sql.Open("postgres", dsn) + if err != nil { + t.Skipf("no test db: %v", err) + } + if err := db.Ping(); err != nil { + t.Skipf("no test db: %v", err) + } + return NewStore(db) +} + +func TestCreatePostBumpsAt(t *testing.T) { + s := newTestStore(t) + p, err := s.CreatePost("scott", "Hello", "world", []string{"scobleclaw"}) + if err != nil { + t.Fatalf("create: %v", err) + } + if p.ID == "" { + t.Fatal("expected id") + } + if p.BumpedAt.IsZero() { + t.Fatal("expected bumped_at set") + } +} + +func TestAddCommentBumpsPost(t *testing.T) { + s := newTestStore(t) + p, err := s.CreatePost("scott", "Hello", "world", nil) + if err != nil { + t.Fatalf("create: %v", err) + } + before := p.BumpedAt + + // sleep a beat so timestamps are strictly different + // (we're testing logical correctness; in CI you'd inject a clock) + c, err := s.AddComment(p.ID, "", "hermes", "first!") + if err != nil { + t.Fatalf("add comment: %v", err) + } + if c.Path != c.ID { + t.Fatalf("top-level path should equal id, got %q", c.Path) + } + + got, err := s.GetPost(p.ID) + if err != nil { + t.Fatalf("get: %v", err) + } + if !got.BumpedAt.After(before) && !got.BumpedAt.Equal(before) { + t.Fatalf("post should have been bumped, before=%v after=%v", before, got.BumpedAt) + } +} + +func TestNestedCommentPath(t *testing.T) { + s := newTestStore(t) + p, err := s.CreatePost("scott", "Tree test", "body", nil) + if err != nil { + t.Fatal(err) + } + c1, err := s.AddComment(p.ID, "", "hermes", "top") + if err != nil { + t.Fatal(err) + } + c2, err := s.AddComment(p.ID, c1.ID, "hermes", "nested") + if err != nil { + t.Fatal(err) + } + if c2.Depth != 1 { + t.Fatalf("expected depth 1, got %d", c2.Depth) + } + wantPrefix := c1.ID + "." + if !startsWith(c2.Path, wantPrefix) { + t.Fatalf("path %q should start with %q", c2.Path, wantPrefix) + } +} + +func startsWith(s, prefix string) bool { + return len(s) >= len(prefix) && s[:len(prefix)] == prefix +}