package db import ( "database/sql" "testing" ) func openTestDB(t *testing.T) *DB { t.Helper() d, err := Open(":memory:") if err != nil { t.Fatalf("open test db: %v", err) } t.Cleanup(func() { d.Close() }) return d } func TestAddAndGetRedirect(t *testing.T) { d := openTestDB(t) if err := d.AddRedirect("old-slug", "new-slug"); err != nil { t.Fatalf("AddRedirect: %v", err) } got, err := d.GetRedirect("old-slug") if err != nil { t.Fatalf("GetRedirect: %v", err) } if got != "new-slug" { t.Errorf("got %q, want %q", got, "new-slug") } } func TestGetRedirect_NotFound(t *testing.T) { d := openTestDB(t) _, err := d.GetRedirect("missing") if err != sql.ErrNoRows { t.Errorf("expected sql.ErrNoRows, got %v", err) } } func TestCollapseRedirects(t *testing.T) { d := openTestDB(t) if err := d.AddRedirect("a", "b"); err != nil { t.Fatalf("AddRedirect: %v", err) } if err := d.CollapseRedirects("b", "c"); err != nil { t.Fatalf("CollapseRedirects: %v", err) } got, err := d.GetRedirect("a") if err != nil { t.Fatalf("GetRedirect: %v", err) } if got != "c" { t.Errorf("got %q, want %q", got, "c") } } func TestAddRedirect_Upsert(t *testing.T) { d := openTestDB(t) if err := d.AddRedirect("old", "first"); err != nil { t.Fatalf("AddRedirect first: %v", err) } if err := d.AddRedirect("old", "second"); err != nil { t.Fatalf("AddRedirect second: %v", err) } got, err := d.GetRedirect("old") if err != nil { t.Fatalf("GetRedirect: %v", err) } if got != "second" { t.Errorf("got %q, want %q", got, "second") } } func TestRenamePost(t *testing.T) { d := openTestDB(t) original := PostRecord{ Slug: "original-slug", Title: "My Post", Date: "2026-04-04", Tags: []string{"go"}, Draft: false, Blocks: `[{"type":"paragraph","data":{"text":"hello"}}]`, UpdatedAt: 1000000, } if err := d.UpsertPost(original); err != nil { t.Fatalf("UpsertPost: %v", err) } if err := d.RenamePost("original-slug", "new-slug"); err != nil { t.Fatalf("RenamePost: %v", err) } got, err := d.GetPostBySlug("new-slug") if err != nil { t.Fatalf("GetPost new-slug: %v", err) } if got.Title != "My Post" { t.Errorf("title: got %q, want %q", got.Title, "My Post") } _, err = d.GetPostBySlug("original-slug") if err == nil { t.Error("expected old slug to be deleted, but GetPost returned no error") } toSlug, err := d.GetRedirect("original-slug") if err != nil { t.Fatalf("GetRedirect: %v", err) } if toSlug != "new-slug" { t.Errorf("redirect: got %q, want %q", toSlug, "new-slug") } } func TestRenameAndUpsertPost(t *testing.T) { d := openTestDB(t) if err := d.UpsertPost(PostRecord{Slug: "old", Title: "Old Title", Date: "2026-01-01", Blocks: "[]", UpdatedAt: 1}); err != nil { t.Fatalf("UpsertPost: %v", err) } updated := PostRecord{ Slug: "new", Title: "New Title", Date: "2026-04-04", Tags: []string{"go"}, Blocks: "[]", UpdatedAt: 2, } if err := d.RenameAndUpsertPost("old", updated); err != nil { t.Fatalf("RenameAndUpsertPost: %v", err) } got, err := d.GetPostBySlug("new") if err != nil { t.Fatalf("GetPost new: %v", err) } if got.Title != "New Title" { t.Errorf("title: got %q, want %q", got.Title, "New Title") } if got.Date != "2026-04-04" { t.Errorf("date: got %q, want %q", got.Date, "2026-04-04") } if _, err := d.GetPostBySlug("old"); err == nil { t.Error("expected old slug to be deleted") } toSlug, err := d.GetRedirect("old") if err != nil { t.Fatalf("GetRedirect: %v", err) } if toSlug != "new" { t.Errorf("redirect: got %q, want %q", toSlug, "new") } } func TestRenamePost_CollapsesChain(t *testing.T) { d := openTestDB(t) if err := d.UpsertPost(PostRecord{Slug: "b", Title: "B", Blocks: "[]", UpdatedAt: 1}); err != nil { t.Fatalf("UpsertPost: %v", err) } if err := d.AddRedirect("a", "b"); err != nil { t.Fatalf("AddRedirect: %v", err) } if err := d.RenamePost("b", "c"); err != nil { t.Fatalf("RenamePost: %v", err) } got, err := d.GetRedirect("a") if err != nil { t.Fatalf("GetRedirect a: %v", err) } if got != "c" { t.Errorf("chain collapse: got %q, want %q", got, "c") } }