todos_test.gno
3.64 Kb · 127 lines
1package todos
2
3import (
4 "strings"
5 "testing"
6
7 "gno.land/p/moul/kit/store/v0"
8 "gno.land/p/nt/testutils/v0"
9)
10
11// reset clears package globals between tests (t.Run/subtests do not reset them).
12func reset() {
13 items = store.Named("todos: item")
14}
15
16// TestAddToggleRemove exercises the full lifecycle through the crossing API.
17// Crossing test: takes `cur realm` and calls via cross(cur).
18func TestAddToggleRemove(cur realm, t *testing.T) {
19 reset()
20 alice := testutils.TestAddress("alice")
21 testing.SetRealm(testing.NewUserRealm(alice))
22
23 id1 := Add(cross(cur), "buy milk")
24 if id1 != 1 {
25 t.Fatalf("Add first id = %d, want 1", id1)
26 }
27 id2 := Add(cross(cur), "walk dog")
28 if id2 != 2 {
29 t.Fatalf("Add second id = %d, want 2", id2)
30 }
31
32 open, done := counts()
33 if open != 2 || done != 0 {
34 t.Fatalf("after adds: open=%d done=%d, want 2/0", open, done)
35 }
36
37 // Toggle #1 done.
38 Toggle(cross(cur), 1)
39 open, done = counts()
40 if open != 1 || done != 1 {
41 t.Fatalf("after toggle: open=%d done=%d, want 1/1", open, done)
42 }
43
44 // Toggle #1 back to open.
45 Toggle(cross(cur), 1)
46 open, done = counts()
47 if open != 2 || done != 0 {
48 t.Fatalf("after re-toggle: open=%d done=%d, want 2/0", open, done)
49 }
50
51 // Remove #1.
52 Remove(cross(cur), 1)
53 open, done = counts()
54 if open != 1 || done != 0 {
55 t.Fatalf("after remove: open=%d done=%d, want 1/0", open, done)
56 }
57 if items.Has(store.ID(1)) {
58 t.Fatalf("item #1 still present after Remove")
59 }
60}
61
62// TestRender checks the Markdown output shape and counts. Render is not a
63// crossing function, so this is a plain test — but the Add calls that seed
64// state still need a realm, so it also takes `cur realm`.
65func TestRender(cur realm, t *testing.T) {
66 reset()
67
68 // Empty board.
69 empty := Render("")
70 if !strings.Contains(empty, "0 open · 0 done · 0 total") {
71 t.Fatalf("empty render missing zero counts:\n%s", empty)
72 }
73 if !strings.Contains(empty, "No items yet") {
74 t.Fatalf("empty render missing placeholder:\n%s", empty)
75 }
76
77 bob := testutils.TestAddress("bob")
78 testing.SetRealm(testing.NewUserRealm(bob))
79
80 Add(cross(cur), "first")
81 Add(cross(cur), "second")
82 Add(cross(cur), "third")
83 Toggle(cross(cur), 2) // mark #2 done
84
85 out := Render("")
86
87 // Counts line: 2 open, 1 done, 3 total.
88 if !strings.Contains(out, "2 open · 1 done · 3 total") {
89 t.Fatalf("render counts wrong:\n%s", out)
90 }
91 // Done checkbox for #2.
92 if !strings.Contains(out, "- [x] #2 second") {
93 t.Fatalf("render missing done item #2:\n%s", out)
94 }
95 // Open checkbox for #1.
96 if !strings.Contains(out, "- [ ] #1 first") {
97 t.Fatalf("render missing open item #1:\n%s", out)
98 }
99 // Newest first: #3 must appear before #1.
100 if strings.Index(out, "#3 third") > strings.Index(out, "#1 first") {
101 t.Fatalf("render not newest-first:\n%s", out)
102 }
103 // Author short form present.
104 if !strings.Contains(out, "(`g1") {
105 t.Fatalf("render missing author short address:\n%s", out)
106 }
107}
108
109// TestIDKeyOrdering verifies the store key sorts numerically. v1 asserted the
110// same thing about its own idKey, which padded to width 12: that held only
111// below the width, since idKey(10^12) is 13 characters and sorts before
112// idKey(10^12-1). The store key is 8 bytes for every id, so there is no
113// ceiling. Non-crossing: plain signature.
114func TestIDKeyOrdering(t *testing.T) {
115 if store.ID(2).Key() >= store.ID(10).Key() {
116 t.Fatal("id 2 should sort before id 10")
117 }
118 if store.ID(9).Key() >= store.ID(100).Key() {
119 t.Fatal("id 9 should sort before id 100")
120 }
121 if store.ID(999999999999).Key() >= store.ID(1000000000000).Key() {
122 t.Fatal("ordering should survive one past v1's width-12 ceiling")
123 }
124 if len(store.ID(1).Key()) != len(store.ID(1000000000000).Key()) {
125 t.Fatal("every key is the same width")
126 }
127}