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  }