github.com/gnolang/gno@v0.0.0-20240520182011-228e9d0192ce/examples/gno.land/p/demo/memeland/memeland_test.gno (about) 1 package memeland 2 3 import ( 4 "std" 5 "strings" 6 "testing" 7 "time" 8 9 "gno.land/p/demo/testutils" 10 "gno.land/p/demo/ufmt" 11 ) 12 13 func TestPostMeme(t *testing.T) { 14 m := NewMemeland() 15 id := m.PostMeme("Test meme data", time.Now().Unix()) 16 if id == "" { 17 t.Error("Expected valid ID, got empty string") 18 } 19 } 20 21 func TestGetPostsInRangePagination(t *testing.T) { 22 m := NewMemeland() 23 now := time.Now() 24 25 numOfPosts := 5 26 var memeData []string 27 for i := 1; i <= numOfPosts; i++ { 28 // Prepare meme data 29 nextTime := now.Add(time.Duration(i) * time.Minute) 30 data := ufmt.Sprintf("Meme #%d", i) 31 memeData = append(memeData, data) 32 33 m.PostMeme(data, nextTime.Unix()) 34 } 35 36 // Get timestamps 37 beforeEarliest := now.Add(-1 * time.Minute) 38 afterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute) 39 40 testCases := []struct { 41 page int 42 pageSize int 43 expectedNumOfPosts int 44 }{ 45 {page: 1, pageSize: 1, expectedNumOfPosts: 1}, // one per page 46 {page: 2, pageSize: 1, expectedNumOfPosts: 1}, // one on second page 47 {page: 1, pageSize: numOfPosts, expectedNumOfPosts: numOfPosts}, // all posts on single page 48 {page: 12, pageSize: 1, expectedNumOfPosts: 0}, // empty page 49 {page: 1, pageSize: numOfPosts + 1, expectedNumOfPosts: numOfPosts}, // page with fewer posts than its size 50 {page: 5, pageSize: numOfPosts / 5, expectedNumOfPosts: 1}, // evenly distribute posts per page 51 } 52 53 for _, tc := range testCases { 54 t.Run(ufmt.Sprintf("Page%d_Size%d", tc.page, tc.pageSize), func(t *testing.T) { 55 result := m.GetPostsInRange(beforeEarliest.Unix(), afterLatest.Unix(), tc.page, tc.pageSize, "DATE_CREATED") 56 57 // Count posts by how many times id: shows up in JSON string 58 postCount := strings.Count(result, `"id":"`) 59 if postCount != tc.expectedNumOfPosts { 60 t.Errorf("Expected %d posts in the JSON string, but found %d", tc.expectedNumOfPosts, postCount) 61 } 62 }) 63 } 64 } 65 66 func TestGetPostsInRangeByTimestamp(t *testing.T) { 67 m := NewMemeland() 68 now := time.Now() 69 70 numOfPosts := 5 71 var memeData []string 72 for i := 1; i <= numOfPosts; i++ { 73 // Prepare meme data 74 nextTime := now.Add(time.Duration(i) * time.Minute) 75 data := ufmt.Sprintf("Meme #%d", i) 76 memeData = append(memeData, data) 77 78 m.PostMeme(data, nextTime.Unix()) 79 } 80 81 // Get timestamps 82 beforeEarliest := now.Add(-1 * time.Minute) 83 afterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute) 84 85 // Default sort is by addition order/timestamp 86 jsonStr := m.GetPostsInRange( 87 beforeEarliest.Unix(), // start at earliest post 88 afterLatest.Unix(), // end at latest post 89 1, // first page 90 numOfPosts, // all memes on the page 91 "DATE_CREATED", // sort by newest first 92 ) 93 94 if jsonStr == "" { 95 t.Error("Expected non-empty JSON string, got empty string") 96 } 97 98 // Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering 99 postCount := strings.Count(jsonStr, `"id":"`) 100 if postCount != m.MemeCounter { 101 t.Errorf("Expected %d posts in the JSON string, but found %d", m.MemeCounter, postCount) 102 } 103 104 // Check if data is there 105 for _, expData := range memeData { 106 if !strings.Contains(jsonStr, expData) { 107 t.Errorf("Expected %s in the JSON string, but counld't find it", expData) 108 } 109 } 110 111 // Check if ordering is correct, sort by created date 112 for i := 0; i < len(memeData)-2; i++ { 113 if strings.Index(jsonStr, memeData[i]) < strings.Index(jsonStr, memeData[i+1]) { 114 t.Errorf("Expected %s to be before %s, but was at %d, and %d", memeData[i], memeData[i+1], i, i+1) 115 } 116 } 117 } 118 119 func TestGetPostsInRangeByUpvote(t *testing.T) { 120 m := NewMemeland() 121 now := time.Now() 122 123 memeData1 := "Meme #1" 124 memeData2 := "Meme #2" 125 126 // Create posts at specific times for testing 127 id1 := m.PostMeme(memeData1, now.Unix()) 128 id2 := m.PostMeme(memeData2, now.Add(time.Minute).Unix()) 129 130 m.Upvote(id1) 131 m.Upvote(id2) 132 133 // Change caller so avoid double upvote panic 134 std.TestSetOrigCaller(testutils.TestAddress("alice")) 135 m.Upvote(id1) 136 137 // Final upvote count: 138 // Meme #1 - 2 upvote 139 // Meme #2 - 1 upvotes 140 141 // Get timestamps 142 beforeEarliest := now.Add(-time.Minute) 143 afterLatest := now.Add(time.Hour) 144 145 // Default sort is by addition order/timestamp 146 jsonStr := m.GetPostsInRange( 147 beforeEarliest.Unix(), // start at earliest post 148 afterLatest.Unix(), // end at latest post 149 1, // first page 150 2, // all memes on the page 151 "UPVOTES", // sort by upvote 152 ) 153 154 if jsonStr == "" { 155 t.Error("Expected non-empty JSON string, got empty string") 156 } 157 158 // Count the number of posts returned in the JSON string as a rudimentary check for correct pagination/filtering 159 postCount := strings.Count(jsonStr, `"id":"`) 160 if postCount != m.MemeCounter { 161 t.Errorf("Expected %d posts in the JSON string, but found %d", m.MemeCounter, postCount) 162 } 163 164 // Check if ordering is correct 165 if strings.Index(jsonStr, "Meme #1") > strings.Index(jsonStr, "Meme #2") { 166 t.Errorf("Expected %s to be before %s", memeData1, memeData2) 167 } 168 } 169 170 func TestBadSortBy(t *testing.T) { 171 m := NewMemeland() 172 now := time.Now() 173 174 numOfPosts := 5 175 var memeData []string 176 for i := 1; i <= numOfPosts; i++ { 177 // Prepare meme data 178 nextTime := now.Add(time.Duration(i) * time.Minute) 179 data := ufmt.Sprintf("Meme #%d", i) 180 memeData = append(memeData, data) 181 182 m.PostMeme(data, nextTime.Unix()) 183 } 184 185 // Get timestamps 186 beforeEarliest := now.Add(-1 * time.Minute) 187 afterLatest := now.Add(time.Duration(numOfPosts)*time.Minute + time.Minute) 188 189 tests := []struct { 190 name string 191 sortBy string 192 wantPanic string 193 }{ 194 { 195 name: "Empty sortBy", 196 sortBy: "", 197 wantPanic: "runtime error: index out of range", 198 }, 199 { 200 name: "Wrong sortBy", 201 sortBy: "random string", 202 wantPanic: "", 203 }, 204 } 205 206 for _, tc := range tests { 207 t.Run(tc.name, func(t *testing.T) { 208 defer func() { 209 if r := recover(); r == nil { 210 t.Errorf("code did not panic when it should have") 211 } 212 }() 213 214 // Panics should be caught 215 _ = m.GetPostsInRange(beforeEarliest.Unix(), afterLatest.Unix(), 1, 1, tc.sortBy) 216 }) 217 } 218 } 219 220 func TestNoPosts(t *testing.T) { 221 m := NewMemeland() 222 223 // Add a post to Memeland 224 now := time.Now().Unix() 225 226 jsonStr := m.GetPostsInRange(0, now, 1, 1, "DATE_CREATED") 227 228 if jsonStr != "[]" { 229 t.Errorf("Expected 0 posts to return [], got %s", jsonStr) 230 } 231 } 232 233 func TestUpvote(t *testing.T) { 234 m := NewMemeland() 235 236 // Add a post to Memeland 237 now := time.Now().Unix() 238 postID := m.PostMeme("Test meme data", now) 239 240 // Initial upvote count should be 0 241 post := m.getPost(postID) 242 243 if post.UpvoteTracker.Size() != 0 { 244 t.Errorf("Expected initial upvotes to be 0, got %d", post.UpvoteTracker.Size()) 245 } 246 247 // Upvote the post 248 upvoteResult := m.Upvote(postID) 249 if upvoteResult != "upvote successful" { 250 t.Errorf("Expected upvote to be successful, got: %s", upvoteResult) 251 } 252 253 // Retrieve the post again and check the upvote count 254 post = m.getPost(postID) 255 256 if post.UpvoteTracker.Size() != 1 { 257 t.Errorf("Expected upvotes to be 1 after upvoting, got %d", post.UpvoteTracker.Size()) 258 } 259 } 260 261 func TestDelete(t *testing.T) { 262 alice := testutils.TestAddress("alice") 263 std.TestSetOrigCaller(alice) 264 265 // Alice is admin 266 m := NewMemeland() 267 268 // Set caller to Bob 269 bob := testutils.TestAddress("bob") 270 std.TestSetOrigCaller(bob) 271 272 // Bob adds post to Memeland 273 now := time.Now() 274 postID := m.PostMeme("Meme #1", now.Unix()) 275 276 // Alice removes Bob's post 277 std.TestSetOrigCaller(alice) 278 279 id := m.RemovePost(postID) 280 if id != postID { 281 t.Errorf("post IDs not matching") 282 } 283 284 if len(m.Posts) != 0 { 285 t.Errorf("there should be 0 posts after removing") 286 } 287 } 288 289 func TestDeleteByNonAdmin(t *testing.T) { 290 alice := testutils.TestAddress("alice") 291 std.TestSetOrigCaller(alice) 292 293 m := NewMemeland() 294 295 // Add a post to Memeland 296 now := time.Now() 297 postID := m.PostMeme("Meme #1", now.Unix()) 298 299 // Bob will try to delete meme posted by Alice, which should fail 300 bob := testutils.TestAddress("bob") 301 std.TestSetOrigCaller(bob) 302 303 defer func() { 304 if r := recover(); r == nil { 305 t.Errorf("code did not panic when it should have") 306 } 307 }() 308 309 // Should panic - caught by defer 310 m.RemovePost(postID) 311 }