mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 08:42:27 -05:00 
			
		
		
		
	
		
			
	
	
		
			316 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
		
		
			
		
	
	
			316 lines
		
	
	
	
		
			8.5 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
|  | // GoToSocial | ||
|  | // Copyright (C) GoToSocial Authors admin@gotosocial.org | ||
|  | // SPDX-License-Identifier: AGPL-3.0-or-later | ||
|  | // | ||
|  | // This program is free software: you can redistribute it and/or modify | ||
|  | // it under the terms of the GNU Affero General Public License as published by | ||
|  | // the Free Software Foundation, either version 3 of the License, or | ||
|  | // (at your option) any later version. | ||
|  | // | ||
|  | // This program is distributed in the hope that it will be useful, | ||
|  | // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
|  | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||
|  | // GNU Affero General Public License for more details. | ||
|  | // | ||
|  | // You should have received a copy of the GNU Affero General Public License | ||
|  | // along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||
|  | 
 | ||
|  | package bundb_test | ||
|  | 
 | ||
|  | import ( | ||
|  | 	"context" | ||
|  | 	"testing" | ||
|  | 
 | ||
|  | 	"github.com/stretchr/testify/suite" | ||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext" | ||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||
|  | 	"golang.org/x/exp/slices" | ||
|  | ) | ||
|  | 
 | ||
