Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

qvote_test.gno

4.22 Kb · 150 lines
  1package qvote
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/avl/v0"
  8	"gno.land/p/nt/testutils/v0"
  9)
 10
 11func resetState() {
 12	polls = avl.Tree{}
 13	voters = avl.Tree{}
 14	nextID = 0
 15}
 16
 17func TestSquare(t *testing.T) {
 18	cases := []struct {
 19		n, want int64
 20	}{
 21		{0, 0}, {1, 1}, {2, 4}, {3, 9}, {10, 100},
 22	}
 23	for _, c := range cases {
 24		if got := square(c.n); got != c.want {
 25			t.Fatalf("square(%d) = %d, want %d", c.n, got, c.want)
 26		}
 27	}
 28}
 29
 30// Crossing: CreatePoll mutates realm state, so the test needs `cur realm`.
 31func TestCreatePollValidatesOptions(cur realm, t *testing.T) {
 32	resetState()
 33	alice := testutils.TestAddress("alice")
 34	testing.SetRealm(testing.NewUserRealm(alice))
 35
 36	id := CreatePoll(cross(cur), "best color?", "red, green , blue")
 37	if id != "poll #1 created with 3 options" {
 38		t.Fatalf("unexpected message: %q", id)
 39	}
 40	p := getPoll("1")
 41	if len(p.Options) != 3 || p.Options[0] != "red" || p.Options[2] != "blue" {
 42		t.Fatalf("options not parsed/trimmed correctly: %#v", p.Options)
 43	}
 44
 45	if rec := revive(func() { CreatePoll(cross(cur), "one option?", "only-one") }); rec == nil {
 46		t.Fatal("expected panic for too few options")
 47	}
 48	if rec := revive(func() { CreatePoll(cross(cur), "  ", "a,b") }); rec == nil {
 49		t.Fatal("expected panic for empty question")
 50	}
 51}
 52
 53func TestVoteQuadraticCostAndBudget(cur realm, t *testing.T) {
 54	resetState()
 55	alice := testutils.TestAddress("alice")
 56	testing.SetRealm(testing.NewUserRealm(alice))
 57	CreatePoll(cross(cur), "pizza topping?", "pineapple,mushroom,pepperoni")
 58
 59	// 1st vote costs 1, 2nd costs 3 more (4 total), 3rd costs 5 more (9 total).
 60	Vote(cross(cur), "1", 0, 1)
 61	vs := getOrCreateVoter("1", alice, 3)
 62	if vs.CreditsUsed != 1 {
 63		t.Fatalf("credits used = %d, want 1", vs.CreditsUsed)
 64	}
 65	Vote(cross(cur), "1", 0, 2)
 66	if vs.CreditsUsed != 9 || vs.Votes[0] != 3 {
 67		t.Fatalf("after buying to 3 votes: credits=%d votes=%d, want 9/3", vs.CreditsUsed, vs.Votes[0])
 68	}
 69
 70	// Spending the rest of the 100-credit budget on another option should work
 71	// right up to the edge, then fail past it.
 72	Vote(cross(cur), "1", 1, 9) // 81 credits, total 90
 73	if vs.CreditsUsed != 90 {
 74		t.Fatalf("credits used = %d, want 90", vs.CreditsUsed)
 75	}
 76	if rec := revive(func() { Vote(cross(cur), "1", 2, 4) }); rec == nil { // would cost 16 more, total 106 > 100
 77		t.Fatal("expected panic for exceeding voice-credit budget")
 78	}
 79
 80	p := getPoll("1")
 81	if p.Tally[0] != 3 || p.Tally[1] != 9 {
 82		t.Fatalf("unexpected tally: %#v", p.Tally)
 83	}
 84}
 85
 86func TestVoteSellBackRefundsCredits(cur realm, t *testing.T) {
 87	resetState()
 88	alice := testutils.TestAddress("alice")
 89	testing.SetRealm(testing.NewUserRealm(alice))
 90	CreatePoll(cross(cur), "best sea?", "north,baltic")
 91
 92	Vote(cross(cur), "1", 0, 4) // 16 credits, 4 votes
 93	Vote(cross(cur), "1", 0, -1) // back to 3 votes: cost delta = 9-16 = -7
 94
 95	vs := getOrCreateVoter("1", alice, 2)
 96	if vs.Votes[0] != 3 {
 97		t.Fatalf("votes = %d, want 3", vs.Votes[0])
 98	}
 99	if vs.CreditsUsed != 9 {
100		t.Fatalf("credits used = %d, want 9", vs.CreditsUsed)
101	}
102	if getPoll("1").Tally[0] != 3 {
103		t.Fatalf("tally = %d, want 3", getPoll("1").Tally[0])
104	}
105
106	if rec := revive(func() { Vote(cross(cur), "1", 0, -10) }); rec == nil {
107		t.Fatal("expected panic for selling more votes than held")
108	}
109}
110
111func TestClosePollOnlyCreator(cur realm, t *testing.T) {
112	resetState()
113	alice := testutils.TestAddress("alice")
114	bob := testutils.TestAddress("bob")
115
116	testing.SetRealm(testing.NewUserRealm(alice))
117	CreatePoll(cross(cur), "close me?", "yes,no")
118
119	testing.SetRealm(testing.NewUserRealm(bob))
120	if rec := revive(func() { ClosePoll(cross(cur), "1") }); rec == nil {
121		t.Fatal("expected panic when a non-creator closes the poll")
122	}
123
124	testing.SetRealm(testing.NewUserRealm(alice))
125	ClosePoll(cross(cur), "1")
126	if !getPoll("1").Closed {
127		t.Fatal("poll should be closed")
128	}
129
130	if rec := revive(func() { Vote(cross(cur), "1", 0, 1) }); rec == nil {
131		t.Fatal("expected panic when voting on a closed poll")
132	}
133}
134
135func TestRenderNoPolls(t *testing.T) {
136	resetState()
137	out := Render("")
138	if !strings.Contains(out, "no polls yet") {
139		t.Fatalf("expected empty-state message, got: %s", out)
140	}
141}
142
143func TestRenderPollNotFound(t *testing.T) {
144	resetState()
145	out := Render("42")
146	if !strings.Contains(out, "No such poll") {
147		t.Fatalf("expected not-found message, got: %s", out)
148	}
149}
150