posts_test.gno

10.70 Kb ยท 553 lines
  1package social_feed
  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	feedsv1 "gno.land/p/zenao/feeds/v1"
 11)
 12
 13var (
 14	alice = testutils.TestAddress("alice")
 15	bob   = testutils.TestAddress("bob")
 16)
 17
 18func TestNewPost(t *testing.T) {
 19	setupTest()
 20	type input struct {
 21		feedId string
 22		post   *feedsv1.Post
 23	}
 24
 25	type output struct {
 26		localPostId uint64
 27		panic       bool
 28	}
 29
 30	type test struct {
 31		input  input
 32		output output
 33	}
 34
 35	type testTable = map[string]test
 36
 37	tests := testTable{
 38		"valid post": {
 39			input: input{
 40				feedId: "public",
 41				post:   &feedsv1.Post{},
 42			},
 43			output: output{
 44				localPostId: 1,
 45				panic:       false,
 46			},
 47		},
 48		/*
 49			"private feed": {
 50				input: input{
 51					feedId: alice.String() + ":private",
 52					post:   &feedsv1.Post{},
 53				},
 54				output: output{
 55					panic: true,
 56				},
 57			},
 58		*/
 59	}
 60
 61	testing.SetContext(testing.Context{
 62		CurrentRealm: testing.NewUserRealm(alice),
 63	})
 64	pvFunc := func() (string, bool) { return "", false }
 65	NewFeed(cross, "private", false, pvFunc)
 66
 67	for name, tc := range tests {
 68		t.Run(name, func(t *testing.T) {
 69			if tc.output.panic {
 70				defer func() {
 71					if r := recover(); r == nil {
 72						t.Errorf("expected panic")
 73					}
 74				}()
 75			}
 76			got := NewPost(cross, tc.input.feedId, tc.input.post)
 77			if !tc.output.panic && got != tc.output.localPostId {
 78				t.Errorf("expected %q, got %q", tc.output.localPostId, got)
 79			}
 80		})
 81	}
 82}
 83
 84func TestGetPost(t *testing.T) {
 85	setupTest()
 86
 87	type input struct {
 88		localPostId uint64
 89	}
 90
 91	type output struct {
 92		post  *feedsv1.Post
 93		panic bool
 94	}
 95
 96	type test struct {
 97		input  input
 98		output output
 99	}
100
101	type testTable = map[string]test
102
103	validPost := &feedsv1.Post{
104		Author:    alice.String(),
105		DeletedAt: 0,
106		Post:      &feedsv1.StandardPost{},
107	}
108
109	tests := testTable{
110		"valid post": {
111			input: input{
112				localPostId: 1,
113			},
114			output: output{
115				post:  validPost,
116				panic: false,
117			},
118		},
119		/*
120			"invalid post": {
121				input: input{
122					localPostId: 2,
123				},
124				output: output{
125					panic: true,
126				},
127			},
128		*/
129	}
130
131	testing.SetContext(testing.Context{
132		CurrentRealm: testing.NewUserRealm(alice),
133	})
134	NewPost(cross, "public", validPost)
135
136	for name, tc := range tests {
137		t.Run(name, func(t *testing.T) {
138			if tc.output.panic {
139				defer func() {
140					if r := recover(); r == nil {
141						t.Errorf("expected panic")
142					}
143				}()
144			}
145			got := GetPost(tc.input.localPostId)
146			if !tc.output.panic && got != tc.output.post {
147				t.Errorf("expected %v, got %v", tc.output.post, got)
148			}
149		})
150	}
151}
152
153func TestDeletePost(t *testing.T) {
154	setupTest()
155
156	type input struct {
157		localPostId uint64
158		caller      address
159	}
160
161	type output struct {
162		panic bool
163	}
164
165	type test struct {
166		input  input
167		output output
168	}
169
170	type testTable = map[string]test
171
172	tests := testTable{
173		"valid post": {
174			input: input{
175				localPostId: 1,
176				caller:      alice,
177			},
178			output: output{
179				panic: false,
180			},
181		},
182		/*
183			"invalid post": {
184				input: input{
185					localPostId: 2,
186					caller:      alice,
187				},
188				output: output{
189					panic: true,
190				},
191			},
192			"not the creator": {
193				input: input{
194					localPostId: 1,
195					caller:      bob,
196				},
197				output: output{
198					panic: true,
199				},
200			},
201			"already deleted": {
202				input: input{
203					localPostId: 1,
204					caller:      alice,
205				},
206				output: output{
207					panic: true,
208				},
209			},
210		*/
211	}
212
213	post := &feedsv1.Post{
214		Author:    alice.String(),
215		DeletedAt: 0,
216		Post:      &feedsv1.StandardPost{},
217	}
218
219	testing.SetContext(testing.Context{
220		CurrentRealm: testing.NewUserRealm(alice),
221	})
222	NewPost(cross, "public", post)
223
224	for name, tc := range tests {
225		t.Run(name, func(t *testing.T) {
226			if tc.output.panic {
227				defer func() {
228					if r := recover(); r == nil {
229						t.Errorf("expected panic")
230					}
231				}()
232			}
233
234			testing.SetContext(testing.Context{
235				Time: time.Unix(1757944096, 0),
236			})
237			testing.SetOriginCaller(tc.input.caller)
238			DeletePost(cross, tc.input.localPostId)
239			got := GetPost(tc.input.localPostId)
240			if !tc.output.panic && got.DeletedAt != time.Now().Unix() {
241				t.Errorf("expected deleted at %d, got %d", time.Now().Unix(), got.DeletedAt)
242			}
243		})
244	}
245}
246
247func TestEditPost(t *testing.T) {
248	setupTest()
249
250	type input struct {
251		localPostId uint64
252		newPost     *feedsv1.Post
253	}
254
255	type output struct {
256		post    *feedsv1.Post
257		panic   bool
258		updated bool
259	}
260
261	type test struct {
262		input  input
263		output output
264	}
265
266	type testTable = map[string]test
267
268	post := &feedsv1.Post{
269		Author:    alice.String(),
270		DeletedAt: 0,
271		UpdatedAt: 0,
272		Tags:      []string{"tag1", "tag2"},
273		Post:      &feedsv1.StandardPost{Content: "original content"},
274	}
275
276	deletedPost := &feedsv1.Post{
277		Author:    alice.String(),
278		DeletedAt: 1,
279		UpdatedAt: 0,
280		Tags:      []string{"tag1", "tag2"},
281		Post:      &feedsv1.StandardPost{Content: "original content"},
282	}
283
284	bobPost := &feedsv1.Post{
285		Author:    bob.String(),
286		DeletedAt: 0,
287		UpdatedAt: 0,
288		Tags:      []string{"tag1", "tag3"},
289		Post:      &feedsv1.StandardPost{Content: "new content"},
290	}
291
292	aliceNewPost := &feedsv1.Post{
293		Author:    alice.String(),
294		DeletedAt: 0,
295		UpdatedAt: 0,
296		Tags:      []string{"tag1", "tag2"},
297		Post:      &feedsv1.StandardPost{Content: "new content"},
298	}
299
300	testing.SetContext(testing.Context{
301		CurrentRealm: testing.NewUserRealm(alice),
302	})
303	NewPost(cross, "public", post)
304	NewPost(cross, "public", deletedPost)
305	DeletePost(cross, 2)
306	testing.SetContext(testing.Context{
307		CurrentRealm: testing.NewUserRealm(bob),
308	})
309	NewPost(cross, "public", bobPost)
310	testing.SetContext(testing.Context{
311		CurrentRealm: testing.NewUserRealm(alice),
312	})
313
314	tests := testTable{
315		"valid post": {
316			input: input{
317				localPostId: 1,
318				newPost:     aliceNewPost,
319			},
320			output: output{
321				post:    aliceNewPost,
322				panic:   false,
323				updated: true,
324			},
325		},
326		/*
327			"invalid post": {
328				input: input{
329					localPostId: 4,
330					newPost:     aliceNewPost,
331				},
332				output: output{
333					post:    nil,
334					panic:   true,
335					updated: false,
336				},
337			},
338			"not the creator": {
339				input: input{
340					localPostId: 3,
341					newPost:     aliceNewPost,
342				},
343				output: output{
344					post:    nil,
345					panic:   true,
346					updated: false,
347				},
348			},
349			"already deleted": {
350				input: input{
351					localPostId: 2,
352					newPost:     aliceNewPost,
353				},
354				output: output{
355					post:    nil,
356					panic:   true,
357					updated: false,
358				},
359			},
360		*/
361	}
362
363	for name, tc := range tests {
364		t.Run(name, func(t *testing.T) {
365			if tc.output.panic {
366				defer func() {
367					if r := recover(); r == nil {
368						t.Errorf("expected panic")
369					}
370				}()
371			}
372			testing.SetContext(testing.Context{
373				Time: time.Unix(1757944096, 0),
374			})
375			EditPost(cross, tc.input.localPostId, tc.input.newPost)
376			got := GetPost(tc.input.localPostId)
377			if tc.output.updated && got.UpdatedAt == 0 {
378				t.Errorf("expected updated post")
379			}
380			if !tc.output.updated && got.UpdatedAt != 0 {
381				t.Errorf("expected unchanged post")
382			}
383
384			if tc.output.post != nil {
385				if got.Author != tc.output.post.Author {
386					t.Errorf("expected author %q, got %q", tc.output.post.Author, got.Author)
387				}
388				if got.DeletedAt != tc.output.post.DeletedAt {
389					t.Errorf("expected deleted at %d, got %d", tc.output.post.DeletedAt, got.DeletedAt)
390				}
391				if len(got.Tags) != len(tc.output.post.Tags) {
392					t.Errorf("expected %d tags, got %d", len(tc.output.post.Tags), len(got.Tags))
393				}
394				for i, tag := range got.Tags {
395					if tag != tc.output.post.Tags[i] {
396						t.Errorf("expected tag %q, got %q", tc.output.post.Tags[i], tag)
397					}
398				}
399				stdExpected := tc.output.post.Post.(*feedsv1.StandardPost)
400				stdGot := got.Post.(*feedsv1.StandardPost)
401				if stdExpected.Content != stdGot.Content {
402					t.Errorf("expected content %q, got %q", stdExpected.Content, stdGot.Content)
403				}
404			}
405		})
406	}
407}
408
409func TestReactPost(t *testing.T) {
410	setupTest()
411
412	type input struct {
413		localPostId uint64
414		user        string
415	}
416
417	type output struct {
418		reactions []*feedsv1.ReactionView
419	}
420
421	type test struct {
422		input  input
423		output output
424	}
425
426	type testTable = map[string]test
427
428	post := &feedsv1.Post{
429		Author:    alice.String(),
430		DeletedAt: 0,
431		Post:      &feedsv1.StandardPost{},
432	}
433
434	post2 := &feedsv1.Post{
435		Author:    alice.String(),
436		DeletedAt: 0,
437		Post:      &feedsv1.StandardPost{},
438	}
439
440	post3 := &feedsv1.Post{
441		Author:    alice.String(),
442		DeletedAt: 0,
443		Post:      &feedsv1.StandardPost{},
444	}
445
446	testing.SetContext(testing.Context{
447		CurrentRealm: testing.NewUserRealm(alice),
448	})
449	NewPost(cross, "public", post)
450	NewPost(cross, "public", post2)
451	NewPost(cross, "public", post3)
452	ReactPost(cross, 1, "like")
453	ReactPost(cross, 1, "dislike")
454	testing.SetContext(testing.Context{
455		CurrentRealm: testing.NewUserRealm(bob),
456	})
457	ReactPost(cross, 1, "like")
458	ReactPost(cross, 2, "like")
459
460	tests := testTable{
461		"post without reaction": {
462			input: input{
463				localPostId: 3,
464				user:        alice.String(),
465			},
466			output: output{
467				reactions: []*feedsv1.ReactionView{},
468			},
469		},
470		"post with reaction": {
471			input: input{
472				localPostId: 1,
473				user:        alice.String(),
474			},
475			output: output{
476				reactions: []*feedsv1.ReactionView{
477					{
478						Icon:         "dislike", // keep it sorted
479						Count:        1,
480						UserHasVoted: true,
481					},
482					{
483						Icon:         "like",
484						Count:        2,
485						UserHasVoted: true,
486					},
487				},
488			},
489		},
490		"post with reaction from another user": {
491			input: input{
492				localPostId: 1,
493				user:        bob.String(),
494			},
495			output: output{
496				reactions: []*feedsv1.ReactionView{
497					{
498						Icon:         "dislike",
499						Count:        1,
500						UserHasVoted: false,
501					},
502					{
503						Icon:         "like",
504						Count:        2,
505						UserHasVoted: true,
506					},
507				},
508			},
509		},
510		"post without reactions": {
511			input: input{
512				localPostId: 3,
513				user:        alice.String(),
514			},
515			output: output{
516				reactions: []*feedsv1.ReactionView{},
517			},
518		},
519	}
520
521	for name, tc := range tests {
522		t.Run(name, func(t *testing.T) {
523			got := GetPostReactions(tc.input.localPostId, tc.input.user)
524			if len(got) != len(tc.output.reactions) {
525				t.Errorf("expected %d reactions, got %d", len(tc.output.reactions), len(got))
526			}
527			for i, r := range got {
528				if r.Icon != tc.output.reactions[i].Icon {
529					t.Errorf("expected %q, got %q", tc.output.reactions[i].Icon, r.Icon)
530				}
531				if r.Count != tc.output.reactions[i].Count {
532					t.Errorf("expected %d, got %d", tc.output.reactions[i].Count, r.Count)
533				}
534				if r.UserHasVoted != tc.output.reactions[i].UserHasVoted {
535					t.Errorf("expected %t, got %t", tc.output.reactions[i].UserHasVoted, r.UserHasVoted)
536				}
537			}
538		})
539	}
540
541}
542
543func setupTest() {
544	id = seqid.ID(0)
545
546	reactions = avl.NewTree()
547
548	posts = avl.NewTree()
549	postsByFeed = avl.NewTree()
550
551	feeds = avl.NewTree()
552	feedsByPost = avl.NewTree()
553}