github.com/web-platform-tests/wpt.fyi@v0.0.0-20240530210107-70cf978996f1/api/query/query_test.go (about) 1 //go:build small 2 // +build small 3 4 // Copyright 2018 The WPT Dashboard Project. All rights reserved. 5 // Use of this source code is governed by a BSD-style license that can be 6 // found in the LICENSE file. 7 8 package query 9 10 import ( 11 "bytes" 12 "errors" 13 "net/http/httptest" 14 "testing" 15 "time" 16 17 "github.com/golang/mock/gomock" 18 "github.com/stretchr/testify/assert" 19 "github.com/web-platform-tests/wpt.fyi/shared" 20 "github.com/web-platform-tests/wpt.fyi/shared/sharedtest" 21 ) 22 23 func TestGetRedisKey(t *testing.T) { 24 assert.Equal(t, "RESULTS_SUMMARY_v2-1", getSummaryFileRedisKey(shared.TestRun{ 25 ID: 1, 26 })) 27 } 28 29 func TestLoadSummaries_success(t *testing.T) { 30 mockCtrl := gomock.NewController(t) 31 defer mockCtrl.Finish() 32 33 urls := []string{ 34 "https://example.com/1-summary_v2.json.gz", 35 "https://example.com/2-summary_v2.json.gz", 36 } 37 testRuns := []shared.TestRun{ 38 { 39 ID: 1, 40 ResultsURL: urls[0], 41 }, 42 { 43 ID: 2, 44 ResultsURL: urls[1], 45 }, 46 } 47 keys := []string{ 48 getSummaryFileRedisKey(testRuns[0]), 49 getSummaryFileRedisKey(testRuns[1]), 50 } 51 52 cachedStore := sharedtest.NewMockCachedStore(mockCtrl) 53 sh := unstructuredSearchHandler{queryHandler{dataSource: cachedStore}} 54 summaryBytes := [][]byte{ 55 []byte(`{"/a/b/c":{"s":"O","c":[1,2]}}`), 56 []byte(`{"/x/y/z":{"s":"E","c":[3,4]}}`), 57 } 58 summaries := []summary{ 59 map[string]SummaryResult{"/a/b/c": {Status: "O", Counts: []int{1, 2}}}, 60 map[string]SummaryResult{"/x/y/z": {Status: "E", Counts: []int{3, 4}}}, 61 } 62 63 bindCopySlice := func(i int) func(_, _, _ interface{}) { 64 return func(cid, sid, iv interface{}) { 65 ptr := iv.(*[]byte) 66 *ptr = summaryBytes[i] 67 } 68 } 69 for i, key := range keys { 70 cachedStore.EXPECT().Get(key, urls[i], gomock.Any()).Do(bindCopySlice(i)).Return(nil) 71 } 72 73 ss, err := sh.loadSummaries(testRuns) 74 assert.Nil(t, err) 75 assert.Equal(t, summaries[0], ss[0]) 76 assert.Equal(t, summaries[1], ss[1]) 77 } 78 79 func TestLoadSummaries_fail(t *testing.T) { 80 mockCtrl := gomock.NewController(t) 81 defer mockCtrl.Finish() 82 83 urls := []string{ 84 "https://example.com/1-summary_v2.json.gz", 85 "https://example.com/2-summary_v2.json.gz", 86 } 87 testRuns := []shared.TestRun{ 88 { 89 ID: 1, 90 ResultsURL: urls[0], 91 }, 92 { 93 ID: 2, 94 ResultsURL: urls[1], 95 }, 96 } 97 keys := []string{ 98 getSummaryFileRedisKey(testRuns[0]), 99 getSummaryFileRedisKey(testRuns[1]), 100 } 101 102 cachedStore := sharedtest.NewMockCachedStore(mockCtrl) 103 sh := unstructuredSearchHandler{queryHandler{dataSource: cachedStore}} 104 summaryBytes := [][]byte{ 105 []byte(`{"/a/b/c":{"s":"O","c":[1,2]}}`), 106 } 107 108 storeMiss := errors.New("No such summary file") 109 cachedStore.EXPECT().Get(keys[0], urls[0], gomock.Any()).Do(func(cid, sid, iv interface{}) { 110 ptr := iv.(*[]byte) 111 *ptr = summaryBytes[0] 112 }).Return(nil) 113 cachedStore.EXPECT().Get(keys[1], urls[1], gomock.Any()).Return(storeMiss) 114 115 _, err := sh.loadSummaries(testRuns) 116 assert.Contains(t, err.Error(), storeMiss.Error()) 117 } 118 119 func TestSummaryIsValid_v1(t *testing.T) { 120 qh := queryHandler{} 121 // Summaries without the "_v2" suffix should not be used. 122 url := "https://example.com/invalid-summary.json.gz" 123 assert.False(t, qh.summaryIsValid(url)) 124 } 125 126 func TestSummaryIsValid_v2(t *testing.T) { 127 qh := queryHandler{} 128 url := "https://example.com/valid-summary_v2.json.gz" 129 assert.True(t, qh.summaryIsValid(url)) 130 } 131 132 func TestGetRunsAndFilters_default(t *testing.T) { 133 mockCtrl := gomock.NewController(t) 134 defer mockCtrl.Finish() 135 136 mockStore := sharedtest.NewMockDatastore(mockCtrl) 137 mockQuery := sharedtest.NewMockTestRunQuery(mockCtrl) 138 mockStore.EXPECT().TestRunQuery().Return(mockQuery) 139 sh := unstructuredSearchHandler{queryHandler{ 140 store: mockStore, 141 }} 142 143 runIDs := []int64{1, 2} 144 urls := []string{ 145 "https://example.com/1-summary_v2.json.gz", 146 "https://example.com/2-summary_v2.json.gz", 147 } 148 chrome, _ := shared.ParseProductSpec("chrome") 149 edge, _ := shared.ParseProductSpec("edge") 150 testRuns := shared.TestRunsByProduct{ 151 shared.ProductTestRuns{ 152 Product: chrome, 153 TestRuns: shared.TestRuns{ 154 shared.TestRun{ 155 ID: runIDs[0], 156 ResultsURL: urls[0], 157 TimeStart: time.Now(), 158 }, 159 }, 160 }, 161 shared.ProductTestRuns{ 162 Product: edge, 163 TestRuns: shared.TestRuns{ 164 shared.TestRun{ 165 ID: runIDs[1], 166 ResultsURL: urls[1], 167 TimeStart: time.Now().AddDate(0, 0, -1), 168 }, 169 }, 170 }, 171 } 172 filters := shared.QueryFilter{} 173 174 mockQuery.EXPECT().LoadTestRuns(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(testRuns, nil) 175 176 trs, fs, err := sh.getRunsAndFilters(filters) 177 assert.Nil(t, err) 178 assert.Equal(t, testRuns.AllRuns(), trs) 179 assert.Equal(t, shared.QueryFilter{ 180 RunIDs: runIDs, 181 }, fs) 182 } 183 184 func TestGetRunsAndFilters_specificRunIDs(t *testing.T) { 185 mockCtrl := gomock.NewController(t) 186 defer mockCtrl.Finish() 187 188 mockStore := sharedtest.NewMockDatastore(mockCtrl) 189 sh := unstructuredSearchHandler{queryHandler{ 190 store: mockStore, 191 }} 192 193 runIDs := []int64{1, 2} 194 urls := []string{ 195 "https://example.com/1-summary_v2.json.gz", 196 "https://example.com/2-summary_v2.json.gz", 197 } 198 chrome, _ := shared.ParseProductSpec("chrome") 199 edge, _ := shared.ParseProductSpec("edge") 200 testRuns := shared.TestRunsByProduct{ 201 shared.ProductTestRuns{ 202 Product: chrome, 203 TestRuns: shared.TestRuns{ 204 shared.TestRun{ 205 ID: runIDs[0], 206 ResultsURL: urls[0], 207 TimeStart: time.Now(), 208 }, 209 }, 210 }, 211 shared.ProductTestRuns{ 212 Product: edge, 213 TestRuns: shared.TestRuns{ 214 shared.TestRun{ 215 ID: runIDs[1], 216 ResultsURL: urls[1], 217 TimeStart: time.Now().AddDate(0, 0, -1), 218 }, 219 }, 220 }, 221 } 222 filters := shared.QueryFilter{ 223 RunIDs: runIDs, 224 } 225 226 for _, id := range runIDs { 227 mockStore.EXPECT().NewIDKey("TestRun", id).Return(sharedtest.MockKey{ID: id}) 228 } 229 mockStore.EXPECT().GetMulti(sharedtest.SameKeys(runIDs), gomock.Any()).DoAndReturn(sharedtest.MultiRuns(testRuns.AllRuns())) 230 231 trs, fs, err := sh.getRunsAndFilters(filters) 232 assert.Nil(t, err) 233 assert.Equal(t, testRuns.AllRuns(), trs) 234 assert.Equal(t, filters, fs) 235 } 236 237 func TestIsRequestCacheable_getNotCacheable(t *testing.T) { 238 assert.False(t, isRequestCacheable(httptest.NewRequest("GET", "https://wpt.fyi/api/search", nil))) 239 } 240 241 func TestIsRequestCacheable_getCacheable(t *testing.T) { 242 assert.True(t, isRequestCacheable(httptest.NewRequest("GET", "https://wpt.fyi/api/search?run_ids=1,2,-3", nil))) 243 } 244 245 func TestIsRequestCacheable_postNotCacheable(t *testing.T) { 246 assert.False(t, isRequestCacheable(httptest.NewRequest("POST", "https://wpt.fyi/api/search", bytes.NewBuffer([]byte("{}"))))) 247 } 248 249 func TestIsRequestCacheable_postCacheable(t *testing.T) { 250 assert.True(t, isRequestCacheable(httptest.NewRequest("POST", "https://wpt.fyi/api/search", bytes.NewBuffer([]byte(`{"run_ids":[1,2,-3]}`))))) 251 }