list_test.go (8731B)
1 // GoToSocial 2 // Copyright (C) GoToSocial Authors admin@gotosocial.org 3 // SPDX-License-Identifier: AGPL-3.0-or-later 4 // 5 // This program is free software: you can redistribute it and/or modify 6 // it under the terms of the GNU Affero General Public License as published by 7 // the Free Software Foundation, either version 3 of the License, or 8 // (at your option) any later version. 9 // 10 // This program is distributed in the hope that it will be useful, 11 // but WITHOUT ANY WARRANTY; without even the implied warranty of 12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 // GNU Affero General Public License for more details. 14 // 15 // You should have received a copy of the GNU Affero General Public License 16 // along with this program. If not, see <http://www.gnu.org/licenses/>. 17 18 package bundb_test 19 20 import ( 21 "context" 22 "testing" 23 24 "github.com/stretchr/testify/suite" 25 "github.com/superseriousbusiness/gotosocial/internal/db" 26 "github.com/superseriousbusiness/gotosocial/internal/gtscontext" 27 "github.com/superseriousbusiness/gotosocial/internal/gtsmodel" 28 "golang.org/x/exp/slices" 29 ) 30 31 type ListTestSuite struct { 32 BunDBStandardTestSuite 33 } 34 35 func (suite *ListTestSuite) testStructs() (*gtsmodel.List, *gtsmodel.Account) { 36 testList := >smodel.List{} 37 *testList = *suite.testLists["local_account_1_list_1"] 38 39 // Populate entries on this list as we'd expect them back from the db. 40 entries := make([]*gtsmodel.ListEntry, 0, len(suite.testListEntries)) 41 for _, entry := range suite.testListEntries { 42 entries = append(entries, entry) 43 } 44 45 // Sort by ID descending (again, as we'd expect from the db). 46 slices.SortFunc(entries, func(a, b *gtsmodel.ListEntry) bool { 47 return b.ID < a.ID 48 }) 49 50 testList.ListEntries = entries 51 52 testAccount := >smodel.Account{} 53 *testAccount = *suite.testAccounts["local_account_1"] 54 55 return testList, testAccount 56 } 57 58 func (suite *ListTestSuite) checkList(expected *gtsmodel.List, actual *gtsmodel.List) { 59 suite.Equal(expected.ID, actual.ID) 60 suite.Equal(expected.Title, actual.Title) 61 suite.Equal(expected.AccountID, actual.AccountID) 62 suite.Equal(expected.RepliesPolicy, actual.RepliesPolicy) 63 suite.NotNil(actual.Account) 64 } 65 66 func (suite *ListTestSuite) checkListEntry(expected *gtsmodel.ListEntry, actual *gtsmodel.ListEntry) { 67 suite.Equal(expected.ID, actual.ID) 68 suite.Equal(expected.ListID, actual.ListID) 69 suite.Equal(expected.FollowID, actual.FollowID) 70 } 71 72 func (suite *ListTestSuite) checkListEntries(expected []*gtsmodel.ListEntry, actual []*gtsmodel.ListEntry) { 73 var ( 74 lExpected = len(expected) 75 lActual = len(actual) 76 ) 77 78 if lExpected != lActual { 79 suite.FailNow("", "expected %d list entries, got %d", lExpected, lActual) 80 } 81 82 var topID string 83 for i, expectedEntry := range expected { 84 actualEntry := actual[i] 85 86 // Ensure ID descending. 87 if topID == "" { 88 topID = actualEntry.ID 89 } else { 90 suite.Less(actualEntry.ID, topID) 91 } 92 93 suite.checkListEntry(expectedEntry, actualEntry) 94 } 95 } 96 97 func (suite *ListTestSuite) TestGetListByID() { 98 testList, _ := suite.testStructs() 99 100 dbList, err := suite.db.GetListByID(context.Background(), testList.ID) 101 if err != nil { 102 suite.FailNow(err.Error()) 103 } 104 105 suite.checkList(testList, dbList) 106 suite.checkListEntries(testList.ListEntries, dbList.ListEntries) 107 } 108 109 func (suite *ListTestSuite) TestGetListsForAccountID() { 110 testList, testAccount := suite.testStructs() 111 112 dbLists, err := suite.db.GetListsForAccountID(context.Background(), testAccount.ID) 113 if err != nil { 114 suite.FailNow(err.Error()) 115 } 116 117 if l := len(dbLists); l != 1 { 118 suite.FailNow("", "expected %d lists, got %d", 1, l) 119 } 120 121 suite.checkList(testList, dbLists[0]) 122 } 123 124 func (suite *ListTestSuite) TestGetListEntries() { 125 testList, _ := suite.testStructs() 126 127 dbListEntries, err := suite.db.GetListEntries(context.Background(), testList.ID, "", "", "", 0) 128 if err != nil { 129 suite.FailNow(err.Error()) 130 } 131 132 suite.checkListEntries(testList.ListEntries, dbListEntries) 133 } 134 135 func (suite *ListTestSuite) TestPutList() { 136 ctx := context.Background() 137 _, testAccount := suite.testStructs() 138 139 testList := >smodel.List{ 140 ID: "01H0J2PMYM54618VCV8Y8QYAT4", 141 Title: "Test List!", 142 AccountID: testAccount.ID, 143 } 144 145 if err := suite.db.PutList(ctx, testList); err != nil { 146 suite.FailNow(err.Error()) 147 } 148 149 dbList, err := suite.db.GetListByID(ctx, testList.ID) 150 if err != nil { 151 suite.FailNow(err.Error()) 152 } 153 154 // Bodge testlist as though default had been set. 155 testList.RepliesPolicy = gtsmodel.RepliesPolicyFollowed 156 suite.checkList(testList, dbList) 157 } 158 159 func (suite *ListTestSuite) TestUpdateList() { 160 ctx := context.Background() 161 testList, _ := suite.testStructs() 162 163 // Get List in the cache first. 164 dbList, err := suite.db.GetListByID(ctx, testList.ID) 165 if err != nil { 166 suite.FailNow(err.Error()) 167 } 168 169 // Now do the update. 170 testList.Title = "New Title!" 171 if err := suite.db.UpdateList(ctx, testList, "title"); err != nil { 172 suite.FailNow(err.Error()) 173 } 174 175 // Cache should be invalidated 176 // + we should have updated list. 177 dbList, err = suite.db.GetListByID(ctx, testList.ID) 178 if err != nil { 179 suite.FailNow(err.Error()) 180 } 181 182 suite.checkList(testList, dbList) 183 } 184 185 func (suite *ListTestSuite) TestDeleteList() { 186 ctx := context.Background() 187 testList, _ := suite.testStructs() 188 189 // Get List in the cache first. 190 if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { 191 suite.FailNow(err.Error()) 192 } 193 194 // Now do the delete. 195 if err := suite.db.DeleteListByID(ctx, testList.ID); err != nil { 196 suite.FailNow(err.Error()) 197 } 198 199 // Cache should be invalidated 200 // + we should have no list. 201 _, err := suite.db.GetListByID(ctx, testList.ID) 202 suite.ErrorIs(err, db.ErrNoEntries) 203 204 // All entries belonging to this 205 // list should now be deleted. 206 listEntries, err := suite.db.GetListEntries(ctx, testList.ID, "", "", "", 0) 207 if err != nil { 208 suite.FailNow(err.Error()) 209 } 210 suite.Empty(listEntries) 211 } 212 213 func (suite *ListTestSuite) TestPutListEntries() { 214 ctx := context.Background() 215 testList, _ := suite.testStructs() 216 217 listEntries := []*gtsmodel.ListEntry{ 218 { 219 ID: "01H0MKMQY69HWDSDR2SWGA17R4", 220 ListID: testList.ID, 221 FollowID: "01H0MKNFRFZS8R9WV6DBX31Y03", // random id, doesn't exist 222 }, 223 { 224 ID: "01H0MKPGQF0E7QAVW5BKTHZ630", 225 ListID: testList.ID, 226 FollowID: "01H0MKP6RR8VEHN3GVWFBP2H30", // random id, doesn't exist 227 }, 228 { 229 ID: "01H0MKPPP2DT68FRBMR1FJM32T", 230 ListID: testList.ID, 231 FollowID: "01H0MKQ0KA29C6NFJ27GTZD16J", // random id, doesn't exist 232 }, 233 } 234 235 if err := suite.db.PutListEntries(ctx, listEntries); err != nil { 236 suite.FailNow(err.Error()) 237 } 238 239 // Add these entries to the test list, sort it again 240 // to reflect what we'd expect to get from the db. 241 testList.ListEntries = append(testList.ListEntries, listEntries...) 242 slices.SortFunc(testList.ListEntries, func(a, b *gtsmodel.ListEntry) bool { 243 return b.ID < a.ID 244 }) 245 246 // Now get all list entries from the db. 247 // Use barebones for this because the ones 248 // we just added will fail if we try to get 249 // the nonexistent follows. 250 dbListEntries, err := suite.db.GetListEntries( 251 gtscontext.SetBarebones(ctx), 252 testList.ID, 253 "", "", "", 0) 254 if err != nil { 255 suite.FailNow(err.Error()) 256 } 257 258 suite.checkListEntries(testList.ListEntries, dbListEntries) 259 } 260 261 func (suite *ListTestSuite) TestDeleteListEntry() { 262 ctx := context.Background() 263 testList, _ := suite.testStructs() 264 265 // Get List in the cache first. 266 if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { 267 suite.FailNow(err.Error()) 268 } 269 270 // Delete the first entry. 271 if err := suite.db.DeleteListEntry(ctx, testList.ListEntries[0].ID); err != nil { 272 suite.FailNow(err.Error()) 273 } 274 275 // Get list from the db again. 276 dbList, err := suite.db.GetListByID(ctx, testList.ID) 277 if err != nil { 278 suite.FailNow(err.Error()) 279 } 280 281 // Bodge the testlist as though 282 // we'd removed the first entry. 283 testList.ListEntries = testList.ListEntries[1:] 284 suite.checkList(testList, dbList) 285 } 286 287 func (suite *ListTestSuite) TestDeleteListEntriesForFollowID() { 288 ctx := context.Background() 289 testList, _ := suite.testStructs() 290 291 // Get List in the cache first. 292 if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { 293 suite.FailNow(err.Error()) 294 } 295 296 // Delete the first entry. 297 if err := suite.db.DeleteListEntriesForFollowID(ctx, testList.ListEntries[0].FollowID); err != nil { 298 suite.FailNow(err.Error()) 299 } 300 301 // Get list from the db again. 302 dbList, err := suite.db.GetListByID(ctx, testList.ID) 303 if err != nil { 304 suite.FailNow(err.Error()) 305 } 306 307 // Bodge the testlist as though 308 // we'd removed the first entry. 309 testList.ListEntries = testList.ListEntries[1:] 310 suite.checkList(testList, dbList) 311 } 312 313 func TestListTestSuite(t *testing.T) { 314 suite.Run(t, new(ListTestSuite)) 315 }