polls_test.gno
10.82 Kb ยท 445 lines
1package polls
2
3import (
4 "testing"
5 "time"
6
7 "gno.land/p/nt/avl"
8 "gno.land/p/nt/seqid"
9 "gno.land/p/nt/testutils"
10 pollsv1 "gno.land/p/zenao/polls/v1"
11 "gno.land/p/zenao/realmid"
12)
13
14var (
15 alice = testutils.TestAddress("alice")
16 bob = testutils.TestAddress("bob")
17 carol = testutils.TestAddress("carol")
18)
19
20func TestNewPoll(t *testing.T) {
21 setupTest()
22
23 type input struct {
24 question string
25 options []string
26 duration int64
27 }
28
29 type output struct {
30 panic bool
31 }
32
33 type test struct {
34 input input
35 output output
36 }
37
38 type testTable = map[string]test
39
40 tests := testTable{
41 "valid poll": {
42 input: input{
43 question: "What is your favorite color?",
44 options: []string{"red", "blue", "green"},
45 duration: int64(time.Minute) * 30 / int64(time.Second),
46 },
47 output: output{
48 panic: false,
49 },
50 },
51 "not enough options": {
52 input: input{
53 question: "What is your favorite color?",
54 options: []string{"red"},
55 duration: int64(time.Minute) * 30 / int64(time.Second),
56 },
57 output: output{
58 panic: true,
59 },
60 },
61 "too many options": {
62 input: input{
63 question: "What is your favorite color?",
64 options: []string{"red", "blue", "green", "yellow", "orange", "purple", "pink", "brown", "black"},
65 duration: int64(time.Minute) * 30 / int64(time.Second),
66 },
67 output: output{
68 panic: true,
69 },
70 },
71 "empty option": {
72 input: input{
73 question: "What is your favorite color?",
74 options: []string{"red", "", "green"},
75 duration: int64(time.Minute) * 30 / int64(time.Second),
76 },
77 output: output{
78 panic: true,
79 },
80 },
81 "long option": {
82 input: input{
83 question: "What is your favorite color?",
84 options: []string{"red", "blue", "green", "yellow", "orange", "purple", "pink", "brown", "black", "this option is too too too too too too long"},
85 duration: int64(time.Minute) * 30 / int64(time.Second),
86 },
87 output: output{
88 panic: true,
89 },
90 },
91 "duplicate option": {
92 input: input{
93 question: "What is your favorite color?",
94 options: []string{"red", "blue", "green", "blue"},
95 duration: int64(time.Minute) * 30 / int64(time.Second),
96 },
97 output: output{
98 panic: true,
99 },
100 },
101 "duration less than 15 minutes": {
102 input: input{
103 question: "What is your favorite color?",
104 options: []string{"red", "blue", "green"},
105 duration: int64(time.Minute) * 10 / int64(time.Second),
106 },
107 output: output{
108 panic: true,
109 },
110 },
111 "duration more than 1 month": {
112 input: input{
113 question: "What is your favorite color?",
114 options: []string{"red", "blue", "green"},
115 duration: int64(time.Hour) * 24 * 30 * 2 / int64(time.Second),
116 },
117 output: output{
118 panic: true,
119 },
120 },
121 }
122
123 for name, test := range tests {
124 t.Run(name, func(t *testing.T) {
125 r := revive(func() {
126 NewPoll(cross, test.input.question, pollsv1.POLL_KIND_MULTIPLE_CHOICE, test.input.duration, test.input.options, nil)
127 })
128 if r != nil && !test.output.panic {
129 t.Errorf("unexpected panic: %v", r)
130 }
131 if r == nil && test.output.panic {
132 t.Errorf("expected panic")
133 }
134 })
135 }
136}
137
138func TestVote(t *testing.T) {
139 setupTest()
140
141 type input struct {
142 id uint64
143 option string
144 caller address
145 }
146
147 type output struct {
148 choicesToHave []string
149 choicesToNotHave []string
150 panic bool
151 }
152
153 type test struct {
154 input input
155 output output
156 }
157
158 type testTable = map[string]test
159
160 pvFunc := func() (string, bool) {
161 callerID := realmid.Previous()
162 if callerID == bob.String() {
163 return callerID, true
164 }
165 return callerID, false
166 }
167 _ = NewPoll(cross, "What is your favorite color?", pollsv1.POLL_KIND_MULTIPLE_CHOICE, int64(time.Hour)/int64(time.Second), []string{"red", "blue", "green"}, nil)
168 _ = NewPoll(cross, "What is your favorite color?", pollsv1.POLL_KIND_MULTIPLE_CHOICE, int64(time.Hour)/int64(time.Second), []string{"red", "blue", "green"}, pvFunc)
169 _ = NewPoll(cross, "What is your favorite color?", pollsv1.POLL_KIND_SINGLE_CHOICE, int64(time.Hour)/int64(time.Second), []string{"red", "blue", "green"}, nil)
170
171 tests := testTable{
172 "valid vote": {
173 input: input{
174 id: 1,
175 option: "red",
176 caller: alice,
177 },
178 output: output{
179 choicesToHave: []string{"red"},
180 choicesToNotHave: []string{"blue", "green"},
181 panic: false,
182 },
183 },
184 "valid vote 2": {
185 input: input{
186 id: 1,
187 option: "blue",
188 caller: alice,
189 },
190 output: output{
191 choicesToHave: []string{"blue", "red"},
192 choicesToNotHave: []string{"green"},
193 panic: false,
194 },
195 },
196 /*
197 "invalid option": {
198 input: input{
199 id: 1,
200 option: "yellow",
201 caller: alice,
202 },
203 output: output{
204 panic: true,
205 },
206 },
207 */
208 "remove vote": {
209 input: input{
210 id: 1,
211 option: "red",
212 caller: alice,
213 },
214 output: output{
215 choicesToHave: []string{"blue"},
216 choicesToNotHave: []string{"red", "green"},
217 panic: false,
218 },
219 },
220 /*
221 "poll not found": {
222 input: input{
223 id: 4,
224 option: "red",
225 caller: alice,
226 },
227 output: output{
228 panic: true,
229 },
230 },
231 "private poll false": {
232 input: input{
233 id: 2,
234 option: "red",
235 caller: alice,
236 },
237 output: output{
238 panic: true,
239 },
240 },
241 */
242 "private poll true": {
243 input: input{
244 id: 2,
245 option: "red",
246 caller: bob,
247 },
248 output: output{
249 choicesToHave: []string{"red"},
250 choicesToNotHave: []string{"blue", "green"},
251 panic: false,
252 },
253 },
254 "valid single vote": {
255 input: input{
256 id: 3,
257 option: "red",
258 caller: alice,
259 },
260 output: output{
261 choicesToHave: []string{"red"},
262 choicesToNotHave: []string{"blue", "green"},
263 panic: false,
264 },
265 },
266 "multiple single vote": {
267 input: input{
268 id: 3,
269 option: "blue",
270 caller: alice,
271 },
272 output: output{
273 choicesToHave: []string{"blue"},
274 choicesToNotHave: []string{"red", "green"},
275 panic: false,
276 },
277 },
278 }
279
280 for name, test := range tests {
281 t.Run(name, func(t *testing.T) {
282 testing.SetOriginCaller(test.input.caller)
283 Vote(cross, test.input.id, test.input.option)
284 id := seqid.ID(test.input.id)
285 pollRaw, ok := polls.Get(id.String())
286 if !ok {
287 t.Errorf("poll not found")
288 }
289 poll := pollRaw.(*Poll)
290 poll.Results.Iterate("", "", func(option string, votes interface{}) bool {
291 votesTree := votes.(*avl.Tree)
292 if votesTree.Has(test.input.caller.String()) {
293 if !stringInSlice(option, test.output.choicesToHave) {
294 t.Errorf("the choice %q is not expected", option)
295 }
296 } else {
297 if !stringInSlice(option, test.output.choicesToNotHave) {
298 t.Errorf("the choice %q is expected", option)
299 }
300 }
301 return false
302 })
303 })
304 }
305}
306
307func TestGetInfo(t *testing.T) {
308 setupTest()
309 type input struct {
310 id uint64
311 user address
312 }
313
314 type output struct {
315 info *pollsv1.Poll
316 }
317
318 type test struct {
319 input input
320 output output
321 }
322
323 type testTable = map[string]test
324
325 poll := NewPoll(cross, "What is your favorite color?", pollsv1.POLL_KIND_MULTIPLE_CHOICE, int64(time.Hour)/int64(time.Second), []string{"red", "blue", "green"}, nil)
326 emptyPoll := NewPoll(cross, "What is your favorite color?", pollsv1.POLL_KIND_MULTIPLE_CHOICE, int64(time.Hour)/int64(time.Second), []string{"red", "blue", "green"}, nil)
327 testing.SetOriginCaller(alice)
328 Vote(cross, 1, "blue")
329 testing.SetOriginCaller(bob)
330 Vote(cross, 1, "blue")
331 testing.SetOriginCaller(carol)
332 Vote(cross, 1, "red")
333
334 tests := testTable{
335 "valid poll alice": {
336 input: input{
337 id: 1,
338 user: alice,
339 },
340 output: output{
341 info: &pollsv1.Poll{
342 Question: "What is your favorite color?",
343 Results: []*pollsv1.PollResult{
344 {Option: "blue", Count: 2, HasUserVoted: true},
345 {Option: "green", Count: 0, HasUserVoted: false},
346 {Option: "red", Count: 1, HasUserVoted: false},
347 },
348 Kind: pollsv1.POLL_KIND_MULTIPLE_CHOICE,
349 Duration: int64(time.Hour) / int64(time.Second),
350 CreatedAt: poll.CreatedAt,
351 CreatedBy: poll.CreatedBy,
352 },
353 },
354 },
355 "valid poll carol": {
356 input: input{
357 id: 1,
358 user: carol,
359 },
360 output: output{
361 info: &pollsv1.Poll{
362 Question: "What is your favorite color?",
363 Results: []*pollsv1.PollResult{
364 {Option: "blue", Count: 2, HasUserVoted: false},
365 {Option: "green", Count: 0, HasUserVoted: false},
366 {Option: "red", Count: 1, HasUserVoted: true},
367 },
368 Kind: pollsv1.POLL_KIND_MULTIPLE_CHOICE,
369 Duration: int64(time.Hour) / int64(time.Second),
370 CreatedAt: poll.CreatedAt,
371 CreatedBy: poll.CreatedBy,
372 },
373 },
374 },
375 "empty poll": {
376 input: input{
377 id: 2,
378 user: alice,
379 },
380 output: output{
381 info: &pollsv1.Poll{
382 Question: "What is your favorite color?",
383 Results: []*pollsv1.PollResult{
384 {Option: "blue", Count: 0, HasUserVoted: false},
385 {Option: "green", Count: 0, HasUserVoted: false},
386 {Option: "red", Count: 0, HasUserVoted: false},
387 },
388 Kind: pollsv1.POLL_KIND_MULTIPLE_CHOICE,
389 Duration: int64(time.Hour) / int64(time.Second),
390 CreatedAt: emptyPoll.CreatedAt,
391 CreatedBy: emptyPoll.CreatedBy,
392 },
393 },
394 },
395 }
396
397 for name, test := range tests {
398 t.Run(name, func(t *testing.T) {
399 info := GetInfo(test.input.id, test.input.user.String())
400 if info.Question != test.output.info.Question {
401 t.Errorf("expected question %q, got %q", test.output.info.Question, info.Question)
402 }
403 if info.Kind != test.output.info.Kind {
404 t.Errorf("expected poll kind %v, got %v", test.output.info.Kind, info.Kind)
405 }
406 if info.Duration != test.output.info.Duration {
407 t.Errorf("expected duration %v, got %v", test.output.info.Duration, info.Duration)
408 }
409 if info.CreatedAt != test.output.info.CreatedAt {
410 t.Errorf("expected created at %v, got %v", test.output.info.CreatedAt, info.CreatedAt)
411 }
412 if info.CreatedBy != test.output.info.CreatedBy {
413 t.Errorf("expected created by %q, got %q", test.output.info.CreatedBy, info.CreatedBy)
414 }
415 if len(info.Results) != len(test.output.info.Results) {
416 t.Errorf("expected %d results, got %d", len(test.output.info.Results), len(info.Results))
417 }
418 for i, result := range info.Results {
419 if result.Option != test.output.info.Results[i].Option {
420 t.Errorf("expected option %q, got %q", test.output.info.Results[i].Option, result.Option)
421 }
422 if result.Count != test.output.info.Results[i].Count {
423 t.Errorf("expected votes %d, got %d", test.output.info.Results[i].Count, result.Count)
424 }
425 if result.HasUserVoted != test.output.info.Results[i].HasUserVoted {
426 t.Errorf("expected has user voted %v, got %v", test.output.info.Results[i].HasUserVoted, result.HasUserVoted)
427 }
428 }
429 })
430 }
431}
432
433func setupTest() {
434 polls = avl.NewTree()
435 id = seqid.ID(0)
436}
437
438func stringInSlice(a string, list []string) bool {
439 for _, b := range list {
440 if b == a {
441 return true
442 }
443 }
444 return false
445}