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  }