From 3fffa5c4cd756b892a633d680c8b1500a90a7d60 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 10:54:34 +0100 Subject: [PATCH 01/10] test: add unit test for new post as thread and reply --- .../gno.land/r/demo/boards2/post_test.gno | 118 ++++++++++++++++++ 1 file changed, 118 insertions(+) create mode 100644 examples/gno.land/r/demo/boards2/post_test.gno diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno new file mode 100644 index 00000000000..ce6edb80e15 --- /dev/null +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -0,0 +1,118 @@ +package boards + +import ( + "strings" + "testing" + + "gno.land/p/demo/testutils" + "gno.land/p/demo/uassert" + "gno.land/p/demo/ufmt" +) + +func TestNewThread(t *testing.T) { + creator := testutils.TestAddress("creator") + member := testutils.TestAddress("member") + body := strings.Repeat("A", 82) + boardID := BoardID(1) + threadID := PostID(42) + boardName := "test123" + board := newBoard(boardID, boardName, creator) + url := ufmt.Sprintf( + "/r/demo/boards2:%s/%d", + boardName, + uint(threadID), + ) + replyURL := ufmt.Sprintf( + "/r/demo/boards2$help&func=CreateReply&bid=%d&threadid=%d&postid=%d", + uint(boardID), + uint(threadID), + uint(threadID), + ) + repostURL := ufmt.Sprintf( + "/r/demo/boards2$help&func=CreateRepost&bid=%d&postid=%d", + uint(boardID), + uint(threadID), + ) + deleteURL := ufmt.Sprintf( + "/r/demo/boards2$help&func=DeleteThread&bid=%d&threadID=%d", + uint(boardID), + uint(threadID), + ) + + thread := newPost(board, threadID, creator, "Test", body, threadID, 0, 0) + + uassert.True(t, thread.IsThread()) + uassert.Equal(t, uint(threadID), uint(thread.GetPostID())) + uassert.Equal(t, body[:77]+"...", thread.GetSummary()) + uassert.Equal(t, url, thread.GetURL()) + uassert.Equal(t, replyURL, thread.GetReplyFormURL()) + uassert.Equal(t, repostURL, thread.GetRepostFormURL()) + uassert.Equal(t, deleteURL, thread.GetDeleteFormURL()) + uassert.True(t, thread.HasPermission(creator, PermissionEdit)) + uassert.True(t, thread.HasPermission(creator, PermissionDelete)) + uassert.False(t, thread.HasPermission(creator, Permission("unknown"))) + uassert.False(t, thread.HasPermission(member, PermissionEdit)) + uassert.False(t, thread.HasPermission(member, PermissionDelete)) + uassert.False(t, thread.HasPermission(member, Permission("unknown"))) +} + +func TestThreadRenderSummary(t *testing.T) { + t.Skip("TODO: implement") +} + +func TestThreadRender(t *testing.T) { + t.Skip("TODO: implement") +} + +func TestThreadRenderInner(t *testing.T) { + t.Skip("TODO: implement") +} + +func TestNewReply(t *testing.T) { + creator := testutils.TestAddress("creator") + member := testutils.TestAddress("member") + body := strings.Repeat("A", 82) + boardID := BoardID(1) + threadID := PostID(42) + parentID := PostID(1) + replyID := PostID(2) + boardName := "test123" + board := newBoard(boardID, boardName, creator) + url := ufmt.Sprintf( + "/r/demo/boards2:%s/%d/%d", + boardName, + uint(threadID), + uint(replyID), + ) + replyURL := ufmt.Sprintf( + "/r/demo/boards2$help&func=CreateReply&bid=%d&threadid=%d&postid=%d", + uint(boardID), + uint(threadID), + uint(replyID), + ) + deleteURL := ufmt.Sprintf( + "/r/demo/boards2$help&func=DeleteReply&bid=%d&threadID=%d&replyID=%d", + uint(boardID), + uint(threadID), + uint(replyID), + ) + + reply := newPost(board, replyID, creator, "", body, threadID, parentID, 0) + + uassert.False(t, reply.IsThread()) + uassert.Equal(t, uint(replyID), uint(reply.GetPostID())) + uassert.Equal(t, body[:77]+"...", reply.GetSummary()) + uassert.Equal(t, url, reply.GetURL()) + uassert.Equal(t, replyURL, reply.GetReplyFormURL()) + uassert.Equal(t, deleteURL, reply.GetDeleteFormURL()) + uassert.True(t, reply.HasPermission(creator, PermissionEdit)) + uassert.True(t, reply.HasPermission(creator, PermissionDelete)) + uassert.False(t, reply.HasPermission(creator, Permission("unknown"))) + uassert.False(t, reply.HasPermission(member, PermissionEdit)) + uassert.False(t, reply.HasPermission(member, PermissionDelete)) + uassert.False(t, reply.HasPermission(member, Permission("unknown"))) +} + +func TestReplyRender(t *testing.T) { + t.Skip("TODO: implement") +} From 5f1afac779233ce5cce6102a6457566b99f10b9e Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 12:26:27 +0100 Subject: [PATCH 02/10] test: add unit test for `Post.AddReply()` --- examples/gno.land/r/demo/boards2/post.gno | 12 +++++ .../gno.land/r/demo/boards2/post_test.gno | 51 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index 5f9ceae2f5e..e45ee234f5e 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -64,6 +64,18 @@ func (post *Post) GetPostID() PostID { return post.id } +func (post *Post) GetParentID() PostID { + return post.parentID +} + +func (post *Post) GetCreator() std.Address { + return post.creator +} + +func (post *Post) GetBody() string { + return post.body +} + func (post *Post) AddReply(creator std.Address, body string) *Post { board := post.board pid := board.incGetPostID() diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index ce6edb80e15..c2570f76102 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -56,6 +56,22 @@ func TestNewThread(t *testing.T) { uassert.False(t, thread.HasPermission(member, Permission("unknown"))) } +func TestThreadAddReply(t *testing.T) { + replier := testutils.TestAddress("replier") + thread := createTestThread(t) + threadID := uint(thread.GetPostID()) + body := "A reply" + + reply := thread.AddReply(replier, body) + + r, found := thread.GetReply(reply.GetPostID()) + uassert.True(t, found) + uassert.True(t, reply == r) + uassert.Equal(t, threadID+1, uint(reply.GetPostID())) + uassert.Equal(t, reply.GetCreator(), replier) + uassert.Equal(t, reply.GetBody(), body) +} + func TestThreadRenderSummary(t *testing.T) { t.Skip("TODO: implement") } @@ -113,6 +129,41 @@ func TestNewReply(t *testing.T) { uassert.False(t, reply.HasPermission(member, Permission("unknown"))) } +func TestReplyAddReply(t *testing.T) { + replier := testutils.TestAddress("replier") + thread := createTestThread(t) + parentReply := thread.AddReply(testutils.TestAddress("parentReplier"), "") + threadID := uint(thread.GetPostID()) + parentReplyID := uint(parentReply.GetPostID()) + body := "A child reply" + + reply := parentReply.AddReply(replier, body) + + r, found := thread.GetReply(reply.GetPostID()) + uassert.True(t, found) + uassert.True(t, reply == r) + uassert.Equal(t, parentReplyID, uint(reply.GetParentID())) + uassert.Equal(t, parentReplyID+1, uint(reply.GetPostID())) + uassert.Equal(t, reply.GetCreator(), replier) + uassert.Equal(t, reply.GetBody(), body) +} + func TestReplyRender(t *testing.T) { t.Skip("TODO: implement") } + +func createTestThread(t *testing.T) *Post { + t.Helper() + + creator := testutils.TestAddress("creator") + board := newBoard(1, "test_board_123", creator) + return board.AddThread(creator, "Title", "Body") +} + +func createTestReply(t *testing.T) *Post { + t.Helper() + + creator := testutils.TestAddress("replier") + thread := createTestThread(t) + return thread.AddReply(creator, "Test message") +} From c0f12f136f97e32e33ad8673c65fd8884cff953c Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 12:50:46 +0100 Subject: [PATCH 03/10] test: add post update unit test --- examples/gno.land/r/demo/boards2/post.gno | 12 ++++++++++++ examples/gno.land/r/demo/boards2/post_test.gno | 18 ++++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index e45ee234f5e..579c7aa8ae5 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -72,10 +72,22 @@ func (post *Post) GetCreator() std.Address { return post.creator } +func (post *Post) GetTitle() string { + return post.title +} + func (post *Post) GetBody() string { return post.body } +func (post *Post) GetCreatedAt() time.Time { + return post.createdAt +} + +func (post *Post) GetUpdatedAt() time.Time { + return post.updatedAt +} + func (post *Post) AddReply(creator std.Address, body string) *Post { board := post.board pid := board.incGetPostID() diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index c2570f76102..f5e9e6c1af0 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -9,6 +9,20 @@ import ( "gno.land/p/demo/ufmt" ) +func TestPostUpdate(t *testing.T) { + board := newBoard(1, "test123", testutils.TestAddress("creator")) + creator := testutils.TestAddress("creator") + post := newPost(board, 1, creator, "Title", "Body", 1, 0, 0) + title := "New Title" + body := "New body" + + post.Update(title, body) + + uassert.Equal(t, title, post.GetTitle()) + uassert.Equal(t, body, post.GetBody()) + uassert.False(t, post.GetUpdatedAt().IsZero()) +} + func TestNewThread(t *testing.T) { creator := testutils.TestAddress("creator") member := testutils.TestAddress("member") @@ -43,6 +57,8 @@ func TestNewThread(t *testing.T) { uassert.True(t, thread.IsThread()) uassert.Equal(t, uint(threadID), uint(thread.GetPostID())) + uassert.False(t, thread.GetCreatedAt().IsZero()) + uassert.True(t, thread.GetUpdatedAt().IsZero()) uassert.Equal(t, body[:77]+"...", thread.GetSummary()) uassert.Equal(t, url, thread.GetURL()) uassert.Equal(t, replyURL, thread.GetReplyFormURL()) @@ -117,6 +133,8 @@ func TestNewReply(t *testing.T) { uassert.False(t, reply.IsThread()) uassert.Equal(t, uint(replyID), uint(reply.GetPostID())) + uassert.False(t, reply.GetCreatedAt().IsZero()) + uassert.True(t, reply.GetUpdatedAt().IsZero()) uassert.Equal(t, body[:77]+"...", reply.GetSummary()) uassert.Equal(t, url, reply.GetURL()) uassert.Equal(t, replyURL, reply.GetReplyFormURL()) From cc6736b844bb759dee36ad7eea3743df725219c3 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 13:16:53 +0100 Subject: [PATCH 04/10] test: add unit tests for `Post.GetReply()` --- .../gno.land/r/demo/boards2/post_test.gno | 75 ++++++++++++++++++- 1 file changed, 74 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index f5e9e6c1af0..b25a5b2a446 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -26,6 +26,7 @@ func TestPostUpdate(t *testing.T) { func TestNewThread(t *testing.T) { creator := testutils.TestAddress("creator") member := testutils.TestAddress("member") + title := "Test Title" body := strings.Repeat("A", 82) boardID := BoardID(1) threadID := PostID(42) @@ -53,12 +54,13 @@ func TestNewThread(t *testing.T) { uint(threadID), ) - thread := newPost(board, threadID, creator, "Test", body, threadID, 0, 0) + thread := newPost(board, threadID, creator, title, body, threadID, 0, 0) uassert.True(t, thread.IsThread()) uassert.Equal(t, uint(threadID), uint(thread.GetPostID())) uassert.False(t, thread.GetCreatedAt().IsZero()) uassert.True(t, thread.GetUpdatedAt().IsZero()) + uassert.Equal(t, title, thread.GetTitle()) uassert.Equal(t, body[:77]+"...", thread.GetSummary()) uassert.Equal(t, url, thread.GetURL()) uassert.Equal(t, replyURL, thread.GetReplyFormURL()) @@ -88,6 +90,41 @@ func TestThreadAddReply(t *testing.T) { uassert.Equal(t, reply.GetBody(), body) } +func TestThreadGetReply(t *testing.T) { + thread := createTestThread(t) + cases := []struct { + name string + setup func() PostID + found bool + }{ + { + name: "found", + setup: func() PostID { + reply := thread.AddReply(testutils.TestAddress("replier"), "") + return reply.GetPostID() + }, + found: true, + }, + { + name: "not found", + setup: func() PostID { return 42 }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + replyID := tc.setup() + + reply, found := thread.GetReply(replyID) + + uassert.Equal(t, tc.found, found) + if reply != nil { + uassert.Equal(t, uint(replyID), uint(reply.GetPostID())) + } + }) + } +} + func TestThreadRenderSummary(t *testing.T) { t.Skip("TODO: implement") } @@ -166,6 +203,42 @@ func TestReplyAddReply(t *testing.T) { uassert.Equal(t, reply.GetBody(), body) } +func TestReplyGetReply(t *testing.T) { + thread := createTestThread(t) + parentReply := thread.AddReply(testutils.TestAddress("parentReplier"), "") + cases := []struct { + name string + setup func() PostID + found bool + }{ + { + name: "found", + setup: func() PostID { + reply := parentReply.AddReply(testutils.TestAddress("replier"), "") + return reply.GetPostID() + }, + found: true, + }, + { + name: "not found", + setup: func() PostID { return 42 }, + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + replyID := tc.setup() + + reply, found := thread.GetReply(replyID) + + uassert.Equal(t, tc.found, found) + if reply != nil { + uassert.Equal(t, uint(replyID), uint(reply.GetPostID())) + } + }) + } +} + func TestReplyRender(t *testing.T) { t.Skip("TODO: implement") } From a45b24381ba077ff6c94c72ed5a8b54c07e3c844 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 16:59:50 +0100 Subject: [PATCH 05/10] test: add unit tests for `Post.DeleteReply()` --- examples/gno.land/r/demo/boards2/post.gno | 25 +++++--- .../gno.land/r/demo/boards2/post_test.gno | 62 +++++++++++++++++++ 2 files changed, 78 insertions(+), 9 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index 579c7aa8ae5..aab4c0726c0 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -1,6 +1,7 @@ package boards import ( + "errors" "std" "strconv" "time" @@ -132,24 +133,30 @@ func (post *Post) AddRepostTo(creator std.Address, title, body string, dst *Boar return repost } -func (thread *Post) DeleteReply(replyID PostID) { - if thread.id == replyID { - panic("should not happen") +func (post *Post) DeleteReply(replyID PostID) error { + if !post.IsThread() { + // TODO: Allow removing replies from parent replies too + panic("cannot delete reply from a non-thread post") + } + + if post.id == replyID { + return errors.New("expected an ID of an inner reply") } key := replyID.Key() - v, removed := thread.repliesAll.Remove(key) + v, removed := post.repliesAll.Remove(key) if !removed { - panic("reply not found in thread") + return errors.New("reply not found in thread") } - post := v.(*Post) - if post.parentID != thread.id { - parent, _ := thread.GetReply(post.parentID) + reply := v.(*Post) + if reply.parentID != post.id { + parent, _ := post.GetReply(reply.parentID) parent.replies.Remove(key) } else { - thread.replies.Remove(key) + post.replies.Remove(key) } + return nil } // TODO: Change HasPermission to use a new authorization interface's `CanDo()` diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index b25a5b2a446..ebbd62f80c4 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -125,6 +125,57 @@ func TestThreadGetReply(t *testing.T) { } } +func TestThreadDeleteReply(t *testing.T) { + thread := createTestThread(t) + cases := []struct { + name string + setup func() PostID + err string + }{ + { + name: "ok", + setup: func() PostID { + reply := thread.AddReply(testutils.TestAddress("replier"), "") + return reply.GetPostID() + }, + }, + { + name: "ok nested", + setup: func() PostID { + reply := thread.AddReply(testutils.TestAddress("replier"), "") + return reply.AddReply(testutils.TestAddress("replier2"), "").GetPostID() + }, + }, + { + name: "invalid", + setup: func() PostID { return thread.GetPostID() }, + err: "expected an ID of an inner reply", + }, + { + name: "not found", + setup: func() PostID { return 42 }, + err: "reply not found in thread", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + replyID := tc.setup() + + err := thread.DeleteReply(replyID) + + if tc.err != "" { + uassert.ErrorContains(t, err, tc.err) + return + } + + uassert.NoError(t, err) + _, found := thread.GetReply(replyID) + uassert.False(t, found) + }) + } +} + func TestThreadRenderSummary(t *testing.T) { t.Skip("TODO: implement") } @@ -239,6 +290,17 @@ func TestReplyGetReply(t *testing.T) { } } +func TestReplyDeleteReply(t *testing.T) { + thread := createTestThread(t) + parentReply := thread.AddReply(testutils.TestAddress("replier"), "") + reply := parentReply.AddReply(testutils.TestAddress("replier2"), "") + + // NOTE: Deleting a reply from a parent reply should eventually be suported + uassert.PanicsWithMessage(t, "cannot delete reply from a non-thread post", func() { + parentReply.DeleteReply(reply.GetPostID()) + }) +} + func TestReplyRender(t *testing.T) { t.Skip("TODO: implement") } From 2d6e0d78d6e83fe47fbf899db8feb291f068ebeb Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 19:46:02 +0100 Subject: [PATCH 06/10] test: add unit test for thread repost --- examples/gno.land/r/demo/boards2/board.gno | 4 ++ examples/gno.land/r/demo/boards2/post.gno | 66 +++++++++++-------- .../gno.land/r/demo/boards2/post_test.gno | 51 ++++++++++++++ 3 files changed, 92 insertions(+), 29 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/board.gno b/examples/gno.land/r/demo/boards2/board.gno index 0df39f7ebfb..16f42f786c5 100644 --- a/examples/gno.land/r/demo/boards2/board.gno +++ b/examples/gno.land/r/demo/boards2/board.gno @@ -64,6 +64,10 @@ func (board *Board) IsPrivate() bool { return board.id == 0 } +func (board *Board) GetID() BoardID { + return board.id +} + // GetURL returns the relative URL of the board. func (board *Board) GetURL() string { return strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + ":" + board.name diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index aab4c0726c0..466354ceec8 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -25,35 +25,35 @@ func (id PostID) Key() string { // A Post is a "thread" or a "reply" depending on context. // A thread is a Post of a Board that holds other replies. type Post struct { - board *Board - id PostID - creator std.Address - title string // optional - body string - replies avl.Tree // Post.id -> *Post - repliesAll avl.Tree // Post.id -> *Post (all replies, for top-level posts) - reposts avl.Tree // Board.id -> Post.id - threadID PostID // original Post.id - parentID PostID // parent Post.id (if reply or repost) - repostBoard BoardID // original Board.id (if repost) - createdAt time.Time - updatedAt time.Time + board *Board + id PostID + creator std.Address + title string // optional + body string + replies avl.Tree // Post.id -> *Post + repliesAll avl.Tree // Post.id -> *Post (all replies, for top-level posts) + reposts avl.Tree // Board.id -> Post.id + threadID PostID // original Post.id + parentID PostID // parent Post.id (if reply or repost) + repostBoardID BoardID // original Board.id (if repost) + createdAt time.Time + updatedAt time.Time } -func newPost(board *Board, id PostID, creator std.Address, title, body string, threadID, parentID PostID, repostBoard BoardID) *Post { +func newPost(board *Board, id PostID, creator std.Address, title, body string, threadID, parentID PostID, repostBoardID BoardID) *Post { return &Post{ - board: board, - id: id, - creator: creator, - title: title, - body: body, - replies: avl.Tree{}, - repliesAll: avl.Tree{}, - reposts: avl.Tree{}, - threadID: threadID, - parentID: parentID, - repostBoard: repostBoard, - createdAt: time.Now(), + board: board, + id: id, + creator: creator, + title: title, + body: body, + replies: avl.Tree{}, + repliesAll: avl.Tree{}, + reposts: avl.Tree{}, + threadID: threadID, + parentID: parentID, + repostBoardID: repostBoardID, + createdAt: time.Now(), } } @@ -61,6 +61,10 @@ func (post *Post) IsThread() bool { return post.parentID == 0 } +func (post *Post) GetBoard() *Board { + return post.board +} + func (post *Post) GetPostID() PostID { return post.id } @@ -69,6 +73,10 @@ func (post *Post) GetParentID() PostID { return post.parentID } +func (post *Post) GetRepostBoardID() BoardID { + return post.repostBoardID +} + func (post *Post) GetCreator() std.Address { return post.creator } @@ -216,10 +224,10 @@ func (post *Post) GetDeleteFormURL() string { } func (post *Post) RenderSummary() string { - if post.repostBoard != 0 { - dstBoard, found := getBoard(post.repostBoard) + if post.repostBoardID != 0 { + dstBoard, found := getBoard(post.repostBoardID) if !found { - panic("repostBoard does not exist") + panic("repost board does not exist") } thread, found := dstBoard.GetThread(PostID(post.parentID)) diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index ebbd62f80c4..9c49348edf4 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -23,6 +23,57 @@ func TestPostUpdate(t *testing.T) { uassert.False(t, post.GetUpdatedAt().IsZero()) } +func TestPostAddRepostTo(t *testing.T) { + cases := []struct { + name, title, body string + dstBoard *Board + thread *Post + setup func() *Post + err string + }{ + { + name: "repost thread", + title: "Repost Title", + body: "Repost body", + dstBoard: newBoard(42, "dst123", testutils.TestAddress("creatorDstBoard")), + setup: func() *Post { return createTestThread(t) }, + }, + { + name: "invalid repost from reply", + setup: func() *Post { return createTestReply(t) }, + err: "cannot repost non-thread post", + }, + } + + for _, tc := range cases { + t.Run(tc.name, func(t *testing.T) { + var ( + repost *Post + creator = testutils.TestAddress("repostCreator") + thread = tc.setup() + ) + + createRepost := func() { + repost = thread.AddRepostTo(creator, tc.title, tc.body, tc.dstBoard) + } + + if tc.err != "" { + uassert.PanicsWithMessage(t, tc.err, createRepost) + return + } else { + uassert.NotPanics(t, createRepost) + } + + r, found := tc.dstBoard.GetThread(repost.GetPostID()) + uassert.True(t, found) + uassert.True(t, repost == r) + uassert.Equal(t, tc.title, repost.GetTitle()) + uassert.Equal(t, tc.body, repost.GetBody()) + uassert.Equal(t, uint(thread.GetBoard().GetID()), uint(repost.GetRepostBoardID())) + }) + } +} + func TestNewThread(t *testing.T) { creator := testutils.TestAddress("creator") member := testutils.TestAddress("member") From 4e073406b4fc124224a4211e1770c657c1a7129b Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 19:48:14 +0100 Subject: [PATCH 07/10] fix: correct `TestThreadGetReply` to use test case local values --- .../gno.land/r/demo/boards2/post_test.gno | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index 9c49348edf4..1c83be9172f 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -142,31 +142,33 @@ func TestThreadAddReply(t *testing.T) { } func TestThreadGetReply(t *testing.T) { - thread := createTestThread(t) cases := []struct { - name string - setup func() PostID - found bool + name string + thread *Post + setup func(thread *Post) (replyID PostID) + found bool }{ { - name: "found", - setup: func() PostID { + name: "found", + thread: createTestThread(t), + setup: func(thread *Post) PostID { reply := thread.AddReply(testutils.TestAddress("replier"), "") return reply.GetPostID() }, found: true, }, { - name: "not found", - setup: func() PostID { return 42 }, + name: "not found", + thread: createTestThread(t), + setup: func(*Post) PostID { return 42 }, }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { - replyID := tc.setup() + replyID := tc.setup(tc.thread) - reply, found := thread.GetReply(replyID) + reply, found := tc.thread.GetReply(replyID) uassert.Equal(t, tc.found, found) if reply != nil { From 580cbe74c8607e0344be1bc8d73ea06111ab8e71 Mon Sep 17 00:00:00 2001 From: jeronimoalbi Date: Fri, 15 Nov 2024 19:52:14 +0100 Subject: [PATCH 08/10] fix: correct ID variables casing to match standards --- examples/gno.land/r/demo/boards2/post.gno | 6 +++--- examples/gno.land/r/demo/boards2/post_test.gno | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/examples/gno.land/r/demo/boards2/post.gno b/examples/gno.land/r/demo/boards2/post.gno index 466354ceec8..a2a19a1b618 100644 --- a/examples/gno.land/r/demo/boards2/post.gno +++ b/examples/gno.land/r/demo/boards2/post.gno @@ -197,15 +197,15 @@ func (post *Post) GetURL() string { func (post *Post) GetReplyFormURL() string { return txlink.URL("CreateReply", "bid", post.board.id.String(), - "threadid", post.threadID.String(), - "postid", post.id.String(), + "threadID", post.threadID.String(), + "postID", post.id.String(), ) } func (post *Post) GetRepostFormURL() string { return txlink.URL("CreateRepost", "bid", post.board.id.String(), - "postid", post.id.String(), + "postID", post.id.String(), ) } diff --git a/examples/gno.land/r/demo/boards2/post_test.gno b/examples/gno.land/r/demo/boards2/post_test.gno index 1c83be9172f..f4ec444f0cc 100644 --- a/examples/gno.land/r/demo/boards2/post_test.gno +++ b/examples/gno.land/r/demo/boards2/post_test.gno @@ -89,13 +89,13 @@ func TestNewThread(t *testing.T) { uint(threadID), ) replyURL := ufmt.Sprintf( - "/r/demo/boards2$help&func=CreateReply&bid=%d&threadid=%d&postid=%d", + "/r/demo/boards2$help&func=CreateReply&bid=%d&threadID=%d&postID=%d", uint(boardID), uint(threadID), uint(threadID), ) repostURL := ufmt.Sprintf( - "/r/demo/boards2$help&func=CreateRepost&bid=%d&postid=%d", + "/r/demo/boards2$help&func=CreateRepost&bid=%d&postID=%d", uint(boardID), uint(threadID), ) @@ -258,7 +258,7 @@ func TestNewReply(t *testing.T) { uint(replyID), ) replyURL := ufmt.Sprintf( - "/r/demo/boards2$help&func=CreateReply&bid=%d&threadid=%d&postid=%d", + "/r/demo/boards2$help&func=CreateReply&bid=%d&threadID=%d&postID=%d", uint(boardID), uint(threadID), uint(replyID), From 5bde869101ec2b9f8340f911efa0f09503cdd125 Mon Sep 17 00:00:00 2001 From: x1unix Date: Mon, 18 Nov 2024 22:31:53 -0500 Subject: [PATCH 09/10] feat: add initial unit tests for board --- .../gno.land/r/demo/boards2/board_test.gno | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 examples/gno.land/r/demo/boards2/board_test.gno diff --git a/examples/gno.land/r/demo/boards2/board_test.gno b/examples/gno.land/r/demo/boards2/board_test.gno new file mode 100644 index 00000000000..348f8788bd0 --- /dev/null +++ b/examples/gno.land/r/demo/boards2/board_test.gno @@ -0,0 +1,43 @@ +package boards + +import ( + "strings" + "testing" + + "gno.land/p/demo/uassert" +) + +func TestBoardID_String(t *testing.T) { + input := BoardID(32) + + uassert.Equal(t, "32", input.String()) +} + +func TestBoardID_Key(t *testing.T) { + input := BoardID(128) + want := strings.Repeat("0", 7) + "128" + uassert.Equal(t, want, input.Key()) +} + +func TestBoard_IsPrivate(t *testing.T) { + b := new(Board) + b.id = 0 + uassert.True(t, b.IsPrivate()) + + b.id = 128 + uassert.False(t, b.IsPrivate()) +} + +func TestBoard_GetID(t *testing.T) { + want := int(92) + b := new(Board) + b.id = BoardID(want) + got := int(b.GetID()) + + uassert.Equal(t, got, want) + uassert.NotEqual(t, got, want*want) +} + +func TestBoard_GetURL(t *testing.T) { + // TODO +} From b0dd7b8066ccd3c43e9be3120dca0c2befca7da5 Mon Sep 17 00:00:00 2001 From: x1unix Date: Tue, 19 Nov 2024 13:38:14 -0500 Subject: [PATCH 10/10] feat: add board tests --- .../gno.land/r/demo/boards2/board_test.gno | 117 +++++++++++++++++- 1 file changed, 116 insertions(+), 1 deletion(-) diff --git a/examples/gno.land/r/demo/boards2/board_test.gno b/examples/gno.land/r/demo/boards2/board_test.gno index 348f8788bd0..9a6c51e8ea9 100644 --- a/examples/gno.land/r/demo/boards2/board_test.gno +++ b/examples/gno.land/r/demo/boards2/board_test.gno @@ -1,10 +1,12 @@ package boards import ( + "std" "strings" "testing" "gno.land/p/demo/uassert" + "gno.land/p/moul/txlink" ) func TestBoardID_String(t *testing.T) { @@ -39,5 +41,118 @@ func TestBoard_GetID(t *testing.T) { } func TestBoard_GetURL(t *testing.T) { - // TODO + pkgPath := strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + name := "foobar_test_get_url123" + want := pkgPath + ":" + name + + var addr std.Address + + board := newBoard(1, name, addr) + got := board.GetURL() + uassert.Equal(t, want, got) +} + +func TestBoard_GetThread(t *testing.T) { + var addr std.Address + b := newBoard(1, "test123", addr) + + _, ok := b.GetThread(12345) + uassert.False(t, ok) + + post := b.AddThread(addr, "foo", "bar") + _, ok = b.GetThread(post.GetPostID()) + uassert.True(t, ok) +} + +func TestBoard_DeleteThread(t *testing.T) { + var addr std.Address + b := newBoard(1, "test123", addr) + + post := b.AddThread(addr, "foo", "bar") + id := post.GetPostID() + + b.DeleteThread(id) + + _, ok := b.GetThread(id) + uassert.False(t, ok) +} + +func TestBoard_HasPermission(t *testing.T) { + var ( + alice std.Address = "012345" + bob std.Address = "cafebabe" + ) + + cases := []struct { + label string + creator std.Address + actor std.Address + perm Permission + expect bool + }{ + { + label: "creator should be able to edit board", + expect: true, + creator: alice, + actor: alice, + perm: PermissionEdit, + }, + { + label: "creator should be able to delete board", + expect: true, + creator: alice, + actor: alice, + perm: PermissionDelete, + }, + { + label: "guest shouldn't be able to edit boards", + expect: false, + creator: alice, + actor: bob, + perm: PermissionEdit, + }, + { + label: "guest shouldn't be able to delete boards", + expect: false, + creator: alice, + actor: bob, + perm: PermissionDelete, + }, + } + + for i, c := range cases { + t.Run(c.label, func(t *testing.T) { + b := newBoard(BoardID(i), "test12345", c.creator) + got := b.HasPermission(c.actor, c.perm) + uassert.Equal(t, c.expect, got) + }) + } +} + +var boardUrlPrefix = strings.TrimPrefix(std.CurrentRealm().PkgPath(), "gno.land") + +func TestBoard_GetURLFromThreadID(t *testing.T) { + boardName := "test12345" + b := newBoard(BoardID(11), boardName, "") + want := boardUrlPrefix + ":" + boardName + "/10" + + got := b.GetURLFromThreadID(10) + uassert.Equal(t, want, got) +} + +func TestBoard_GetURLFromReplyID(t *testing.T) { + boardName := "test12345" + b := newBoard(BoardID(11), boardName, "") + want := boardUrlPrefix + ":" + boardName + "/10/20" + + got := b.GetURLFromReplyID(10, 20) + uassert.Equal(t, want, got) +} + +func TestBoard_GetPostFormURL(t *testing.T) { + bid := BoardID(386) + b := newBoard(bid, "foo1234", "") + expect := txlink.URL("CreateThread", "bid", bid.String()) + got := b.GetPostFormURL() + uassert.Equal(t, expect, got) }