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.

microblog_test.gno

3.95 Kb · 160 lines
  1package microblog
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/testutils/v0"
  8	"gno.land/p/nt/uassert/v0"
  9)
 10
 11// resetWall clears package state between tests (globals persist in-process,
 12// and subtests do not reset them — do it explicitly).
 13func resetWall() { posts = nil }
 14
 15// asUser must be inlined into each test: SetRealm affects the calling frame
 16// only. We therefore call it directly in the test bodies below.
 17
 18func TestPostAndCount(cur realm, t *testing.T) {
 19	resetWall()
 20
 21	alice := testutils.TestAddress("alice")
 22	testing.SetRealm(testing.NewUserRealm(alice))
 23	Post(cross(cur), "hello wall")
 24
 25	if got := Count(); got != 1 {
 26		t.Fatalf("Count() = %d, want 1", got)
 27	}
 28	if posts[0].Msg != "hello wall" {
 29		t.Errorf("Msg = %q, want %q", posts[0].Msg, "hello wall")
 30	}
 31	if posts[0].Author != alice.String() {
 32		t.Errorf("Author = %q, want %q", posts[0].Author, alice.String())
 33	}
 34}
 35
 36func TestPostTooLongPanics(cur realm, t *testing.T) {
 37	resetWall()
 38	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("bob")))
 39
 40	// Aborts from a cross(cur) call cannot be caught by a plain recover();
 41	// assert on the abort message instead.
 42	uassert.AbortsWithMessage(t, cur, "microblog: message too long (max 280 bytes)", func() {
 43		Post(cross(cur), strings.Repeat("x", maxMsgLen+1))
 44	})
 45}
 46
 47func TestPostEmptyPanics(cur realm, t *testing.T) {
 48	resetWall()
 49	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("bob")))
 50
 51	// Aborts from a cross(cur) call cannot be caught by a plain recover();
 52	// assert on the abort message instead.
 53	uassert.AbortsWithMessage(t, cur, "microblog: empty message", func() {
 54		Post(cross(cur), "")
 55	})
 56}
 57
 58func TestPostMaxLenOK(cur realm, t *testing.T) {
 59	resetWall()
 60	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("carol")))
 61
 62	// Exactly maxMsgLen bytes must be accepted.
 63	Post(cross(cur), strings.Repeat("y", maxMsgLen))
 64	if got := Count(); got != 1 {
 65		t.Fatalf("Count() = %d, want 1 for exactly-max message", got)
 66	}
 67}
 68
 69func TestRenderEmpty(t *testing.T) {
 70	resetWall()
 71	out := Render("")
 72	if !strings.Contains(out, "No posts yet") {
 73		t.Errorf("empty Render should mention no posts, got:\n%s", out)
 74	}
 75}
 76
 77func TestRenderNewestFirstAndPagination(cur realm, t *testing.T) {
 78	resetWall()
 79	testing.SetRealm(testing.NewUserRealm(testutils.TestAddress("dave")))
 80
 81	// 25 posts -> 2 pages (20 + 5).
 82	for i := 0; i < 25; i++ {
 83		Post(cross(cur), "msg"+itoa(i))
 84	}
 85
 86	if got := Count(); got != 25 {
 87		t.Fatalf("Count() = %d, want 25", got)
 88	}
 89
 90	page1 := Render("")
 91	// Newest (msg24) must appear before older (msg5) on page 1.
 92	i24 := strings.Index(page1, "msg24")
 93	i5 := strings.Index(page1, "msg5")
 94	if i24 < 0 || i5 < 0 {
 95		t.Fatalf("page 1 missing expected posts: msg24=%d msg5=%d", i24, i5)
 96	}
 97	if i24 > i5 {
 98		t.Errorf("page 1 not newest-first: msg24 at %d should precede msg5 at %d", i24, i5)
 99	}
100	if !strings.Contains(page1, "page 1 of 2") {
101		t.Errorf("page 1 header wrong, got:\n%s", page1)
102	}
103
104	// Page 2 via query form must contain the oldest post.
105	page2 := Render("?page=2")
106	if !strings.Contains(page2, "msg0") {
107		t.Errorf("page 2 should contain oldest msg0, got:\n%s", page2)
108	}
109	if !strings.Contains(page2, "page 2 of 2") {
110		t.Errorf("page 2 header wrong, got:\n%s", page2)
111	}
112
113	// Path form "/2" should equal query form "?page=2".
114	if Render("/2") != page2 {
115		t.Errorf("path form /2 differs from query form ?page=2")
116	}
117}
118
119func TestParsePage(t *testing.T) {
120	cases := []struct {
121		in   string
122		want int
123	}{
124		{"", 1},
125		{"?page=3", 3},
126		{"?p=4", 4},
127		{"/2", 2},
128		{"5", 5},
129		{"?page=abc", 1},
130		{"garbage", 1},
131	}
132	for _, c := range cases {
133		if got := parsePage(c.in); got != c.want {
134			t.Errorf("parsePage(%q) = %d, want %d", c.in, got, c.want)
135		}
136	}
137}
138
139// itoa is a tiny local int->string helper for building test messages.
140func itoa(n int) string {
141	if n == 0 {
142		return "0"
143	}
144	neg := n < 0
145	if neg {
146		n = -n
147	}
148	var buf [20]byte
149	i := len(buf)
150	for n > 0 {
151		i--
152		buf[i] = byte('0' + n%10)
153		n /= 10
154	}
155	if neg {
156		i--
157		buf[i] = '-'
158	}
159	return string(buf[i:])
160}