eintopf.info@v0.13.16/service/eventsearch/eventsearch_test.go (about) 1 package eventsearch_test 2 3 import ( 4 "context" 5 "testing" 6 "time" 7 8 "eintopf.info/service/auth" 9 "eintopf.info/service/event" 10 "eintopf.info/service/eventsearch" 11 "eintopf.info/service/group" 12 "eintopf.info/service/oqueue" 13 "eintopf.info/service/place" 14 "eintopf.info/service/search" 15 "eintopf.info/test" 16 17 "github.com/google/go-cmp/cmp" 18 "github.com/google/go-cmp/cmp/cmpopts" 19 ) 20 21 func TestAggregation(t *testing.T) { 22 tests := []eventsearchTest{ 23 { 24 name: "InvolvedAggregation", 25 index: []*event.Event{ 26 { 27 ID: "1", 28 Name: "foo", 29 Involved: []event.Involved{ 30 {Name: "involved1", Description: "foo bar foo"}, 31 {Name: "involved2", Description: "bar foo bar"}, 32 }, 33 }, 34 { 35 ID: "2", 36 Name: "bar", 37 Involved: []event.Involved{ 38 {Name: "involved3", Description: "foo bar foo"}, 39 {Name: "involved4", Description: "bar foo bar"}, 40 }, 41 }, 42 }, 43 options: eventsearch.Options{ 44 Filters: []eventsearch.Filter{}, 45 Aggregations: map[string]eventsearch.Aggregation{ 46 "involved": eventsearch.InvolvedObjectsAggregation{}, 47 }, 48 }, 49 wantBuckets: map[string]search.Bucket{ 50 "involved": eventsearch.InvolvedBucket{ 51 {Name: "involved1", Description: "foo bar foo"}, 52 {Name: "involved2", Description: "bar foo bar"}, 53 {Name: "involved3", Description: "foo bar foo"}, 54 {Name: "involved4", Description: "bar foo bar"}, 55 }, 56 }, 57 }, 58 } 59 testEventSearch(t, tests) 60 } 61 62 func TestFilter(t *testing.T) { 63 tests := []eventsearchTest{ 64 { 65 name: "GroupFilter", 66 groups: []*group.NewGroup{ 67 {Name: "foo"}, 68 }, 69 index: []*event.Event{ 70 { 71 ID: "1", 72 Name: "bar", 73 Published: true, 74 Organizers: []string{"id:0"}, 75 }, 76 }, 77 options: eventsearch.Options{ 78 Filters: []eventsearch.Filter{ 79 eventsearch.GroupIDsFilter{GroupIDs: []string{"0"}}, 80 }, 81 }, 82 wantEvents: []*eventsearch.EventDocument{ 83 { 84 ID: "1", 85 Listed: true, 86 Published: true, 87 Name: "bar", 88 Organizers: []eventsearch.Organizer{{Group: &group.Group{ID: "0", Name: "foo", OwnedBy: []string{"my_id"}}}}, 89 MultiDay: -1, 90 }, 91 }, 92 }, 93 { 94 name: "EmptyIDsFilter", 95 index: []*event.Event{ 96 { 97 ID: "1", 98 Name: "bar", 99 }, 100 }, 101 options: eventsearch.Options{ 102 Filters: []eventsearch.Filter{ 103 eventsearch.IDsFilter{IDs: []string{}}, 104 }, 105 }, 106 wantEvents: []*eventsearch.EventDocument{ 107 { 108 ID: "1", 109 Name: "bar", 110 Organizers: []eventsearch.Organizer{}, 111 MultiDay: -1, 112 }, 113 }, 114 }, 115 { 116 name: "MultiDay -2 to -1", 117 index: []*event.Event{ 118 { 119 ID: "1", 120 Name: "foo", 121 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 122 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 123 }, 124 }, 125 options: eventsearch.Options{ 126 Sort: "day", 127 Filters: []eventsearch.Filter{ 128 eventsearch.MultidayRangeFilter{MultidayMin: intptr(-2), MultidayMax: intptr(-1)}, 129 }, 130 }, 131 wantEvents: []*eventsearch.EventDocument{ 132 { 133 ID: "1", 134 Name: "foo", 135 Organizers: []eventsearch.Organizer{}, 136 MultiDay: -2, 137 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 138 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 139 }, 140 }, 141 }, 142 { 143 name: "MultiDay < 0", 144 index: []*event.Event{ 145 { 146 ID: "1", 147 Name: "foo", 148 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 149 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 150 }, 151 { 152 ID: "2", 153 Name: "bar", 154 Published: true, 155 }, 156 }, 157 options: eventsearch.Options{ 158 Sort: "day", 159 Filters: []eventsearch.Filter{ 160 eventsearch.MultidayRangeFilter{MultidayMax: intptr(0)}, 161 }, 162 }, 163 wantEvents: []*eventsearch.EventDocument{ 164 { 165 ID: "1", 166 Name: "foo", 167 Organizers: []eventsearch.Organizer{}, 168 MultiDay: -2, 169 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 170 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 171 }, 172 { 173 ID: "2", 174 Listed: true, 175 Published: true, 176 Name: "bar", 177 Organizers: []eventsearch.Organizer{}, 178 MultiDay: -1, 179 }, 180 }, 181 }, 182 { 183 name: "MultiDay > 1", 184 index: []*event.Event{ 185 { 186 ID: "1", 187 Name: "foo", 188 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 189 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 190 }, 191 }, 192 options: eventsearch.Options{ 193 Sort: "day", 194 Filters: []eventsearch.Filter{ 195 eventsearch.MultidayRangeFilter{MultidayMin: intptr(1)}, 196 }, 197 }, 198 wantEvents: []*eventsearch.EventDocument{ 199 { 200 ID: "1", 201 Name: "foo", 202 Organizers: []eventsearch.Organizer{}, 203 MultiDay: 1, 204 Date: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 205 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 206 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 207 }, 208 { 209 ID: "1", 210 Name: "foo", 211 Organizers: []eventsearch.Organizer{}, 212 MultiDay: 2, 213 Date: time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC), 214 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 215 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 216 }, 217 { 218 ID: "1", 219 Name: "foo", 220 Organizers: []eventsearch.Organizer{}, 221 MultiDay: 3, 222 Date: time.Date(2023, 3, 27, 12, 0, 0, 0, time.UTC), 223 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 224 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 225 }, 226 { 227 ID: "1", 228 Name: "foo", 229 Organizers: []eventsearch.Organizer{}, 230 MultiDay: 4, 231 Date: time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC), 232 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 233 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 234 }, 235 }, 236 }, 237 } 238 testEventSearch(t, tests) 239 } 240 241 func TestMultiDayEvents(t *testing.T) { 242 tests := []eventsearchTest{ 243 { 244 name: "MultiDay", 245 index: []*event.Event{ 246 { 247 ID: "1", 248 Name: "foo", 249 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 250 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 251 }, 252 }, 253 options: eventsearch.Options{ 254 Sort: "day", 255 }, 256 wantEvents: []*eventsearch.EventDocument{ 257 { 258 ID: "1", 259 Name: "foo", 260 Organizers: []eventsearch.Organizer{}, 261 MultiDay: 1, 262 Date: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 263 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 264 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 265 }, 266 { 267 ID: "1", 268 Name: "foo", 269 Organizers: []eventsearch.Organizer{}, 270 MultiDay: 2, 271 Date: time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC), 272 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 273 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 274 }, 275 { 276 ID: "1", 277 Name: "foo", 278 Organizers: []eventsearch.Organizer{}, 279 MultiDay: 3, 280 Date: time.Date(2023, 3, 27, 12, 0, 0, 0, time.UTC), 281 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 282 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 283 }, 284 { 285 ID: "1", 286 Name: "foo", 287 Organizers: []eventsearch.Organizer{}, 288 MultiDay: 4, 289 Date: time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC), 290 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 291 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 292 }, 293 { 294 ID: "1", 295 Name: "foo", 296 Organizers: []eventsearch.Organizer{}, 297 MultiDay: -2, 298 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 299 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 300 }, 301 }, 302 }, 303 { 304 name: "MultiDayWithSubEvent", 305 events: []*event.NewEvent{ 306 { 307 Published: true, 308 Parent: "id:abcd", 309 ParentListed: false, 310 Name: "bar", 311 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 312 }, 313 }, 314 index: []*event.Event{ 315 { 316 ID: "abcd", 317 Name: "foo", 318 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 319 End: tptr(time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC)), 320 }, 321 }, 322 options: eventsearch.Options{ 323 Sort: "day", 324 }, 325 wantEvents: []*eventsearch.EventDocument{ 326 { 327 ID: "abcd", 328 Name: "foo", 329 Organizers: []eventsearch.Organizer{}, 330 MultiDay: 1, 331 Date: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 332 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 333 End: tptr(time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC)), 334 Children: []*eventsearch.EventDocument{ 335 { 336 ID: "0", 337 Published: true, 338 Name: "bar", 339 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 340 Date: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 341 MultiDay: -1, 342 Organizers: []eventsearch.Organizer{}, 343 }, 344 }, 345 }, 346 { 347 ID: "abcd", 348 Name: "foo", 349 Organizers: []eventsearch.Organizer{}, 350 MultiDay: 2, 351 Date: time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC), 352 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 353 End: tptr(time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC)), 354 }, 355 { 356 ID: "abcd", 357 Name: "foo", 358 Organizers: []eventsearch.Organizer{}, 359 MultiDay: -2, 360 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 361 End: tptr(time.Date(2023, 3, 26, 12, 0, 0, 0, time.UTC)), 362 Children: []*eventsearch.EventDocument{ 363 { 364 ID: "0", 365 Published: true, 366 Name: "bar", 367 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 368 Date: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 369 MultiDay: -1, 370 Organizers: []eventsearch.Organizer{}, 371 }, 372 }, 373 }, 374 }, 375 }, 376 } 377 testEventSearch(t, tests) 378 } 379 380 func TestReindex(t *testing.T) { 381 tests := []eventsearchTest{ 382 { 383 name: "RemoveAndAdd", 384 events: []*event.NewEvent{ 385 { 386 Name: "bar", 387 }, 388 }, 389 index: []*event.Event{ 390 { 391 ID: "10", 392 Name: "foo", 393 }, 394 }, 395 reindex: true, 396 options: eventsearch.Options{}, 397 wantEvents: []*eventsearch.EventDocument{ 398 { 399 ID: "0", 400 Name: "bar", 401 Organizers: []eventsearch.Organizer{}, 402 MultiDay: -1, 403 }, 404 }, 405 }, 406 { 407 name: "RemoveMultiDay", 408 events: []*event.NewEvent{ 409 { 410 Name: "bar", 411 }, 412 }, 413 index: []*event.Event{ 414 { 415 ID: "1", 416 Name: "foo", 417 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 418 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 419 }, 420 }, 421 reindex: true, 422 options: eventsearch.Options{}, 423 wantEvents: []*eventsearch.EventDocument{ 424 { 425 ID: "0", 426 Name: "bar", 427 Organizers: []eventsearch.Organizer{}, 428 MultiDay: -1, 429 }, 430 }, 431 }, 432 } 433 testEventSearch(t, tests) 434 } 435 436 func TestOperations(t *testing.T) { 437 tests := []eventsearchTest{ 438 { 439 name: "Update", 440 events: []*event.NewEvent{ 441 { 442 Name: "bar", 443 }, 444 }, 445 operations: []oqueue.Operation{ 446 event.UpdateOperation{ 447 Event: &event.Event{ 448 ID: "0", 449 Name: "bar updated", 450 }, 451 }, 452 }, 453 options: eventsearch.Options{}, 454 wantEvents: []*eventsearch.EventDocument{ 455 { 456 ID: "0", 457 Name: "bar updated", 458 Organizers: []eventsearch.Organizer{}, 459 MultiDay: -1, 460 }, 461 }, 462 }, 463 { 464 name: "Delete", 465 events: []*event.NewEvent{ 466 { 467 Name: "bar", 468 }, 469 }, 470 operations: []oqueue.Operation{ 471 event.DeleteOperation{ 472 ID: "0", 473 }, 474 }, 475 options: eventsearch.Options{}, 476 wantEvents: []*eventsearch.EventDocument{}, 477 }, 478 { 479 name: "DeleteMultiDay", 480 index: []*event.Event{ 481 { 482 ID: "0", 483 Name: "bar", 484 Start: time.Date(2023, 3, 25, 12, 0, 0, 0, time.UTC), 485 End: tptr(time.Date(2023, 3, 28, 12, 0, 0, 0, time.UTC)), 486 }, 487 }, 488 operations: []oqueue.Operation{ 489 event.DeleteOperation{ 490 ID: "0", 491 }, 492 }, 493 options: eventsearch.Options{}, 494 wantEvents: []*eventsearch.EventDocument{}, 495 }, 496 } 497 testEventSearch(t, tests) 498 } 499 500 type eventsearchTest struct { 501 name string 502 tz *time.Location 503 504 events []*event.NewEvent 505 groups []*group.NewGroup 506 places []*place.NewPlace 507 508 // index, reindex and operations are applied in this order 509 index []*event.Event 510 reindex bool 511 operations []oqueue.Operation 512 513 options eventsearch.Options 514 515 wantEvents []*eventsearch.EventDocument 516 wantBuckets map[string]search.Bucket 517 } 518 519 func testEventSearch(t *testing.T, tests []eventsearchTest) { 520 for _, tc := range tests { 521 t.Run(tc.name, func(tt *testing.T) { 522 index, cleanupIndex := test.CreateBleveTestIndex(tc.name) 523 t.Cleanup(cleanupIndex) 524 525 tz := time.UTC 526 ctx := auth.ContextWithID(context.Background(), "my_id") 527 528 searchService, err := search.NewService(index, time.Second, 1, 1, tz) 529 if err != nil { 530 t.Fatalf("search: %s", err) 531 } 532 533 eventService := event.NewService(event.NewMemoryStore(), nil) 534 groupService := group.NewService(group.NewMemoryStore()) 535 placeService := place.NewService(place.NewMemoryStore()) 536 537 queue := oqueue.NewQueue() 538 539 for _, e := range tc.events { 540 if _, err := eventService.Create(ctx, e); err != nil { 541 tt.Fatalf("create events: %s", err) 542 } 543 } 544 for _, g := range tc.groups { 545 if _, err := groupService.Create(ctx, g); err != nil { 546 tt.Fatalf("create group: %s", err) 547 } 548 } 549 for _, p := range tc.places { 550 if _, err := placeService.Create(ctx, p); err != nil { 551 tt.Fatalf("create place: %s", err) 552 } 553 } 554 555 eventsearchService := eventsearch.NewService(searchService, eventService, groupService, placeService, nil) 556 queue.AddSubscriber(eventsearchService.ConsumeOperation, 1) 557 558 for _, e := range tc.index { 559 err = eventsearchService.Index(e) 560 if err != nil { 561 tt.Fatalf("Index: %s", err) 562 } 563 } 564 565 if tc.reindex { 566 eventsearchService.Reindex(context.Background()) 567 } 568 569 for _, op := range tc.operations { 570 err = queue.AddOperation(op) 571 if err != nil { 572 tt.Errorf("AddOperation: %s", err) 573 } 574 } 575 // Wait for all operations to be consumed 576 queue.Close() 577 578 result, err := eventsearchService.Search(context.Background(), tc.options) 579 if err != nil { 580 tt.Fatalf("Search: %s", err) 581 } 582 583 if tc.wantEvents != nil { 584 if diff := cmp.Diff(tc.wantEvents, result.Events, cmpopts.IgnoreUnexported(eventsearch.EventDocument{})); diff != "" { 585 tt.Errorf("result mismatch (-want +got):\n%s", diff) 586 } 587 } 588 589 if tc.wantBuckets != nil { 590 if diff := cmp.Diff(tc.wantBuckets, result.Buckets, cmpopts.IgnoreUnexported()); diff != "" { 591 tt.Errorf("buckets mismatch (-want +got):\n%s", diff) 592 } 593 } 594 }) 595 } 596 } 597 598 func intptr(i int) *int { 599 return &i 600 }