Skip to content
quarry Artifact [2d4c4f95b0]
Login

Artifact [2d4c4f95b0]

Artifact 2d4c4f95b010856250354add99a3000ff85414a6bfaf536455b410f70460504e:


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)
	}
}