gtsocial-umbx

Unnamed repository; edit this file 'description' to name the repository.
Log | Files | Refs | README | LICENSE

get_test.go (15712B)


      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 timeline_test
     19 
     20 import (
     21 	"context"
     22 	"sync"
     23 	"testing"
     24 
     25 	"github.com/stretchr/testify/suite"
     26 	"github.com/superseriousbusiness/gotosocial/internal/gtscontext"
     27 	"github.com/superseriousbusiness/gotosocial/internal/id"
     28 	"github.com/superseriousbusiness/gotosocial/internal/timeline"
     29 )
     30 
     31 type GetTestSuite struct {
     32 	TimelineStandardTestSuite
     33 }
     34 
     35 func (suite *GetTestSuite) checkStatuses(statuses []timeline.Preparable, maxID string, minID string, expectedLength int) {
     36 	if l := len(statuses); l != expectedLength {
     37 		suite.FailNow("", "expected %d statuses in slice, got %d", expectedLength, l)
     38 	} else if l == 0 {
     39 		// Can't test empty slice.
     40 		return
     41 	}
     42 
     43 	// Check ordering + bounds of statuses.
     44 	highest := statuses[0].GetID()
     45 	for _, status := range statuses {
     46 		id := status.GetID()
     47 
     48 		if id >= maxID {
     49 			suite.FailNow("", "%s greater than maxID %s", id, maxID)
     50 		}
     51 
     52 		if id <= minID {
     53 			suite.FailNow("", "%s smaller than minID %s", id, minID)
     54 		}
     55 
     56 		if id > highest {
     57 			suite.FailNow("", "statuses in slice were not ordered highest -> lowest ID")
     58 		}
     59 
     60 		highest = id
     61 	}
     62 }
     63 
     64 func (suite *GetTestSuite) emptyAccountFollows(ctx context.Context, accountID string) {
     65 	// Get all of account's follows.
     66 	follows, err := suite.state.DB.GetAccountFollows(
     67 		gtscontext.SetBarebones(ctx),
     68 		accountID,
     69 	)
     70 	if err != nil {
     71 		suite.FailNow(err.Error())
     72 	}
     73 
     74 	// Remove each follow.
     75 	for _, follow := range follows {
     76 		if err := suite.state.DB.DeleteFollowByID(ctx, follow.ID); err != nil {
     77 			suite.FailNow(err.Error())
     78 		}
     79 	}
     80 
     81 	// Ensure no follows left.
     82 	follows, err = suite.state.DB.GetAccountFollows(
     83 		gtscontext.SetBarebones(ctx),
     84 		accountID,
     85 	)
     86 	if err != nil {
     87 		suite.FailNow(err.Error())
     88 	}
     89 	if len(follows) != 0 {
     90 		suite.FailNow("follows should be empty")
     91 	}
     92 }
     93 
     94 func (suite *GetTestSuite) emptyAccountStatuses(ctx context.Context, accountID string) {
     95 	// Get all of account's statuses.
     96 	statuses, err := suite.state.DB.GetAccountStatuses(
     97 		ctx,
     98 		accountID,
     99 		9999,
    100 		false,
    101 		false,
    102 		id.Highest,
    103 		id.Lowest,
    104 		false,
    105 		false,
    106 	)
    107 	if err != nil {
    108 		suite.FailNow(err.Error())
    109 	}
    110 
    111 	// Remove each status.
    112 	for _, status := range statuses {
    113 		if err := suite.state.DB.DeleteStatusByID(ctx, status.ID); err != nil {
    114 			suite.FailNow(err.Error())
    115 		}
    116 	}
    117 }
    118 
    119 func (suite *GetTestSuite) TestGetNewTimelinePageDown() {
    120 	var (
    121 		ctx         = context.Background()
    122 		testAccount = suite.testAccounts["local_account_1"]
    123 		maxID       = ""
    124 		sinceID     = ""
    125 		minID       = ""
    126 		limit       = 5
    127 		local       = false
    128 	)
    129 
    130 	// Get 5 from the top.
    131 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    132 		ctx,
    133 		testAccount.ID,
    134 		maxID,
    135 		sinceID,
    136 		minID,
    137 		limit,
    138 		local,
    139 	)
    140 	if err != nil {
    141 		suite.FailNow(err.Error())
    142 	}
    143 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 5)
    144 
    145 	// Get 5 from next maxID.
    146 	maxID = statuses[len(statuses)-1].GetID()
    147 	statuses, err = suite.state.Timelines.Home.GetTimeline(
    148 		ctx,
    149 		testAccount.ID,
    150 		maxID,
    151 		sinceID,
    152 		minID,
    153 		limit,
    154 		local,
    155 	)
    156 	if err != nil {
    157 		suite.FailNow(err.Error())
    158 	}
    159 	suite.checkStatuses(statuses, maxID, id.Lowest, 5)
    160 }
    161 
    162 func (suite *GetTestSuite) TestGetNewTimelinePageUp() {
    163 	var (
    164 		ctx         = context.Background()
    165 		testAccount = suite.testAccounts["local_account_1"]
    166 		maxID       = ""
    167 		sinceID     = ""
    168 		minID       = id.Lowest
    169 		limit       = 5
    170 		local       = false
    171 	)
    172 
    173 	// Get 5 from the back.
    174 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    175 		ctx,
    176 		testAccount.ID,
    177 		maxID,
    178 		sinceID,
    179 		minID,
    180 		limit,
    181 		local,
    182 	)
    183 	if err != nil {
    184 		suite.FailNow(err.Error())
    185 	}
    186 	suite.checkStatuses(statuses, id.Highest, minID, 5)
    187 
    188 	// Page up from next minID.
    189 	minID = statuses[0].GetID()
    190 	statuses, err = suite.state.Timelines.Home.GetTimeline(
    191 		ctx,
    192 		testAccount.ID,
    193 		maxID,
    194 		sinceID,
    195 		minID,
    196 		limit,
    197 		local,
    198 	)
    199 	if err != nil {
    200 		suite.FailNow(err.Error())
    201 	}
    202 	suite.checkStatuses(statuses, id.Highest, minID, 5)
    203 }
    204 
    205 func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossible() {
    206 	var (
    207 		ctx         = context.Background()
    208 		testAccount = suite.testAccounts["local_account_1"]
    209 		maxID       = ""
    210 		sinceID     = ""
    211 		minID       = ""
    212 		limit       = 100
    213 		local       = false
    214 	)
    215 
    216 	// Get 100 from the top.
    217 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    218 		ctx,
    219 		testAccount.ID,
    220 		maxID,
    221 		sinceID,
    222 		minID,
    223 		limit,
    224 		local,
    225 	)
    226 	if err != nil {
    227 		suite.FailNow(err.Error())
    228 	}
    229 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 16)
    230 }
    231 
    232 func (suite *GetTestSuite) TestGetNewTimelineMoreThanPossiblePageUp() {
    233 	var (
    234 		ctx         = context.Background()
    235 		testAccount = suite.testAccounts["local_account_1"]
    236 		maxID       = ""
    237 		sinceID     = ""
    238 		minID       = id.Lowest
    239 		limit       = 100
    240 		local       = false
    241 	)
    242 
    243 	// Get 100 from the back.
    244 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    245 		ctx,
    246 		testAccount.ID,
    247 		maxID,
    248 		sinceID,
    249 		minID,
    250 		limit,
    251 		local,
    252 	)
    253 	if err != nil {
    254 		suite.FailNow(err.Error())
    255 	}
    256 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 16)
    257 }
    258 
    259 func (suite *GetTestSuite) TestGetNewTimelineNoFollowing() {
    260 	var (
    261 		ctx         = context.Background()
    262 		testAccount = suite.testAccounts["local_account_1"]
    263 		maxID       = ""
    264 		sinceID     = ""
    265 		minID       = ""
    266 		limit       = 10
    267 		local       = false
    268 	)
    269 
    270 	suite.emptyAccountFollows(ctx, testAccount.ID)
    271 
    272 	// Try to get 10 from the top of the timeline.
    273 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    274 		ctx,
    275 		testAccount.ID,
    276 		maxID,
    277 		sinceID,
    278 		minID,
    279 		limit,
    280 		local,
    281 	)
    282 	if err != nil {
    283 		suite.FailNow(err.Error())
    284 	}
    285 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 5)
    286 
    287 	for _, s := range statuses {
    288 		if s.GetAccountID() != testAccount.ID {
    289 			suite.FailNow("timeline with no follows should only contain posts by timeline owner account")
    290 		}
    291 	}
    292 }
    293 
    294 func (suite *GetTestSuite) TestGetNewTimelineNoFollowingNoStatuses() {
    295 	var (
    296 		ctx         = context.Background()
    297 		testAccount = suite.testAccounts["local_account_1"]
    298 		maxID       = ""
    299 		sinceID     = ""
    300 		minID       = ""
    301 		limit       = 5
    302 		local       = false
    303 	)
    304 
    305 	suite.emptyAccountFollows(ctx, testAccount.ID)
    306 	suite.emptyAccountStatuses(ctx, testAccount.ID)
    307 
    308 	// Try to get 5 from the top of the timeline.
    309 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    310 		ctx,
    311 		testAccount.ID,
    312 		maxID,
    313 		sinceID,
    314 		minID,
    315 		limit,
    316 		local,
    317 	)
    318 	if err != nil {
    319 		suite.FailNow(err.Error())
    320 	}
    321 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 0)
    322 }
    323 
    324 func (suite *GetTestSuite) TestGetNoParams() {
    325 	var (
    326 		ctx         = context.Background()
    327 		testAccount = suite.testAccounts["local_account_1"]
    328 		maxID       = ""
    329 		sinceID     = ""
    330 		minID       = ""
    331 		limit       = 10
    332 		local       = false
    333 	)
    334 
    335 	suite.fillTimeline(testAccount.ID)
    336 
    337 	// Get 10 statuses from the top (no params).
    338 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    339 		ctx,
    340 		testAccount.ID,
    341 		maxID,
    342 		sinceID,
    343 		minID,
    344 		limit,
    345 		local,
    346 	)
    347 	if err != nil {
    348 		suite.FailNow(err.Error())
    349 	}
    350 
    351 	suite.checkStatuses(statuses, id.Highest, id.Lowest, 10)
    352 
    353 	// First status should have the highest ID in the testrig.
    354 	suite.Equal(suite.highestStatusID, statuses[0].GetID())
    355 }
    356 
    357 func (suite *GetTestSuite) TestGetMaxID() {
    358 	var (
    359 		ctx         = context.Background()
    360 		testAccount = suite.testAccounts["local_account_1"]
    361 		maxID       = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    362 		sinceID     = ""
    363 		minID       = ""
    364 		limit       = 10
    365 		local       = false
    366 	)
    367 
    368 	suite.fillTimeline(testAccount.ID)
    369 
    370 	// Ask for 10 with a max ID somewhere in the middle of the stack.
    371 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    372 		ctx,
    373 		testAccount.ID,
    374 		maxID,
    375 		sinceID,
    376 		minID,
    377 		limit,
    378 		local,
    379 	)
    380 	if err != nil {
    381 		suite.FailNow(err.Error())
    382 	}
    383 
    384 	// We'll only get 6 statuses back.
    385 	suite.checkStatuses(statuses, maxID, id.Lowest, 6)
    386 }
    387 
    388 func (suite *GetTestSuite) TestGetSinceID() {
    389 	var (
    390 		ctx         = context.Background()
    391 		testAccount = suite.testAccounts["local_account_1"]
    392 		maxID       = ""
    393 		sinceID     = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    394 		minID       = ""
    395 		limit       = 10
    396 		local       = false
    397 	)
    398 
    399 	suite.fillTimeline(testAccount.ID)
    400 
    401 	// Ask for 10 with a since ID somewhere in the middle of the stack.
    402 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    403 		ctx,
    404 		testAccount.ID,
    405 		maxID,
    406 		sinceID,
    407 		minID,
    408 		limit,
    409 		local,
    410 	)
    411 	if err != nil {
    412 		suite.FailNow(err.Error())
    413 	}
    414 
    415 	suite.checkStatuses(statuses, id.Highest, sinceID, 10)
    416 
    417 	// The first status in the stack should have the highest ID of all
    418 	// in the testrig, because we're paging down.
    419 	suite.Equal(suite.highestStatusID, statuses[0].GetID())
    420 }
    421 
    422 func (suite *GetTestSuite) TestGetSinceIDOneOnly() {
    423 	var (
    424 		ctx         = context.Background()
    425 		testAccount = suite.testAccounts["local_account_1"]
    426 		maxID       = ""
    427 		sinceID     = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    428 		minID       = ""
    429 		limit       = 1
    430 		local       = false
    431 	)
    432 
    433 	suite.fillTimeline(testAccount.ID)
    434 
    435 	// Ask for 1 with a since ID somewhere in the middle of the stack.
    436 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    437 		ctx,
    438 		testAccount.ID,
    439 		maxID,
    440 		sinceID,
    441 		minID,
    442 		limit,
    443 		local,
    444 	)
    445 	if err != nil {
    446 		suite.FailNow(err.Error())
    447 	}
    448 
    449 	suite.checkStatuses(statuses, id.Highest, sinceID, 1)
    450 
    451 	// The one status we got back should have the highest ID of all in
    452 	// the testrig, because using sinceID means we're paging down.
    453 	suite.Equal(suite.highestStatusID, statuses[0].GetID())
    454 }
    455 
    456 func (suite *GetTestSuite) TestGetMinID() {
    457 	var (
    458 		ctx         = context.Background()
    459 		testAccount = suite.testAccounts["local_account_1"]
    460 		maxID       = ""
    461 		sinceID     = ""
    462 		minID       = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    463 		limit       = 5
    464 		local       = false
    465 	)
    466 
    467 	suite.fillTimeline(testAccount.ID)
    468 
    469 	// Ask for 5 with a min ID somewhere in the middle of the stack.
    470 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    471 		ctx,
    472 		testAccount.ID,
    473 		maxID,
    474 		sinceID,
    475 		minID,
    476 		limit,
    477 		local,
    478 	)
    479 	if err != nil {
    480 		suite.FailNow(err.Error())
    481 	}
    482 
    483 	suite.checkStatuses(statuses, id.Highest, minID, 5)
    484 
    485 	// We're paging up so even the highest status ID in the pile
    486 	// shouldn't be the highest ID we have.
    487 	suite.NotEqual(suite.highestStatusID, statuses[0])
    488 }
    489 
    490 func (suite *GetTestSuite) TestGetMinIDOneOnly() {
    491 	var (
    492 		ctx         = context.Background()
    493 		testAccount = suite.testAccounts["local_account_1"]
    494 		maxID       = ""
    495 		sinceID     = ""
    496 		minID       = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    497 		limit       = 1
    498 		local       = false
    499 	)
    500 
    501 	suite.fillTimeline(testAccount.ID)
    502 
    503 	// Ask for 1 with a min ID somewhere in the middle of the stack.
    504 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    505 		ctx,
    506 		testAccount.ID,
    507 		maxID,
    508 		sinceID,
    509 		minID,
    510 		limit,
    511 		local,
    512 	)
    513 	if err != nil {
    514 		suite.FailNow(err.Error())
    515 	}
    516 
    517 	suite.checkStatuses(statuses, id.Highest, minID, 1)
    518 
    519 	// The one status we got back should have the an ID equal to the
    520 	// one ID immediately newer than it.
    521 	suite.Equal("01F8MHC0H0A7XHTVH5F596ZKBM", statuses[0].GetID())
    522 }
    523 
    524 func (suite *GetTestSuite) TestGetMinIDFromLowestInTestrig() {
    525 	var (
    526 		ctx         = context.Background()
    527 		testAccount = suite.testAccounts["local_account_1"]
    528 		maxID       = ""
    529 		sinceID     = ""
    530 		minID       = suite.lowestStatusID
    531 		limit       = 1
    532 		local       = false
    533 	)
    534 
    535 	suite.fillTimeline(testAccount.ID)
    536 
    537 	// Ask for 1 with minID equal to the lowest status in the testrig.
    538 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    539 		ctx,
    540 		testAccount.ID,
    541 		maxID,
    542 		sinceID,
    543 		minID,
    544 		limit,
    545 		local,
    546 	)
    547 	if err != nil {
    548 		suite.FailNow(err.Error())
    549 	}
    550 
    551 	suite.checkStatuses(statuses, id.Highest, minID, 1)
    552 
    553 	// The one status we got back should have an id higher than
    554 	// the lowest status in the testrig, since minID is not inclusive.
    555 	suite.Greater(statuses[0].GetID(), suite.lowestStatusID)
    556 }
    557 
    558 func (suite *GetTestSuite) TestGetMinIDFromLowestPossible() {
    559 	var (
    560 		ctx         = context.Background()
    561 		testAccount = suite.testAccounts["local_account_1"]
    562 		maxID       = ""
    563 		sinceID     = ""
    564 		minID       = id.Lowest
    565 		limit       = 1
    566 		local       = false
    567 	)
    568 
    569 	suite.fillTimeline(testAccount.ID)
    570 
    571 	// Ask for 1 with the lowest possible min ID.
    572 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    573 		ctx,
    574 		testAccount.ID,
    575 		maxID,
    576 		sinceID,
    577 		minID,
    578 		limit,
    579 		local,
    580 	)
    581 	if err != nil {
    582 		suite.FailNow(err.Error())
    583 	}
    584 
    585 	suite.checkStatuses(statuses, id.Highest, minID, 1)
    586 
    587 	// The one status we got back should have the an ID equal to the
    588 	// lowest ID status in the test rig.
    589 	suite.Equal(suite.lowestStatusID, statuses[0].GetID())
    590 }
    591 
    592 func (suite *GetTestSuite) TestGetBetweenID() {
    593 	var (
    594 		ctx         = context.Background()
    595 		testAccount = suite.testAccounts["local_account_1"]
    596 		maxID       = "01F8MHCP5P2NWYQ416SBA0XSEV"
    597 		sinceID     = ""
    598 		minID       = "01F8MHBQCBTDKN6X5VHGMMN4MA"
    599 		limit       = 10
    600 		local       = false
    601 	)
    602 
    603 	suite.fillTimeline(testAccount.ID)
    604 
    605 	// Ask for 10 between these two IDs
    606 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    607 		ctx,
    608 		testAccount.ID,
    609 		maxID,
    610 		sinceID,
    611 		minID,
    612 		limit,
    613 		local,
    614 	)
    615 	if err != nil {
    616 		suite.FailNow(err.Error())
    617 	}
    618 
    619 	// There's only two statuses between these two IDs.
    620 	suite.checkStatuses(statuses, maxID, minID, 2)
    621 }
    622 
    623 func (suite *GetTestSuite) TestGetBetweenIDImpossible() {
    624 	var (
    625 		ctx         = context.Background()
    626 		testAccount = suite.testAccounts["local_account_1"]
    627 		maxID       = id.Lowest
    628 		sinceID     = ""
    629 		minID       = id.Highest
    630 		limit       = 10
    631 		local       = false
    632 	)
    633 
    634 	suite.fillTimeline(testAccount.ID)
    635 
    636 	// Ask for 10 between these two IDs which present
    637 	// an impossible query.
    638 	statuses, err := suite.state.Timelines.Home.GetTimeline(
    639 		ctx,
    640 		testAccount.ID,
    641 		maxID,
    642 		sinceID,
    643 		minID,
    644 		limit,
    645 		local,
    646 	)
    647 	if err != nil {
    648 		suite.FailNow(err.Error())
    649 	}
    650 
    651 	// We should have nothing back.
    652 	suite.checkStatuses(statuses, maxID, minID, 0)
    653 }
    654 
    655 func (suite *GetTestSuite) TestGetTimelinesAsync() {
    656 	var (
    657 		ctx           = context.Background()
    658 		accountToNuke = suite.testAccounts["local_account_1"]
    659 		maxID         = ""
    660 		sinceID       = ""
    661 		minID         = ""
    662 		limit         = 5
    663 		local         = false
    664 		multiplier    = 5
    665 	)
    666 
    667 	// Nuke one account's statuses and follows,
    668 	// as though the account had just been created.
    669 	suite.emptyAccountFollows(ctx, accountToNuke.ID)
    670 	suite.emptyAccountStatuses(ctx, accountToNuke.ID)
    671 
    672 	// Get 5 statuses from each timeline in
    673 	// our testrig at the same time, five times.
    674 	wg := new(sync.WaitGroup)
    675 	wg.Add(len(suite.testAccounts) * multiplier)
    676 
    677 	for i := 0; i < multiplier; i++ {
    678 		go func() {
    679 			for _, testAccount := range suite.testAccounts {
    680 				if _, err := suite.state.Timelines.Home.GetTimeline(
    681 					ctx,
    682 					testAccount.ID,
    683 					maxID,
    684 					sinceID,
    685 					minID,
    686 					limit,
    687 					local,
    688 				); err != nil {
    689 					suite.FailNow(err.Error())
    690 				}
    691 
    692 				wg.Done()
    693 			}
    694 		}()
    695 	}
    696 
    697 	wg.Wait() // Wait until all get calls have returned.
    698 }
    699 
    700 func TestGetTestSuite(t *testing.T) {
    701 	suite.Run(t, new(GetTestSuite))
    702 }