package handlers_test import ( "encoding/json" "fmt" "net/http" "testing" "github.com/fiskerinc/cloud-services/pkg/redis" "otaupdate/handlers" "otaupdate/services" m "github.com/fiskerinc/cloud-services/pkg/common" orm "github.com/fiskerinc/cloud-services/pkg/db/queries" mo "github.com/fiskerinc/cloud-services/pkg/db/queries/mocks" r "github.com/fiskerinc/cloud-services/pkg/redis" rm "github.com/fiskerinc/cloud-services/pkg/redis/tester" th "github.com/fiskerinc/cloud-services/pkg/testhelper" "github.com/fiskerinc/cloud-services/pkg/utils/elptr" ) type MockCarsCounted struct { mo.MockCars searchCounter int countCounter int } func (m *MockCarsCounted) Search(s *m.CarSearch, q *orm.PageQueryOptions) ([]m.Car, error) { m.searchCounter++ return m.MockCars.Search(s, q) } func (m *MockCarsCounted) SearchCount(s *m.CarSearch) (int, error) { m.countCounter++ return m.MockCars.SearchCount(s) } func TestGetVehiclesCached(t *testing.T) { r.MockRedisConnection() mockRedis := rm.MockRedis{ GetSetResults: "[]", GetCommandResult: map[string]map[string]interface{}{ "SMEMBERS": {redis.CarSessionsKey(): []interface{}{}}, }, } services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis)) mock := MockCarsCounted{} services.GetDB().SetCars(&mock) defaultOrder := "vin" tests := []mo.DBHttpTest{ { Name: "Default Limit 100", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: longListExpectedResultGen(longListData, 0, 0), DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: longListData[:100], }, }, { Name: "Cached Limit 100", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: longListExpectedResultGen(longListData, 0, 0), DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: longListData[:100], }, }, } mo.RunDBTests(t, tests, handlers.HandleVehiclesGet, &mock) if mock.searchCounter != 2 && mock.countCounter != 2 { t.Errorf("Expected search and count to be called only once, got %d and %d", mock.searchCounter, mock.countCounter) } } func TestGetVehicles(t *testing.T) { r.MockRedisConnection() mockRedis := rm.MockRedis{ GetSetResults: "[]", GetCommandResult: map[string]map[string]interface{}{ "SMEMBERS": {redis.CarSessionsKey(): []interface{}{}}, }, } services.SetRedisClientPool(rm.NewMockClientPool(&mockRedis)) handlers.SetVehiclesCache(&rm.MockVehiclesCache{}) mock := mo.MockCars{} services.GetDB().SetCars(&mock) expectedResp := `{"data":[{"vin":"3C4PDCBG0ET127145","year":2021,"model":"Ocean"}],"total":1}` expectedRespNoTotal := `{"data":[{"vin":"3C4PDCBG0ET127145","year":2021,"model":"Ocean"}]}` defaultOrder := "vin" listData := []m.Car{ { VIN: "3C4PDCBG0ET127145", Model: "Ocean", Year: 2021, }, } tests := []mo.DBHttpTest{ { Name: "No parameters", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedResp, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: listData, }, }, { Name: "Default Limit 100", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: longListExpectedResultGen(longListData, 0, 0), DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: longListData[:100], }, }, { Name: "Wrong limit, -100", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?limit=-100", nil), ExpectedStatus: http.StatusBadRequest, ExpectedResponse: `{"message":"Limit less than 0","error":"Bad Request"}`, }, { Name: "Wrong limit, 1000", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?limit=1000", nil), ExpectedStatus: http.StatusBadRequest, ExpectedResponse: `{"message":"Limit greater than 100","error":"Bad Request"}`, }, { Name: "Invalid VIN", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?vin=FISKER123", nil), ExpectedStatus: http.StatusBadRequest, ExpectedResponse: `{"message":"VIN 'FISKER123' invalid","error":"Bad Request"}`, }, { Name: "Id parameter", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?vin=3C4PDCBG0ET127145", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedRespNoTotal, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{ Car: m.Car{ VIN: "3C4PDCBG0ET127145", }, }, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: listData, }, }, { Name: "Name, version, description parameters", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?model=Ocean&year=2021", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedResp, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{ Car: m.Car{ Model: "Ocean", Year: 2021, }, }, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: listData, }, }, { Name: "Paging parameters", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?offset=10&limit=5", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedRespNoTotal, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 5, Offset: 10, }, MockListResponse: listData, }, }, { // TODO: since the logic is on the db layer, we can't check the online/offline test here, // it's whether we have to have a full mock database, or we have to expect the query to be // equal to some string. Name: "Online", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?online=true", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedResp, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{ Online: &m.CarOnlineFilter{ Online: elptr.ElPtr(true), }, }, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: listData, }, }, { Name: "Offline", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?online=false", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: expectedResp, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{ Online: &m.CarOnlineFilter{ Online: elptr.ElPtr(false), }, }, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: listData, }, }, { Name: "Multiple VINs", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars?vins=SCAZD42A3HCX11779,SCAZD42A3HCX11850", nil), ExpectedStatus: http.StatusOK, ExpectedResponse: `{"data":[{"vin":"SCAZD42A3HCX11779","year":2021,"model":"Ocean"},{"vin":"SCAZD42A3HCX11850","year":2021,"model":"Ocean"}],"total":2}`, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{ VINs: "SCAZD42A3HCX11779,SCAZD42A3HCX11850", }, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockListResponse: longListData[:2], }, }, { Name: "Error", Request: th.MakeTestRequest(http.MethodGet, "http://example.com/cars", nil), ExpectedStatus: http.StatusServiceUnavailable, ExpectedResponse: `{"message":"something went wrong","error":"Service Unavailable"}`, DBTestCase: mo.DBTestCase{ ExpectedFilter: m.CarSearch{}, ExpectedPage: &orm.PageQueryOptions{ Order: defaultOrder, Limit: 100, Offset: 0, }, MockError: fmt.Errorf("something went wrong"), }, }, } mo.RunDBTests(t, tests, handlers.HandleVehiclesGet, &mock) } // 105 entries var longListData []m.Car = []m.Car{ { VIN: "SCAZD42A3HCX11779", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11850", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16041", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12984", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11576", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15688", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17778", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10518", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17196", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17156", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13043", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13784", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19371", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19905", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX18835", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11056", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16812", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13636", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12874", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17360", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13373", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11933", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11286", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16703", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12358", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15157", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17005", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10283", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11247", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17714", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16219", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16036", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19125", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15079", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX18843", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14722", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16906", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11601", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14453", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11028", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17184", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12328", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13860", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15042", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17519", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10309", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10304", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17052", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19086", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15606", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19217", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16092", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17281", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13886", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19772", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15135", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17306", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16200", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12910", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10944", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10117", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16097", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13145", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19594", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10200", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14353", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11049", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10682", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14547", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14368", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12021", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17152", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19291", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11513", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13697", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX18883", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14454", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX16767", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14896", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13477", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14916", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13866", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17261", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15298", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17098", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19128", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX15130", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10156", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19665", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX10477", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX18530", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX18158", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14232", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13238", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17683", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11460", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX11272", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX14306", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX17241", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13865", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX12546", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19404", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19747", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX13450", Model: "Ocean", Year: 2021, }, { VIN: "SCAZD42A3HCX19459", Model: "Ocean", Year: 2021, }, } type fakeResponse struct { Data []m.Car `json:"data"` Total int `json:"total"` } func longListExpectedResultGen(carList []m.Car, offset, limit int) string { max := len(carList) if limit <= 0 { limit = 100 } if offset+limit < max { max = offset + limit } carSubset := carList[offset:max] fakeRes := fakeResponse{Data: carSubset, Total: len(carSubset)} fmt.Println() b, _ := json.Marshal(fakeRes) return string(b) }