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  }