code.gitea.io/gitea@v1.22.3/modules/indexer/issues/internal/tests/tests.go (about) 1 // Copyright 2023 The Gitea Authors. All rights reserved. 2 // SPDX-License-Identifier: MIT 3 4 // This package contains tests for issues indexer modules. 5 // All the code in this package is only used for testing. 6 // Do not put any production code in this package to avoid it being included in the final binary. 7 8 package tests 9 10 import ( 11 "context" 12 "fmt" 13 "slices" 14 "testing" 15 "time" 16 17 "code.gitea.io/gitea/models/db" 18 "code.gitea.io/gitea/modules/indexer/issues/internal" 19 "code.gitea.io/gitea/modules/optional" 20 "code.gitea.io/gitea/modules/timeutil" 21 22 "github.com/stretchr/testify/assert" 23 "github.com/stretchr/testify/require" 24 ) 25 26 func TestIndexer(t *testing.T, indexer internal.Indexer) { 27 _, err := indexer.Init(context.Background()) 28 require.NoError(t, err) 29 30 require.NoError(t, indexer.Ping(context.Background())) 31 32 var ( 33 ids []int64 34 data = map[int64]*internal.IndexerData{} 35 ) 36 { 37 d := generateDefaultIndexerData() 38 for _, v := range d { 39 ids = append(ids, v.ID) 40 data[v.ID] = v 41 } 42 require.NoError(t, indexer.Index(context.Background(), d...)) 43 require.NoError(t, waitData(indexer, int64(len(data)))) 44 } 45 46 defer func() { 47 require.NoError(t, indexer.Delete(context.Background(), ids...)) 48 }() 49 50 for _, c := range cases { 51 t.Run(c.Name, func(t *testing.T) { 52 if len(c.ExtraData) > 0 { 53 require.NoError(t, indexer.Index(context.Background(), c.ExtraData...)) 54 for _, v := range c.ExtraData { 55 data[v.ID] = v 56 } 57 require.NoError(t, waitData(indexer, int64(len(data)))) 58 defer func() { 59 for _, v := range c.ExtraData { 60 require.NoError(t, indexer.Delete(context.Background(), v.ID)) 61 delete(data, v.ID) 62 } 63 require.NoError(t, waitData(indexer, int64(len(data)))) 64 }() 65 } 66 67 result, err := indexer.Search(context.Background(), c.SearchOptions) 68 require.NoError(t, err) 69 70 if c.Expected != nil { 71 c.Expected(t, data, result) 72 } else { 73 ids := make([]int64, 0, len(result.Hits)) 74 for _, hit := range result.Hits { 75 ids = append(ids, hit.ID) 76 } 77 assert.Equal(t, c.ExpectedIDs, ids) 78 assert.Equal(t, c.ExpectedTotal, result.Total) 79 } 80 81 // test counting 82 c.SearchOptions.Paginator = &db.ListOptions{PageSize: 0} 83 countResult, err := indexer.Search(context.Background(), c.SearchOptions) 84 require.NoError(t, err) 85 assert.Empty(t, countResult.Hits) 86 assert.Equal(t, result.Total, countResult.Total) 87 }) 88 } 89 } 90 91 var cases = []*testIndexerCase{ 92 { 93 Name: "default", 94 SearchOptions: &internal.SearchOptions{}, 95 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 96 assert.Equal(t, len(data), len(result.Hits)) 97 assert.Equal(t, len(data), int(result.Total)) 98 }, 99 }, 100 { 101 Name: "empty", 102 SearchOptions: &internal.SearchOptions{ 103 Keyword: "f1dfac73-fda6-4a6b-b8a4-2408fcb8ef69", 104 }, 105 ExpectedIDs: []int64{}, 106 ExpectedTotal: 0, 107 }, 108 { 109 Name: "with limit", 110 SearchOptions: &internal.SearchOptions{ 111 Paginator: &db.ListOptions{ 112 PageSize: 5, 113 }, 114 }, 115 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 116 assert.Equal(t, 5, len(result.Hits)) 117 assert.Equal(t, len(data), int(result.Total)) 118 }, 119 }, 120 { 121 Name: "Keyword", 122 ExtraData: []*internal.IndexerData{ 123 {ID: 1000, Title: "hi hello world"}, 124 {ID: 1001, Content: "hi hello world"}, 125 {ID: 1002, Comments: []string{"hi", "hello world"}}, 126 }, 127 SearchOptions: &internal.SearchOptions{ 128 Keyword: "hello", 129 }, 130 ExpectedIDs: []int64{1002, 1001, 1000}, 131 ExpectedTotal: 3, 132 }, 133 { 134 Name: "RepoIDs", 135 ExtraData: []*internal.IndexerData{ 136 {ID: 1001, Title: "hello world", RepoID: 1, IsPublic: false}, 137 {ID: 1002, Title: "hello world", RepoID: 1, IsPublic: false}, 138 {ID: 1003, Title: "hello world", RepoID: 2, IsPublic: true}, 139 {ID: 1004, Title: "hello world", RepoID: 2, IsPublic: true}, 140 {ID: 1005, Title: "hello world", RepoID: 3, IsPublic: true}, 141 {ID: 1006, Title: "hello world", RepoID: 4, IsPublic: false}, 142 {ID: 1007, Title: "hello world", RepoID: 5, IsPublic: false}, 143 }, 144 SearchOptions: &internal.SearchOptions{ 145 Keyword: "hello", 146 RepoIDs: []int64{1, 4}, 147 }, 148 ExpectedIDs: []int64{1006, 1002, 1001}, 149 ExpectedTotal: 3, 150 }, 151 { 152 Name: "RepoIDs and AllPublic", 153 ExtraData: []*internal.IndexerData{ 154 {ID: 1001, Title: "hello world", RepoID: 1, IsPublic: false}, 155 {ID: 1002, Title: "hello world", RepoID: 1, IsPublic: false}, 156 {ID: 1003, Title: "hello world", RepoID: 2, IsPublic: true}, 157 {ID: 1004, Title: "hello world", RepoID: 2, IsPublic: true}, 158 {ID: 1005, Title: "hello world", RepoID: 3, IsPublic: true}, 159 {ID: 1006, Title: "hello world", RepoID: 4, IsPublic: false}, 160 {ID: 1007, Title: "hello world", RepoID: 5, IsPublic: false}, 161 }, 162 SearchOptions: &internal.SearchOptions{ 163 Keyword: "hello", 164 RepoIDs: []int64{1, 4}, 165 AllPublic: true, 166 }, 167 ExpectedIDs: []int64{1006, 1005, 1004, 1003, 1002, 1001}, 168 ExpectedTotal: 6, 169 }, 170 { 171 Name: "issue only", 172 SearchOptions: &internal.SearchOptions{ 173 Paginator: &db.ListOptions{ 174 PageSize: 5, 175 }, 176 IsPull: optional.Some(false), 177 }, 178 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 179 assert.Equal(t, 5, len(result.Hits)) 180 for _, v := range result.Hits { 181 assert.False(t, data[v.ID].IsPull) 182 } 183 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { return !v.IsPull }), result.Total) 184 }, 185 }, 186 { 187 Name: "pull only", 188 SearchOptions: &internal.SearchOptions{ 189 Paginator: &db.ListOptions{ 190 PageSize: 5, 191 }, 192 IsPull: optional.Some(true), 193 }, 194 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 195 assert.Equal(t, 5, len(result.Hits)) 196 for _, v := range result.Hits { 197 assert.True(t, data[v.ID].IsPull) 198 } 199 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { return v.IsPull }), result.Total) 200 }, 201 }, 202 { 203 Name: "opened only", 204 SearchOptions: &internal.SearchOptions{ 205 Paginator: &db.ListOptions{ 206 PageSize: 5, 207 }, 208 IsClosed: optional.Some(false), 209 }, 210 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 211 assert.Equal(t, 5, len(result.Hits)) 212 for _, v := range result.Hits { 213 assert.False(t, data[v.ID].IsClosed) 214 } 215 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { return !v.IsClosed }), result.Total) 216 }, 217 }, 218 { 219 Name: "closed only", 220 SearchOptions: &internal.SearchOptions{ 221 Paginator: &db.ListOptions{ 222 PageSize: 5, 223 }, 224 IsClosed: optional.Some(true), 225 }, 226 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 227 assert.Equal(t, 5, len(result.Hits)) 228 for _, v := range result.Hits { 229 assert.True(t, data[v.ID].IsClosed) 230 } 231 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { return v.IsClosed }), result.Total) 232 }, 233 }, 234 { 235 Name: "labels", 236 ExtraData: []*internal.IndexerData{ 237 {ID: 1000, Title: "hello a", LabelIDs: []int64{2000, 2001, 2002}}, 238 {ID: 1001, Title: "hello b", LabelIDs: []int64{2000, 2001}}, 239 {ID: 1002, Title: "hello c", LabelIDs: []int64{2000, 2001, 2003}}, 240 {ID: 1003, Title: "hello d", LabelIDs: []int64{2000}}, 241 {ID: 1004, Title: "hello e", LabelIDs: []int64{}}, 242 }, 243 SearchOptions: &internal.SearchOptions{ 244 Keyword: "hello", 245 IncludedLabelIDs: []int64{2000, 2001}, 246 ExcludedLabelIDs: []int64{2003}, 247 }, 248 ExpectedIDs: []int64{1001, 1000}, 249 ExpectedTotal: 2, 250 }, 251 { 252 Name: "include any labels", 253 ExtraData: []*internal.IndexerData{ 254 {ID: 1000, Title: "hello a", LabelIDs: []int64{2000, 2001, 2002}}, 255 {ID: 1001, Title: "hello b", LabelIDs: []int64{2001}}, 256 {ID: 1002, Title: "hello c", LabelIDs: []int64{2000, 2001, 2003}}, 257 {ID: 1003, Title: "hello d", LabelIDs: []int64{2002}}, 258 {ID: 1004, Title: "hello e", LabelIDs: []int64{}}, 259 }, 260 SearchOptions: &internal.SearchOptions{ 261 Keyword: "hello", 262 IncludedAnyLabelIDs: []int64{2001, 2002}, 263 ExcludedLabelIDs: []int64{2003}, 264 }, 265 ExpectedIDs: []int64{1003, 1001, 1000}, 266 ExpectedTotal: 3, 267 }, 268 { 269 Name: "MilestoneIDs", 270 SearchOptions: &internal.SearchOptions{ 271 Paginator: &db.ListOptions{ 272 PageSize: 5, 273 }, 274 MilestoneIDs: []int64{1, 2, 6}, 275 }, 276 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 277 assert.Equal(t, 5, len(result.Hits)) 278 for _, v := range result.Hits { 279 assert.Contains(t, []int64{1, 2, 6}, data[v.ID].MilestoneID) 280 } 281 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 282 return v.MilestoneID == 1 || v.MilestoneID == 2 || v.MilestoneID == 6 283 }), result.Total) 284 }, 285 }, 286 { 287 Name: "no MilestoneIDs", 288 SearchOptions: &internal.SearchOptions{ 289 Paginator: &db.ListOptions{ 290 PageSize: 5, 291 }, 292 MilestoneIDs: []int64{0}, 293 }, 294 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 295 assert.Equal(t, 5, len(result.Hits)) 296 for _, v := range result.Hits { 297 assert.Equal(t, int64(0), data[v.ID].MilestoneID) 298 } 299 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 300 return v.MilestoneID == 0 301 }), result.Total) 302 }, 303 }, 304 { 305 Name: "ProjectID", 306 SearchOptions: &internal.SearchOptions{ 307 Paginator: &db.ListOptions{ 308 PageSize: 5, 309 }, 310 ProjectID: optional.Some(int64(1)), 311 }, 312 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 313 assert.Equal(t, 5, len(result.Hits)) 314 for _, v := range result.Hits { 315 assert.Equal(t, int64(1), data[v.ID].ProjectID) 316 } 317 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 318 return v.ProjectID == 1 319 }), result.Total) 320 }, 321 }, 322 { 323 Name: "no ProjectID", 324 SearchOptions: &internal.SearchOptions{ 325 Paginator: &db.ListOptions{ 326 PageSize: 5, 327 }, 328 ProjectID: optional.Some(int64(0)), 329 }, 330 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 331 assert.Equal(t, 5, len(result.Hits)) 332 for _, v := range result.Hits { 333 assert.Equal(t, int64(0), data[v.ID].ProjectID) 334 } 335 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 336 return v.ProjectID == 0 337 }), result.Total) 338 }, 339 }, 340 { 341 Name: "ProjectBoardID", 342 SearchOptions: &internal.SearchOptions{ 343 Paginator: &db.ListOptions{ 344 PageSize: 5, 345 }, 346 ProjectBoardID: optional.Some(int64(1)), 347 }, 348 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 349 assert.Equal(t, 5, len(result.Hits)) 350 for _, v := range result.Hits { 351 assert.Equal(t, int64(1), data[v.ID].ProjectBoardID) 352 } 353 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 354 return v.ProjectBoardID == 1 355 }), result.Total) 356 }, 357 }, 358 { 359 Name: "no ProjectBoardID", 360 SearchOptions: &internal.SearchOptions{ 361 Paginator: &db.ListOptions{ 362 PageSize: 5, 363 }, 364 ProjectBoardID: optional.Some(int64(0)), 365 }, 366 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 367 assert.Equal(t, 5, len(result.Hits)) 368 for _, v := range result.Hits { 369 assert.Equal(t, int64(0), data[v.ID].ProjectBoardID) 370 } 371 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 372 return v.ProjectBoardID == 0 373 }), result.Total) 374 }, 375 }, 376 { 377 Name: "PosterID", 378 SearchOptions: &internal.SearchOptions{ 379 Paginator: &db.ListOptions{ 380 PageSize: 5, 381 }, 382 PosterID: optional.Some(int64(1)), 383 }, 384 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 385 assert.Equal(t, 5, len(result.Hits)) 386 for _, v := range result.Hits { 387 assert.Equal(t, int64(1), data[v.ID].PosterID) 388 } 389 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 390 return v.PosterID == 1 391 }), result.Total) 392 }, 393 }, 394 { 395 Name: "AssigneeID", 396 SearchOptions: &internal.SearchOptions{ 397 Paginator: &db.ListOptions{ 398 PageSize: 5, 399 }, 400 AssigneeID: optional.Some(int64(1)), 401 }, 402 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 403 assert.Equal(t, 5, len(result.Hits)) 404 for _, v := range result.Hits { 405 assert.Equal(t, int64(1), data[v.ID].AssigneeID) 406 } 407 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 408 return v.AssigneeID == 1 409 }), result.Total) 410 }, 411 }, 412 { 413 Name: "no AssigneeID", 414 SearchOptions: &internal.SearchOptions{ 415 Paginator: &db.ListOptions{ 416 PageSize: 5, 417 }, 418 AssigneeID: optional.Some(int64(0)), 419 }, 420 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 421 assert.Equal(t, 5, len(result.Hits)) 422 for _, v := range result.Hits { 423 assert.Equal(t, int64(0), data[v.ID].AssigneeID) 424 } 425 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 426 return v.AssigneeID == 0 427 }), result.Total) 428 }, 429 }, 430 { 431 Name: "MentionID", 432 SearchOptions: &internal.SearchOptions{ 433 Paginator: &db.ListOptions{ 434 PageSize: 5, 435 }, 436 MentionID: optional.Some(int64(1)), 437 }, 438 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 439 assert.Equal(t, 5, len(result.Hits)) 440 for _, v := range result.Hits { 441 assert.Contains(t, data[v.ID].MentionIDs, int64(1)) 442 } 443 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 444 return slices.Contains(v.MentionIDs, 1) 445 }), result.Total) 446 }, 447 }, 448 { 449 Name: "ReviewedID", 450 SearchOptions: &internal.SearchOptions{ 451 Paginator: &db.ListOptions{ 452 PageSize: 5, 453 }, 454 ReviewedID: optional.Some(int64(1)), 455 }, 456 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 457 assert.Equal(t, 5, len(result.Hits)) 458 for _, v := range result.Hits { 459 assert.Contains(t, data[v.ID].ReviewedIDs, int64(1)) 460 } 461 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 462 return slices.Contains(v.ReviewedIDs, 1) 463 }), result.Total) 464 }, 465 }, 466 { 467 Name: "ReviewRequestedID", 468 SearchOptions: &internal.SearchOptions{ 469 Paginator: &db.ListOptions{ 470 PageSize: 5, 471 }, 472 ReviewRequestedID: optional.Some(int64(1)), 473 }, 474 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 475 assert.Equal(t, 5, len(result.Hits)) 476 for _, v := range result.Hits { 477 assert.Contains(t, data[v.ID].ReviewRequestedIDs, int64(1)) 478 } 479 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 480 return slices.Contains(v.ReviewRequestedIDs, 1) 481 }), result.Total) 482 }, 483 }, 484 { 485 Name: "SubscriberID", 486 SearchOptions: &internal.SearchOptions{ 487 Paginator: &db.ListOptions{ 488 PageSize: 5, 489 }, 490 SubscriberID: optional.Some(int64(1)), 491 }, 492 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 493 assert.Equal(t, 5, len(result.Hits)) 494 for _, v := range result.Hits { 495 assert.Contains(t, data[v.ID].SubscriberIDs, int64(1)) 496 } 497 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 498 return slices.Contains(v.SubscriberIDs, 1) 499 }), result.Total) 500 }, 501 }, 502 { 503 Name: "updated", 504 SearchOptions: &internal.SearchOptions{ 505 Paginator: &db.ListOptions{ 506 PageSize: 5, 507 }, 508 UpdatedAfterUnix: optional.Some(int64(20)), 509 UpdatedBeforeUnix: optional.Some(int64(30)), 510 }, 511 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 512 assert.Equal(t, 5, len(result.Hits)) 513 for _, v := range result.Hits { 514 assert.GreaterOrEqual(t, data[v.ID].UpdatedUnix, int64(20)) 515 assert.LessOrEqual(t, data[v.ID].UpdatedUnix, int64(30)) 516 } 517 assert.Equal(t, countIndexerData(data, func(v *internal.IndexerData) bool { 518 return data[v.ID].UpdatedUnix >= 20 && data[v.ID].UpdatedUnix <= 30 519 }), result.Total) 520 }, 521 }, 522 { 523 Name: "SortByCreatedDesc", 524 SearchOptions: &internal.SearchOptions{ 525 Paginator: &db.ListOptionsAll, 526 SortBy: internal.SortByCreatedDesc, 527 }, 528 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 529 assert.Equal(t, len(data), len(result.Hits)) 530 assert.Equal(t, len(data), int(result.Total)) 531 for i, v := range result.Hits { 532 if i < len(result.Hits)-1 { 533 assert.GreaterOrEqual(t, data[v.ID].CreatedUnix, data[result.Hits[i+1].ID].CreatedUnix) 534 } 535 } 536 }, 537 }, 538 { 539 Name: "SortByUpdatedDesc", 540 SearchOptions: &internal.SearchOptions{ 541 Paginator: &db.ListOptionsAll, 542 SortBy: internal.SortByUpdatedDesc, 543 }, 544 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 545 assert.Equal(t, len(data), len(result.Hits)) 546 assert.Equal(t, len(data), int(result.Total)) 547 for i, v := range result.Hits { 548 if i < len(result.Hits)-1 { 549 assert.GreaterOrEqual(t, data[v.ID].UpdatedUnix, data[result.Hits[i+1].ID].UpdatedUnix) 550 } 551 } 552 }, 553 }, 554 { 555 Name: "SortByCommentsDesc", 556 SearchOptions: &internal.SearchOptions{ 557 Paginator: &db.ListOptionsAll, 558 SortBy: internal.SortByCommentsDesc, 559 }, 560 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 561 assert.Equal(t, len(data), len(result.Hits)) 562 assert.Equal(t, len(data), int(result.Total)) 563 for i, v := range result.Hits { 564 if i < len(result.Hits)-1 { 565 assert.GreaterOrEqual(t, data[v.ID].CommentCount, data[result.Hits[i+1].ID].CommentCount) 566 } 567 } 568 }, 569 }, 570 { 571 Name: "SortByDeadlineDesc", 572 SearchOptions: &internal.SearchOptions{ 573 Paginator: &db.ListOptionsAll, 574 SortBy: internal.SortByDeadlineDesc, 575 }, 576 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 577 assert.Equal(t, len(data), len(result.Hits)) 578 assert.Equal(t, len(data), int(result.Total)) 579 for i, v := range result.Hits { 580 if i < len(result.Hits)-1 { 581 assert.GreaterOrEqual(t, data[v.ID].DeadlineUnix, data[result.Hits[i+1].ID].DeadlineUnix) 582 } 583 } 584 }, 585 }, 586 { 587 Name: "SortByCreatedAsc", 588 SearchOptions: &internal.SearchOptions{ 589 Paginator: &db.ListOptionsAll, 590 SortBy: internal.SortByCreatedAsc, 591 }, 592 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 593 assert.Equal(t, len(data), len(result.Hits)) 594 assert.Equal(t, len(data), int(result.Total)) 595 for i, v := range result.Hits { 596 if i < len(result.Hits)-1 { 597 assert.LessOrEqual(t, data[v.ID].CreatedUnix, data[result.Hits[i+1].ID].CreatedUnix) 598 } 599 } 600 }, 601 }, 602 { 603 Name: "SortByUpdatedAsc", 604 SearchOptions: &internal.SearchOptions{ 605 Paginator: &db.ListOptionsAll, 606 SortBy: internal.SortByUpdatedAsc, 607 }, 608 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 609 assert.Equal(t, len(data), len(result.Hits)) 610 assert.Equal(t, len(data), int(result.Total)) 611 for i, v := range result.Hits { 612 if i < len(result.Hits)-1 { 613 assert.LessOrEqual(t, data[v.ID].UpdatedUnix, data[result.Hits[i+1].ID].UpdatedUnix) 614 } 615 } 616 }, 617 }, 618 { 619 Name: "SortByCommentsAsc", 620 SearchOptions: &internal.SearchOptions{ 621 Paginator: &db.ListOptionsAll, 622 SortBy: internal.SortByCommentsAsc, 623 }, 624 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 625 assert.Equal(t, len(data), len(result.Hits)) 626 assert.Equal(t, len(data), int(result.Total)) 627 for i, v := range result.Hits { 628 if i < len(result.Hits)-1 { 629 assert.LessOrEqual(t, data[v.ID].CommentCount, data[result.Hits[i+1].ID].CommentCount) 630 } 631 } 632 }, 633 }, 634 { 635 Name: "SortByDeadlineAsc", 636 SearchOptions: &internal.SearchOptions{ 637 Paginator: &db.ListOptionsAll, 638 SortBy: internal.SortByDeadlineAsc, 639 }, 640 Expected: func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) { 641 assert.Equal(t, len(data), len(result.Hits)) 642 assert.Equal(t, len(data), int(result.Total)) 643 for i, v := range result.Hits { 644 if i < len(result.Hits)-1 { 645 assert.LessOrEqual(t, data[v.ID].DeadlineUnix, data[result.Hits[i+1].ID].DeadlineUnix) 646 } 647 } 648 }, 649 }, 650 } 651 652 type testIndexerCase struct { 653 Name string 654 ExtraData []*internal.IndexerData 655 656 SearchOptions *internal.SearchOptions 657 658 Expected func(t *testing.T, data map[int64]*internal.IndexerData, result *internal.SearchResult) // if nil, use ExpectedIDs, ExpectedTotal 659 ExpectedIDs []int64 660 ExpectedTotal int64 661 } 662 663 func generateDefaultIndexerData() []*internal.IndexerData { 664 var id int64 665 var data []*internal.IndexerData 666 for repoID := int64(1); repoID <= 10; repoID++ { 667 for issueIndex := int64(1); issueIndex <= 20; issueIndex++ { 668 id++ 669 670 comments := make([]string, id%4) 671 for i := range comments { 672 comments[i] = fmt.Sprintf("comment%d", i) 673 } 674 675 labelIDs := make([]int64, id%5) 676 for i := range labelIDs { 677 labelIDs[i] = int64(i) + 1 // LabelID should not be 0 678 } 679 mentionIDs := make([]int64, id%6) 680 for i := range mentionIDs { 681 mentionIDs[i] = int64(i) + 1 // MentionID should not be 0 682 } 683 reviewedIDs := make([]int64, id%7) 684 for i := range reviewedIDs { 685 reviewedIDs[i] = int64(i) + 1 // ReviewID should not be 0 686 } 687 reviewRequestedIDs := make([]int64, id%8) 688 for i := range reviewRequestedIDs { 689 reviewRequestedIDs[i] = int64(i) + 1 // ReviewRequestedID should not be 0 690 } 691 subscriberIDs := make([]int64, id%9) 692 for i := range subscriberIDs { 693 subscriberIDs[i] = int64(i) + 1 // SubscriberID should not be 0 694 } 695 696 data = append(data, &internal.IndexerData{ 697 ID: id, 698 RepoID: repoID, 699 IsPublic: repoID%2 == 0, 700 Title: fmt.Sprintf("issue%d of repo%d", issueIndex, repoID), 701 Content: fmt.Sprintf("content%d", issueIndex), 702 Comments: comments, 703 IsPull: issueIndex%2 == 0, 704 IsClosed: issueIndex%3 == 0, 705 LabelIDs: labelIDs, 706 NoLabel: len(labelIDs) == 0, 707 MilestoneID: issueIndex % 4, 708 ProjectID: issueIndex % 5, 709 ProjectBoardID: issueIndex % 6, 710 PosterID: id%10 + 1, // PosterID should not be 0 711 AssigneeID: issueIndex % 10, 712 MentionIDs: mentionIDs, 713 ReviewedIDs: reviewedIDs, 714 ReviewRequestedIDs: reviewRequestedIDs, 715 SubscriberIDs: subscriberIDs, 716 UpdatedUnix: timeutil.TimeStamp(id + issueIndex), 717 CreatedUnix: timeutil.TimeStamp(id), 718 DeadlineUnix: timeutil.TimeStamp(id + issueIndex + repoID), 719 CommentCount: int64(len(comments)), 720 }) 721 } 722 } 723 724 return data 725 } 726 727 func countIndexerData(data map[int64]*internal.IndexerData, f func(v *internal.IndexerData) bool) int64 { 728 var count int64 729 for _, v := range data { 730 if f(v) { 731 count++ 732 } 733 } 734 return count 735 } 736 737 // waitData waits for the indexer to index all data. 738 // Some engines like Elasticsearch index data asynchronously, so we need to wait for a while. 739 func waitData(indexer internal.Indexer, total int64) error { 740 var actual int64 741 for i := 0; i < 100; i++ { 742 result, err := indexer.Search(context.Background(), &internal.SearchOptions{ 743 Paginator: &db.ListOptions{ 744 PageSize: 0, 745 }, 746 }) 747 if err != nil { 748 return err 749 } 750 actual = result.Total 751 if actual == total { 752 return nil 753 } 754 time.Sleep(100 * time.Millisecond) 755 } 756 return fmt.Errorf("waitData: expected %d, actual %d", total, actual) 757 }