Artifact 2d4c4f95b010856250354add99a3000ff85414a6bfaf536455b410f70460504e:
- File
internal/oltp/vote_test.go
— part of check-in
[e674e718a8]
at
2026-08-21 19:34:28
on branch quarry-forum
— forum polls: fenced-block definition, OLTP ballots, no-script widget
The block syncs as markdown (fossil peers render it readably); option range and close date re-read from the body at cast time, never the form. (user: dmestas, size: 5677) [annotate] [blame] [check-ins using] [more...]
package oltp import "testing" func TestForumVoteRoundTrip(t *testing.T) { db := testDB(t) // Vote, and the score reflects it. if err := db.SetForumVote("r", "alice", "post1", "thread1", 1); err != nil { t.Fatal(err) } if err := db.SetForumVote("r", "bob", "post1", "thread1", -1); err != nil { t.Fatal(err) } if err := db.SetForumVote("r", "carol", "post1", "thread1", 1); err != nil { t.Fatal(err) } scores, err := db.ForumPostScores("r", []string{"post1", "absent"}) if err != nil { t.Fatal(err) } if scores["post1"] != 1 { t.Fatalf("post1 score = %d, want 1", scores["post1"]) } if _, ok := scores["absent"]; ok { t.Fatal("unvoted post must be absent from the map, not zero-valued in it") } // Changing a vote replaces it — the (repo, account, post) key is the // idempotency: two upvotes from one account are one upvote. if err := db.SetForumVote("r", "alice", "post1", "thread1", -1); err != nil { t.Fatal(err) } scores, _ = db.ForumPostScores("r", []string{"post1"}) if scores["post1"] != -1 { t.Fatalf("after alice flips: score = %d, want -1", scores["post1"]) } // Retracting (dir 0) deletes the row. if err := db.SetForumVote("r", "bob", "post1", "thread1", 0); err != nil { t.Fatal(err) } scores, _ = db.ForumPostScores("r", []string{"post1"}) if scores["post1"] != 0 { t.Fatalf("after bob retracts: score = %d, want 0", scores["post1"]) } // Anything other than -1, 0, 1 is refused. if err := db.SetForumVote("r", "mallory", "post1", "thread1", 5); err == nil { t.Fatal("dir 5 accepted") } } func TestForumThreadScoresAggregate(t *testing.T) { db := testDB(t) must := func(err error) { t.Helper() if err != nil { t.Fatal(err) } } must(db.SetForumVote("r", "alice", "p1", "t1", 1)) must(db.SetForumVote("r", "bob", "p2", "t1", 1)) must(db.SetForumVote("r", "carol", "p3", "t2", -1)) // Another repo's votes must not bleed in. must(db.SetForumVote("other", "dave", "p1", "t1", -1)) scores, err := db.ForumThreadScores("r", []string{"t1", "t2", "t3"}) if err != nil { t.Fatal(err) } if scores["t1"] != 2 || scores["t2"] != -1 { t.Fatalf("thread scores = %v, want t1:2 t2:-1", scores) } if _, ok := scores["t3"]; ok { t.Fatal("unvoted thread present in map") } } func TestForumAccountVotes(t *testing.T) { db := testDB(t) if err := db.SetForumVote("r", "alice", "p1", "t1", 1); err != nil { t.Fatal(err) } if err := db.SetForumVote("r", "alice", "p2", "t1", -1); err != nil { t.Fatal(err) } mine, err := db.ForumAccountVotes("r", "alice", []string{"p1", "p2", "p3"}) if err != nil { t.Fatal(err) } if mine["p1"] != 1 || mine["p2"] != -1 { t.Fatalf("account votes = %v", mine) } if _, ok := mine["p3"]; ok { t.Fatal("unvoted post present") } // Empty account (anonymous) asks for nothing and gets nothing. if none, _ := db.ForumAccountVotes("r", "", []string{"p1"}); len(none) != 0 { t.Fatalf("anonymous votes = %v, want empty", none) } } func TestPurgeRepoSweepsForumVotes(t *testing.T) { db := testDB(t) if err := db.SetForumVote("gone", "alice", "p1", "t1", 1); err != nil { t.Fatal(err) } if err := db.PurgeRepo("gone"); err != nil { t.Fatal(err) } scores, err := db.ForumPostScores("gone", []string{"p1"}) if err != nil { t.Fatal(err) } if len(scores) != 0 { t.Fatalf("votes survived purge: %v", scores) } } func TestThreadCategoryRoundTrip(t *testing.T) { db := testDB(t) if err := db.SetThreadCategory("r", "t1", Announcements); err != nil { t.Fatal(err) } if err := db.SetThreadCategory("r", "t2", "qa"); err != nil { t.Fatal(err) } cats, err := db.ThreadCategories("r") if err != nil { t.Fatal(err) } if cats["t1"] != Announcements || cats["t2"] != "qa" || len(cats) != 2 { t.Fatalf("categories = %v", cats) } // Empty category uncategorizes; other repos never bleed in. if err := db.SetThreadCategory("r", "t2", ""); err != nil { t.Fatal(err) } if cats, _ = db.ThreadCategories("r"); len(cats) != 1 { t.Fatalf("after uncategorize: %v", cats) } if other, _ := db.ThreadCategories("other"); len(other) != 0 { t.Fatalf("cross-repo bleed: %v", other) } } func TestPurgeRepoSweepsCategories(t *testing.T) { db := testDB(t) if err := db.SetThreadCategory("gone", "t1", Announcements); err != nil { t.Fatal(err) } if err := db.PurgeRepo("gone"); err != nil { t.Fatal(err) } if cats, _ := db.ThreadCategories("gone"); len(cats) != 0 { t.Fatalf("categories survived purge: %v", cats) } } func TestPollBallotRoundTrip(t *testing.T) { db := testDB(t) must := func(err error) { t.Helper() if err != nil { t.Fatal(err) } } must(db.SetPollVote("r", "alice", "p1", 0)) must(db.SetPollVote("r", "bob", "p1", 1)) must(db.SetPollVote("r", "carol", "p1", 1)) counts, err := db.PollCounts("r", "p1") if err != nil { t.Fatal(err) } if counts[0] != 1 || counts[1] != 2 { t.Fatalf("counts = %v", counts) } // One ballot per account: changing a mind replaces, never adds. must(db.SetPollVote("r", "alice", "p1", 1)) counts, _ = db.PollCounts("r", "p1") if counts[0] != 0 || counts[1] != 3 { t.Fatalf("after change: %v", counts) } if c, ok := db.PollChoice("r", "alice", "p1"); !ok || c != 1 { t.Fatalf("alice's ballot = %d ok=%v", c, ok) } if _, ok := db.PollChoice("r", "", "p1"); ok { t.Fatal("anonymous has a ballot") } } func TestPurgeRepoSweepsPollVotes(t *testing.T) { db := testDB(t) if err := db.SetPollVote("gone", "alice", "p1", 0); err != nil { t.Fatal(err) } if err := db.PurgeRepo("gone"); err != nil { t.Fatal(err) } if counts, _ := db.PollCounts("gone", "p1"); len(counts) != 0 { t.Fatalf("ballots survived purge: %v", counts) } }