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}