eintopf.info@v0.13.16/service/search/aggregation_test.go (about)

     1  // Copyright (C) 2022 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package search
    17  
    18  import (
    19  	"reflect"
    20  	"testing"
    21  	"time"
    22  )
    23  
    24  type aggregatorTestcase struct {
    25  	name       string
    26  	values     []interface{}
    27  	wantError  string
    28  	wantBucket Bucket
    29  }
    30  
    31  func TestTermsAggregator(t *testing.T) {
    32  	testcases := []aggregatorTestcase{
    33  		{
    34  			name:      "InvalidType",
    35  			values:    []interface{}{2},
    36  			wantError: "invalid type: int",
    37  		}, {
    38  			name:       "TypeString",
    39  			values:     []interface{}{"a"},
    40  			wantBucket: TermsBucket{Term{Term: "a", Count: 1}},
    41  		}, {
    42  			name:       "Type[]string",
    43  			values:     []interface{}{[]string{"a"}},
    44  			wantBucket: TermsBucket{Term{Term: "a", Count: 1}},
    45  		}, {
    46  			name:       "Type[]interface{}",
    47  			values:     []interface{}{[]interface{}{"a"}},
    48  			wantBucket: TermsBucket{Term{Term: "a", Count: 1}},
    49  		}, {
    50  			name:   "IncreasesCount",
    51  			values: []interface{}{"a", "a", "b"},
    52  			wantBucket: TermsBucket{
    53  				Term{Term: "a", Count: 2},
    54  				Term{Term: "b", Count: 1},
    55  			},
    56  		},
    57  	}
    58  	testAggregator(t, testcases, func() aggregator {
    59  		return &termsAggregator{make(map[string]int)}
    60  	})
    61  }
    62  
    63  func TestDateAggregator(t *testing.T) {
    64  	time1 := time.Date(2019, 1, 12, 20, 0, 0, 0, time.UTC)
    65  	time2 := time.Date(2019, 2, 12, 20, 0, 0, 0, time.UTC)
    66  	time3 := time.Date(2019, 7, 12, 20, 0, 0, 0, time.UTC)
    67  	testcases := []aggregatorTestcase{
    68  		{
    69  			name:      "InvalidType",
    70  			values:    []interface{}{2},
    71  			wantError: "invalid type: int",
    72  		}, {
    73  			name:      "InvalidDate",
    74  			values:    []interface{}{"a"},
    75  			wantError: `invalid date format: parsing time "a" as "2006-01-02T15:04:05Z07:00": cannot parse "a" as "2006"`,
    76  		}, {
    77  			name: "2Dates",
    78  			values: []interface{}{
    79  				time1.Format(DateLayout),
    80  				time2.Format(DateLayout),
    81  			},
    82  			wantBucket: DateRangeBucket{
    83  				Min: time1,
    84  				Max: time2,
    85  			},
    86  		}, {
    87  			name: "3Dates",
    88  			values: []interface{}{
    89  				time2.Format(DateLayout),
    90  				time3.Format(DateLayout),
    91  				time1.Format(DateLayout),
    92  			},
    93  			wantBucket: DateRangeBucket{
    94  				Min: time1,
    95  				Max: time3,
    96  			},
    97  		},
    98  	}
    99  
   100  	testAggregator(t, testcases, func() aggregator {
   101  		return &dateRangeAggregator{
   102  			min: time.Date(9999, 0, 0, 0, 0, 0, 0, time.UTC),
   103  			max: time.Date(0, 0, 0, 0, 0, 0, 0, time.UTC),
   104  		}
   105  	})
   106  }
   107  
   108  func testAggregator(t *testing.T, testcases []aggregatorTestcase, newAggregator func() aggregator) {
   109  	t.Helper()
   110  
   111  	for _, tc := range testcases {
   112  		t.Run(tc.name, func(tt *testing.T) {
   113  			aggregator := newAggregator()
   114  			for _, v := range tc.values {
   115  				err := aggregator.aggregate(v)
   116  				if tc.wantError != "" {
   117  					if err.Error() != tc.wantError {
   118  						tt.Fatalf("aggregator.aggregate(%v): error doesn't match:\nwant: %s\ngot:  %s", v, tc.wantError, err.Error())
   119  					}
   120  					return
   121  				}
   122  				if err != nil {
   123  					tt.Fatalf("aggregator.aggregate(%v): failed unexpectedly: %s", v, err.Error())
   124  				}
   125  			}
   126  
   127  			bucket := aggregator.bucket()
   128  			if !reflect.DeepEqual(bucket, tc.wantBucket) {
   129  				tt.Fatalf("bucket doesn't match:\nwant: %v\ngot:  %v", tc.wantBucket, bucket)
   130  			}
   131  		})
   132  	}
   133  }
   134  
   135  func TestAggregationCacheKey(t *testing.T) {
   136  	for _, tc := range []struct {
   137  		a1        Aggregation
   138  		a2        Aggregation
   139  		wantEqual bool
   140  	}{
   141  		{
   142  			a1: Aggregation{
   143  				Type:  TermsAggregation,
   144  				Field: "foo",
   145  				Filters: []Filter{
   146  					&TermsFilter{Field: "bar", Terms: []string{"a", "b"}},
   147  					&DateRangeFilter{Field: "baz", Min: time.Unix(1000000, 0), Max: time.Unix(1000, 0)},
   148  				},
   149  			},
   150  			a2: Aggregation{
   151  				Type:  TermsAggregation,
   152  				Field: "foo",
   153  				Filters: []Filter{
   154  					&TermsFilter{Field: "bar", Terms: []string{"a", "b"}},
   155  					&DateRangeFilter{Field: "baz", Min: time.Unix(1000000, 0), Max: time.Unix(1000, 0)},
   156  				},
   157  			},
   158  			wantEqual: true,
   159  		}, {
   160  			a1:        Aggregation{Type: TermsAggregation},
   161  			a2:        Aggregation{Type: DateRangeAggregation},
   162  			wantEqual: false,
   163  		}, {
   164  			a1:        Aggregation{Field: "foo"},
   165  			a2:        Aggregation{Field: "bar"},
   166  			wantEqual: false,
   167  		}, {
   168  			a1:        Aggregation{Filters: []Filter{&TermsFilter{Field: "foo"}}},
   169  			a2:        Aggregation{Filters: []Filter{&TermsFilter{Field: "bar"}}},
   170  			wantEqual: false,
   171  		},
   172  	} {
   173  		c1 := tc.a1.CacheKey()
   174  		c2 := tc.a2.CacheKey()
   175  
   176  		if tc.wantEqual && c1 != c2 {
   177  			t.Errorf("cache keys are unequal: %v == %v: %s != %s", tc.a1, tc.a2, c1, c2)
   178  		}
   179  		if !tc.wantEqual && c1 == c2 {
   180  			t.Errorf("cache keys are equal: %v != %v: %s == %s", tc.a1, tc.a2, c1, c2)
   181  		}
   182  	}
   183  }