|  | type ListTestSuite struct { | ||
|  | 	BunDBStandardTestSuite | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) testStructs() (*gtsmodel.List, *gtsmodel.Account) { | ||
|  | 	testList := >smodel.List{} | ||
|  | 	*testList = *suite.testLists["local_account_1_list_1"] | ||
|  | 
 | ||
|  | 	// Populate entries on this list as we'd expect them back from the db. | ||
|  | 	entries := make([]*gtsmodel.ListEntry, 0, len(suite.testListEntries)) | ||
|  | 	for _, entry := range suite.testListEntries { | ||
|  | 		entries = append(entries, entry) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Sort by ID descending (again, as we'd expect from the db). | ||
|  | 	slices.SortFunc(entries, func(a, b *gtsmodel.ListEntry) bool { | ||
|  | 		return b.ID < a.ID | ||
|  | 	}) | ||
|  | 
 | ||
|  | 	testList.ListEntries = entries | ||
|  | 
 | ||
|  | 	testAccount := >smodel.Account{} | ||
|  | 	*testAccount = *suite.testAccounts["local_account_1"] | ||
|  | 
 | ||
|  | 	return testList, testAccount | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) checkList(expected *gtsmodel.List, actual *gtsmodel.List) { | ||
|  | 	suite.Equal(expected.ID, actual.ID) | ||
|  | 	suite.Equal(expected.Title, actual.Title) | ||
|  | 	suite.Equal(expected.AccountID, actual.AccountID) | ||
|  | 	suite.Equal(expected.RepliesPolicy, actual.RepliesPolicy) | ||
|  | 	suite.NotNil(actual.Account) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) checkListEntry(expected *gtsmodel.ListEntry, actual *gtsmodel.ListEntry) { | ||
|  | 	suite.Equal(expected.ID, actual.ID) | ||
|  | 	suite.Equal(expected.ListID, actual.ListID) | ||
|  | 	suite.Equal(expected.FollowID, actual.FollowID) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) checkListEntries(expected []*gtsmodel.ListEntry, actual []*gtsmodel.ListEntry) { | ||
|  | 	var ( | ||
|  | 		lExpected = len(expected) | ||
|  | 		lActual   = len(actual) | ||
|  | 	) | ||
|  | 
 | ||
|  | 	if lExpected != lActual { | ||
|  | 		suite.FailNow("", "expected %d list entries, got %d", lExpected, lActual) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	var topID string | ||
|  | 	for i, expectedEntry := range expected { | ||
|  | 		actualEntry := actual[i] | ||
|  | 
 | ||
|  | 		// Ensure ID descending. | ||
|  | 		if topID == "" { | ||
|  | 			topID = actualEntry.ID | ||
|  | 		} else { | ||
|  | 			suite.Less(actualEntry.ID, topID) | ||
|  | 		} | ||
|  | 
 | ||
|  | 		suite.checkListEntry(expectedEntry, actualEntry) | ||
|  | 	} | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestGetListByID() { | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	dbList, err := suite.db.GetListByID(context.Background(), testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	suite.checkList(testList, dbList) | ||
|  | 	suite.checkListEntries(testList.ListEntries, dbList.ListEntries) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestGetListsForAccountID() { | ||
|  | 	testList, testAccount := suite.testStructs() | ||
|  | 
 | ||
|  | 	dbLists, err := suite.db.GetListsForAccountID(context.Background(), testAccount.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if l := len(dbLists); l != 1 { | ||
|  | 		suite.FailNow("", "expected %d lists, got %d", 1, l) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	suite.checkList(testList, dbLists[0]) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestGetListEntries() { | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	dbListEntries, err := suite.db.GetListEntries(context.Background(), testList.ID, "", "", "", 0) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	suite.checkListEntries(testList.ListEntries, dbListEntries) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestPutList() { | ||
|  | 	ctx := context.Background() | ||
|  | 	_, testAccount := suite.testStructs() | ||
|  | 
 | ||
|  | 	testList := >smodel.List{ | ||
|  | 		ID:        "01H0J2PMYM54618VCV8Y8QYAT4", | ||
|  | 		Title:     "Test List!", | ||
|  | 		AccountID: testAccount.ID, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if err := suite.db.PutList(ctx, testList); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	dbList, err := suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Bodge testlist as though default had been set. | ||
|  | 	testList.RepliesPolicy = gtsmodel.RepliesPolicyFollowed | ||
|  | 	suite.checkList(testList, dbList) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestUpdateList() { | ||
|  | 	ctx := context.Background() | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	// Get List in the cache first. | ||
|  | 	dbList, err := suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Now do the update. | ||
|  | 	testList.Title = "New Title!" | ||
|  | 	if err := suite.db.UpdateList(ctx, testList, "title"); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Cache should be invalidated | ||
|  | 	// + we should have updated list. | ||
|  | 	dbList, err = suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	suite.checkList(testList, dbList) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestDeleteList() { | ||
|  | 	ctx := context.Background() | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	// Get List in the cache first. | ||
|  | 	if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Now do the delete. | ||
|  | 	if err := suite.db.DeleteListByID(ctx, testList.ID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Cache should be invalidated | ||
|  | 	// + we should have no list. | ||
|  | 	_, err := suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	suite.ErrorIs(err, db.ErrNoEntries) | ||
|  | 
 | ||
|  | 	// All entries belonging to this | ||
|  | 	// list should now be deleted. | ||
|  | 	listEntries, err := suite.db.GetListEntries(ctx, testList.ID, "", "", "", 0) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 	suite.Empty(listEntries) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestPutListEntries() { | ||
|  | 	ctx := context.Background() | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	listEntries := []*gtsmodel.ListEntry{ | ||
|  | 		{ | ||
|  | 			ID:       "01H0MKMQY69HWDSDR2SWGA17R4", | ||
|  | 			ListID:   testList.ID, | ||
|  | 			FollowID: "01H0MKNFRFZS8R9WV6DBX31Y03", // random id, doesn't exist | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			ID:       "01H0MKPGQF0E7QAVW5BKTHZ630", | ||
|  | 			ListID:   testList.ID, | ||
|  | 			FollowID: "01H0MKP6RR8VEHN3GVWFBP2H30", // random id, doesn't exist | ||
|  | 		}, | ||
|  | 		{ | ||
|  | 			ID:       "01H0MKPPP2DT68FRBMR1FJM32T", | ||
|  | 			ListID:   testList.ID, | ||
|  | 			FollowID: "01H0MKQ0KA29C6NFJ27GTZD16J", // random id, doesn't exist | ||
|  | 		}, | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if err := suite.db.PutListEntries(ctx, listEntries); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Add these entries to the test list, sort it again | ||
|  | 	// to reflect what we'd expect to get from the db. | ||
|  | 	testList.ListEntries = append(testList.ListEntries, listEntries...) | ||
|  | 	slices.SortFunc(testList.ListEntries, func(a, b *gtsmodel.ListEntry) bool { | ||
|  | 		return b.ID < a.ID | ||
|  | 	}) | ||
|  | 
 | ||
|  | 	// Now get all list entries from the db. | ||
|  | 	// Use barebones for this because the ones | ||
|  | 	// we just added will fail if we try to get | ||
|  | 	// the nonexistent follows. | ||
|  | 	dbListEntries, err := suite.db.GetListEntries( | ||
|  | 		gtscontext.SetBarebones(ctx), | ||
|  | 		testList.ID, | ||
|  | 		"", "", "", 0) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	suite.checkListEntries(testList.ListEntries, dbListEntries) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestDeleteListEntry() { | ||
|  | 	ctx := context.Background() | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	// Get List in the cache first. | ||
|  | 	if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Delete the first entry. | ||
|  | 	if err := suite.db.DeleteListEntry(ctx, testList.ListEntries[0].ID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Get list from the db again. | ||
|  | 	dbList, err := suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Bodge the testlist as though | ||
|  | 	// we'd removed the first entry. | ||
|  | 	testList.ListEntries = testList.ListEntries[1:] | ||
|  | 	suite.checkList(testList, dbList) | ||
|  | } | ||
|  | 
 | ||
|  | func (suite *ListTestSuite) TestDeleteListEntriesForFollowID() { | ||
|  | 	ctx := context.Background() | ||
|  | 	testList, _ := suite.testStructs() | ||
|  | 
 | ||
|  | 	// Get List in the cache first. | ||
|  | 	if _, err := suite.db.GetListByID(ctx, testList.ID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Delete the first entry. | ||
|  | 	if err := suite.db.DeleteListEntriesForFollowID(ctx, testList.ListEntries[0].FollowID); err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Get list from the db again. | ||
|  | 	dbList, err := suite.db.GetListByID(ctx, testList.ID) | ||
|  | 	if err != nil { | ||
|  | 		suite.FailNow(err.Error()) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Bodge the testlist as though | ||
|  | 	// we'd removed the first entry. | ||
|  | 	testList.ListEntries = testList.ListEntries[1:] | ||
|  | 	suite.checkList(testList, dbList) | ||
|  | } | ||
|  | 
 | ||
|  | func TestListTestSuite(t *testing.T) { | ||
|  | 	suite.Run(t, new(ListTestSuite)) | ||
|  | } |