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 }