From 3a03046f16aee299ffe5edfe78db796feac62c2c Mon Sep 17 00:00:00 2001 From: Bernd Warmuth <72415058+warber@users.noreply.github.com> Date: Thu, 21 Sep 2023 13:17:48 +0200 Subject: [PATCH] chore: enhance `testutils.TestServer` (#36) --- api/clients/automation/automation_test.go | 574 ++++++++++------ api/clients/buckets/bucket_test.go | 793 +++++++++++++++------- internal/testutils/testserver.go | 120 +++- 3 files changed, 971 insertions(+), 516 deletions(-) diff --git a/api/clients/automation/automation_test.go b/api/clients/automation/automation_test.go index cba08b5..0cbe1c6 100644 --- a/api/clients/automation/automation_test.go +++ b/api/clients/automation/automation_test.go @@ -36,8 +36,8 @@ func TestAutomationClient_Get(t *testing.T) { const payload = `{ "id" : "91cc8988-2223-404a-a3f5-5f1a839ecd45", "data" : "some-data1" }` t.Run("Get - no ID given", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -49,13 +49,18 @@ func TestAutomationClient_Get(t *testing.T) { t.Run("GET - OK", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payload, + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -68,8 +73,9 @@ func TestAutomationClient_Get(t *testing.T) { }) t.Run("GET - Unable to make HTTP call", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.FaultyClient())) @@ -80,12 +86,17 @@ func TestAutomationClient_Get(t *testing.T) { }) t.Run("GET - API Call returned with != 2xx", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodGet: { - ResponseCode: http.StatusBadRequest, + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusBadRequest, + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -98,15 +109,20 @@ func TestAutomationClient_Get(t *testing.T) { func TestAutomationClient_Create(t *testing.T) { const payload = `{ "id" : "91cc8988-2223-404a-a3f5-5f1a839ecd45", "data" : "some-data1" }` - t.Run("Create - OK", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPost: { - ResponseCode: http.StatusCreated, - ResponseBody: payload, + t.Run("Create - OK", func(t *testing.T) { + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusCreated, + ResponseBody: payload, + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -120,13 +136,17 @@ func TestAutomationClient_Create(t *testing.T) { t.Run("Create - HTTP PUT returns non 2xx", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPost: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: "{}"} + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -140,13 +160,18 @@ func TestAutomationClient_Create(t *testing.T) { t.Run("Create - Unable to make HTTP POST call", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPost: { - ResponseCode: http.StatusCreated, - ResponseBody: payload, + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusCreated, + ResponseBody: payload, + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.FaultyClient())) @@ -162,28 +187,34 @@ func TestAutomationClient_Update(t *testing.T) { const payload = `{ "id" : "91cc8988-2223-404a-a3f5-5f1a839ecd45", "data" : "some-data1" }` t.Run("Update - try with adminAccess -if fails try without - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: payload, - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: payload, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 1) assert.Equal(t, "true", adminAccessQP[0]) }, }, - }, { - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: payload, - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] - assert.Len(t, adminAccessQP, 0) - }, + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] + assert.Len(t, adminAccessQP, 0) }, }, } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -198,13 +229,18 @@ func TestAutomationClient_Update(t *testing.T) { t.Run("Update - HTTP PUT returns non 2xx", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPut: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: "{}", + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -217,13 +253,19 @@ func TestAutomationClient_Update(t *testing.T) { }) t.Run("Update - HTTP PUT call is not possible", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPut: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: "{}", + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.FaultyClient())) @@ -239,8 +281,9 @@ func TestAutomationClient_Update(t *testing.T) { func TestAutomationClient_Upsert(t *testing.T) { t.Run("Upsert - no ID given", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -251,8 +294,8 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - invalid data", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -263,14 +306,19 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - not able to make HTTP PUT call", func(t *testing.T) { - responses := testutils.ServerResponses{ - http.MethodPut: { - ResponseCode: http.StatusBadRequest, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusBadRequest, + ResponseBody: "{}", + } + }, }, } - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.FaultyClient())) @@ -282,29 +330,36 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - adminAccess query parameter set for workflows", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 1) assert.Equal(t, "true", adminAccessQP[0]) }, }, - }, { - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] - assert.Len(t, adminAccessQP, 1) - assert.Equal(t, "true", adminAccessQP[0]) - }, + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } }, - }} + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] + assert.Len(t, adminAccessQP, 1) + assert.Equal(t, "true", adminAccessQP[0]) + }, + }, + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -315,26 +370,34 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - adminAccess forbidden", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 1) assert.Equal(t, "true", adminAccessQP[0]) }, }, - }, { - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 0) }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -349,22 +412,33 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - Direct update using HTTP PUT API Call returned with != 2xx- creation via POST fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, }, - }, { - http.MethodPut: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - }, { - http.MethodPost: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", + { + POST: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -379,22 +453,32 @@ func TestAutomationClient_Upsert(t *testing.T) { }) t.Run("Upsert - Direct update using HTTP PUT API Call returned with != 2xx - creation via POST OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", + responses := []testutils.ResponseDef{ + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, }, - }, { - http.MethodPut: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + { + PUT: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - }, { - http.MethodPost: { - ResponseCode: http.StatusCreated, - ResponseBody: "{}", + { + POST: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusCreated, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -411,8 +495,8 @@ func TestAutomationClient_Upsert(t *testing.T) { func TestAutomationClient_Delete(t *testing.T) { t.Run("Delete - no ID given", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.Client())) @@ -423,8 +507,8 @@ func TestAutomationClient_Delete(t *testing.T) { }) t.Run("Delete - HTTP Call fails", func(t *testing.T) { - responses := testutils.ServerResponses{} - server := testutils.NewHTTPTestServer(t, []testutils.ServerResponses{responses}) + responses := []testutils.ResponseDef{} + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := automation.NewClient(rest.NewClient(server.URL(), server.FaultyClient())) @@ -435,29 +519,36 @@ func TestAutomationClient_Delete(t *testing.T) { }) t.Run("Delete - adminAccess query parameter set", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 1) assert.Equal(t, "true", adminAccessQP[0]) }, }, - }, { - http.MethodDelete: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] - assert.Len(t, adminAccessQP, 1) - assert.Equal(t, "true", adminAccessQP[0]) - }, + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] + assert.Len(t, adminAccessQP, 1) + assert.Equal(t, "true", adminAccessQP[0]) }, - }} + }, + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -468,26 +559,34 @@ func TestAutomationClient_Delete(t *testing.T) { }) t.Run("Delete - adminAccess forbidden", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 1) assert.Equal(t, "true", adminAccessQP[0]) }, }, - }, { - http.MethodDelete: { - ResponseCode: http.StatusOK, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - adminAccessQP := request.URL.Query()["adminAccess"] + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + adminAccessQP := req.URL.Query()["adminAccess"] assert.Len(t, adminAccessQP, 0) }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -501,17 +600,25 @@ func TestAutomationClient_Delete(t *testing.T) { }) t.Run("Delete - adminAccess forbidden - DELETE API Call returned with != 2xx", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, }, - }, { - http.MethodDelete: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -525,12 +632,17 @@ func TestAutomationClient_Delete(t *testing.T) { }) t.Run("Delete - adminAccess forbidden - resource not found", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -560,25 +672,31 @@ func TestAutomationClient_List(t *testing.T) { } t.Run("List - Paginated - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payloadePages[0], - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"0"}, request.URL.Query()["offset"]) + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payloadePages[0], + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"0"}, req.URL.Query()["offset"]) }, }, - }, { - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payloadePages[1], - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"2"}, request.URL.Query()["offset"]) - }, + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payloadePages[1], + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"2"}, req.URL.Query()["offset"]) }, }, } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -592,37 +710,45 @@ func TestAutomationClient_List(t *testing.T) { }) t.Run("List - Paginated - With Admin Permissions Missing", func(t *testing.T) { - responses := []testutils.ServerResponses{ + + responses := []testutils.ResponseDef{ { - http.MethodGet: { - ResponseCode: http.StatusForbidden, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"true"}, request.URL.Query()["adminAccess"]) - }, + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "{}", + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"true"}, req.URL.Query()["adminAccess"]) }, }, { - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: `{ "count": 2,"results": [ {"id": "82e7e7a4-dc69-4a7f-b0ad-7123f579ddf6","title": "Workflow1"} ] }`, - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"false"}, request.URL.Query()["adminAccess"]) - assert.Equal(t, []string{"0"}, request.URL.Query()["offset"]) - }, + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: `{ "count": 2,"results": [ {"id": "82e7e7a4-dc69-4a7f-b0ad-7123f579ddf6","title": "Workflow1"} ] }`, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"false"}, req.URL.Query()["adminAccess"]) + assert.Equal(t, []string{"0"}, req.URL.Query()["offset"]) }, }, { - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: `{ "count": 2,"results": [ {"id": "da105889-3817-435a-8b15-ec9777374b99","title": "Workflow2"} ] }`, - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"false"}, request.URL.Query()["adminAccess"]) - assert.Equal(t, []string{"1"}, request.URL.Query()["offset"]) - }, + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: `{ "count": 2,"results": [ {"id": "da105889-3817-435a-8b15-ec9777374b99","title": "Workflow2"} ] }`, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"false"}, req.URL.Query()["adminAccess"]) + assert.Equal(t, []string{"1"}, req.URL.Query()["offset"]) }, }, } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -636,25 +762,32 @@ func TestAutomationClient_List(t *testing.T) { }) t.Run("List - Paginated - Getting one page fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payloadePages[0], - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"0"}, request.URL.Query()["offset"]) + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payloadePages[0], + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"0"}, req.URL.Query()["offset"]) }, }, - }, { - http.MethodGet: { - ResponseCode: http.StatusInternalServerError, - ResponseBody: "{}", - ValidateRequestFunc: func(request *http.Request) { - assert.Equal(t, []string{"2"}, request.URL.Query()["offset"]) - }, + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusInternalServerError, + ResponseBody: `{}`, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Equal(t, []string{"2"}, req.URL.Query()["offset"]) }, }, } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -667,13 +800,17 @@ func TestAutomationClient_List(t *testing.T) { }) t.Run("List - API Call returned with != 2xx", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusBadRequest, - ResponseBody: "{}", + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, req *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusBadRequest, + ResponseBody: "{}", + } + }, }, - }, } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -687,10 +824,7 @@ func TestAutomationClient_List(t *testing.T) { }) t.Run("List - API Call failed", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: {}, - }, - } + responses := []testutils.ResponseDef{} server := testutils.NewHTTPTestServer(t, responses) defer server.Close() diff --git a/api/clients/buckets/bucket_test.go b/api/clients/buckets/bucket_test.go index edf5053..c4c0a90 100644 --- a/api/clients/buckets/bucket_test.go +++ b/api/clients/buckets/bucket_test.go @@ -42,12 +42,17 @@ func TestGet(t *testing.T) { "version": 1 }` - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payload, + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -61,12 +66,17 @@ func TestGet(t *testing.T) { }) t.Run("correctly create the error in case of a server issue", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -83,36 +93,41 @@ func TestGet(t *testing.T) { func TestList(t *testing.T) { t.Run("successfully fetch a list of buckets", func(t *testing.T) { const bucket1 = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "active", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 -}` + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "active", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 + }` const bucket2 = `{ - "bucketName": "another name", - "table": "metrics", - "displayName": "Some logs", - "status": "active", - "retentionDays": 31, - "metricInterval": "PT2M", - "version": 42 -}` + "bucketName": "another name", + "table": "metrics", + "displayName": "Some logs", + "status": "active", + "retentionDays": 31, + "metricInterval": "PT2M", + "version": 42 + }` payload := fmt.Sprintf(`{ - "buckets": [ - %s, - %s - ] -}`, bucket1, bucket2) - - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payload, - }, - }} + "buckets": [ + %s, + %s + ] + }`, bucket1, bucket2) + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, + }, + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -128,12 +143,16 @@ func TestList(t *testing.T) { t.Run("successfully returns empty response if no buckets exist", func(t *testing.T) { const payload = `{ "buckets": [] }` - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payload, + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -148,12 +167,16 @@ func TestList(t *testing.T) { }) t.Run("successfully returns response in case of HTTP error", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -186,31 +209,36 @@ func TestList(t *testing.T) { func TestUpsert(t *testing.T) { const creatingBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "creating", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "creating", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` const activeBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "active", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "active", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` t.Run("create new bucket - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusOK, - ResponseBody: creatingBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: creatingBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { data, err := io.ReadAll(req.Body) assert.NoError(t, err) @@ -221,11 +249,15 @@ func TestUpsert(t *testing.T) { assert.Equal(t, "bucket name", m["bucketName"]) }, }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -281,20 +313,34 @@ func TestUpsert(t *testing.T) { }) t.Run("create fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusForbidden, - ResponseBody: "Bucket exists", + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "Bucket exists", + } + }, }, - http.MethodGet: { - ResponseCode: http.StatusNotFound, - ResponseBody: "{}", + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "{}", + } + }, }, - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, + { + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -309,22 +355,40 @@ func TestUpsert(t *testing.T) { }) t.Run("bucket exists, update - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusConflict, - ResponseBody: "Bucket exists", - }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, - ValidateRequestFunc: func(req *http.Request) { - assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: "Bucket exists", + } + }, + }, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, + }, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } }, }, - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + { + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { data, err := io.ReadAll(req.Body) assert.NoError(t, err) @@ -335,7 +399,8 @@ func TestUpsert(t *testing.T) { assert.Equal(t, "bucket name", m["bucketName"]) }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -355,20 +420,42 @@ func TestUpsert(t *testing.T) { }) t.Run("bucket exists, update fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusConflict, - ResponseBody: "Bucket exists", + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: "Bucket exists", + } + }, }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: "no write access message", + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - }} + { + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "no write access message", + } + }, + }, + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -383,20 +470,46 @@ func TestUpsert(t *testing.T) { }) t.Run("bucket exists, update fails with conflict", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusConflict, - ResponseBody: "Bucket exists", - }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: "Bucket exists", + } + }, }, - http.MethodPut: { - ResponseCode: http.StatusConflict, - ResponseBody: `some conflicting error'`, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - }} + } + + for i := 0; i < 5; i++ { + get := testutils.ResponseDef{ + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, + } + responses = append(responses, get) + put := testutils.ResponseDef{ + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: `some conflicting error'`, + } + }, + } + responses = append(responses, put) + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -413,16 +526,39 @@ func TestUpsert(t *testing.T) { }) t.Run("bucket exists, update fails because GET fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusConflict, - ResponseBody: "expected error, we don't want to create", + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: "expected error, we don't want to create", + } + }, }, - http.MethodGet: { - ResponseCode: http.StatusNotFound, - ResponseBody: "expected error, we don't want to get", + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "expected error, we don't want to get", + } + }, }, - }} + } + + for i := 0; i < 5; i++ { + get := testutils.ResponseDef{ + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + ResponseBody: "expected error, we don't want to get", + } + }, + } + responses = append(responses, get) + + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -480,19 +616,28 @@ func TestUpsert(t *testing.T) { }) t.Run("bucket exists, but is not modified, no update happens", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusConflict, - ResponseBody: "Bucket exists", - }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: "Bucket exists", + } + }, + }, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -510,22 +655,26 @@ func TestUpsert(t *testing.T) { func TestDelete(t *testing.T) { const someBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "deleting", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "deleting", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` t.Run("delete bucket - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusAccepted, - ResponseBody: someBucketResponse, + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusAccepted, + ResponseBody: someBucketResponse, + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -540,11 +689,17 @@ func TestDelete(t *testing.T) { }) t.Run("delete bucket - not found", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusNotFound, + + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -559,11 +714,16 @@ func TestDelete(t *testing.T) { }) t.Run("delete bucket - network error", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodDelete: { - ResponseCode: http.StatusNotFound, + responses := []testutils.ResponseDef{ + { + DELETE: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusNotFound, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -579,45 +739,55 @@ func TestDelete(t *testing.T) { func TestCreate(t *testing.T) { const someBucketData = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` const creatingBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "creating", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "creating", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` const activeBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "active", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "active", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` t.Run("create bucket - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusCreated, - ResponseBody: creatingBucketResponse, + + responses := []testutils.ResponseDef{ + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusCreated, + ResponseBody: creatingBucketResponse, + } + }, }, - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: activeBucketResponse, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: activeBucketResponse, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -668,9 +838,11 @@ func TestCreate(t *testing.T) { }) t.Run("create bucket - network error", func(t *testing.T) { - responses := []testutils.ServerResponses{{ + + responses := []testutils.ResponseDef{ // no request should reach test server - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -684,9 +856,10 @@ func TestCreate(t *testing.T) { }) t.Run("create bucket - invalid data", func(t *testing.T) { - responses := []testutils.ServerResponses{{ + + responses := []testutils.ResponseDef{ // no request should reach test server - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -703,25 +876,43 @@ func TestCreate(t *testing.T) { func TestUpdate(t *testing.T) { const someBucketResponse = `{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "status": "active", - "retentionDays": 462, - "metricInterval": "PT1M", - "version": 1 + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "status": "active", + "retentionDays": 462, + "metricInterval": "PT1M", + "version": 1 }` t.Run("update fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: someBucketResponse, + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, }, - http.MethodPut: { - ResponseCode: http.StatusForbidden, - ResponseBody: "no write access message", + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, }, - }} + { + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "no write access message", + } + }, + }, + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -735,18 +926,37 @@ func TestUpdate(t *testing.T) { }) t.Run("update bucket - OK", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: someBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) + }, + }, + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) }, }, - http.MethodPut: { - ResponseCode: http.StatusOK, - ResponseBody: someBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + { + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { data, err := io.ReadAll(req.Body) assert.NoError(t, err) @@ -757,7 +967,8 @@ func TestUpdate(t *testing.T) { assert.Equal(t, "bucket name", m["bucketName"]) }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -776,26 +987,32 @@ func TestUpdate(t *testing.T) { }) t.Run("unmodified bucket - nothing happens", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: someBucketResponse, - ValidateRequestFunc: func(req *http.Request) { + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() client := buckets.NewClient(rest.NewClient(server.URL(), server.Client())) data := []byte(`{ - "bucketName": "bucket name", - "table": "metrics", - "displayName": "Default metrics (15 months)", - "retentionDays": 462, - "metricInterval": "PT1M" -}`) + "bucketName": "bucket name", + "table": "metrics", + "displayName": "Default metrics (15 months)", + "retentionDays": 462, + "metricInterval": "PT1M" + }`) ctx := testutils.ContextWithLogger(t) @@ -805,16 +1022,43 @@ func TestUpdate(t *testing.T) { }) t.Run("Update fails with conflict", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: someBucketResponse, - }, - http.MethodPut: { - ResponseCode: http.StatusConflict, - ResponseBody: `some conflicting error'`, + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + ValidateRequest: func(t *testing.T, req *http.Request) { + assert.Contains(t, req.URL.String(), url.PathEscape("bucket name")) + }, }, - }} + } + + for i := 0; i < 5; i++ { + get := testutils.ResponseDef{ + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: someBucketResponse, + } + }, + } + responses = append(responses, get) + + put := testutils.ResponseDef{ + PUT: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusConflict, + ResponseBody: `some conflicting error'`, + } + }, + } + responses = append(responses, put) + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -830,16 +1074,33 @@ func TestUpdate(t *testing.T) { }) t.Run("Update fails because GET fails", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodPost: { - ResponseCode: http.StatusForbidden, - ResponseBody: "expected error, we don't want to create", + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "expected error, we don't want to get", + } + }, }, - http.MethodGet: { - ResponseCode: http.StatusForbidden, - ResponseBody: "expected error, we don't want to get", + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "expected error, we don't want to get", + } + }, + }, + { + POST: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusForbidden, + ResponseBody: "expected error, we don't want to create", + } + }, }, - }} + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -956,12 +1217,18 @@ func TestDecodingBucketResponses(t *testing.T) { } t.Run("Get single bucket", func(t *testing.T) { - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: bucket1, + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: bucket1, + } + }, }, - }} + } + server := testutils.NewHTTPTestServer(t, responses) defer server.Close() @@ -982,18 +1249,22 @@ func TestDecodingBucketResponses(t *testing.T) { t.Run("List multiple buckets", func(t *testing.T) { payload := fmt.Sprintf(`{ - "buckets": [ - %s, - %s - ] -}`, bucket1, bucket2) - - responses := []testutils.ServerResponses{{ - http.MethodGet: { - ResponseCode: http.StatusOK, - ResponseBody: payload, - }, - }} + "buckets": [ + %s, + %s + ] + }`, bucket1, bucket2) + + responses := []testutils.ResponseDef{ + { + GET: func(t *testing.T, request *http.Request) testutils.Response { + return testutils.Response{ + ResponseCode: http.StatusOK, + ResponseBody: payload, + } + }, + }, + } server := testutils.NewHTTPTestServer(t, responses) defer server.Close() diff --git a/internal/testutils/testserver.go b/internal/testutils/testserver.go index 1960430..0ac6c3f 100644 --- a/internal/testutils/testserver.go +++ b/internal/testutils/testserver.go @@ -16,6 +16,7 @@ package testutils import ( "errors" + "fmt" "net/http" "net/http/httptest" "net/url" @@ -51,55 +52,87 @@ func (t TestServer) FaultyClient() *http.Client { return client } -// HTTPMethod is an alias for string, representing an HTTP method. -type HTTPMethod = string - -// ServerResponses is a map of HTTP methods to expected responses for testing. -type ServerResponses map[HTTPMethod]struct { - ResponseCode int // HTTP response status code. - ResponseBody string // HTTP response body. - ValidateRequestFunc func(*http.Request) // Function to validate incoming requests. -} - // NewHTTPTestServer creates a new HTTP test server with the specified responses for each HTTP method. -func NewHTTPTestServer(t *testing.T, responses []ServerResponses) *TestServer { +func NewHTTPTestServer(t *testing.T, responses []ResponseDef) *TestServer { + for i, r := range responses { + // it's only allowed to set ONE handler per response + if !checkExactlyOneHandlerSet(r) { + panic(fmt.Sprintf("Response nr. %d has more than one handler defined", i)) + } + } + testServer := &TestServer{} handler := func(rw http.ResponseWriter, req *http.Request) { testServer.calls++ - // Special case: if only one response is specified, this will be reused - // for each and every call - if len(responses) == 1 { - if res, found := responses[0][req.Method]; found { - if res.ValidateRequestFunc != nil { - res.ValidateRequestFunc(req) - } - rw.WriteHeader(res.ResponseCode) - _, _ = rw.Write([]byte(res.ResponseBody)) // nosemgrep: go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter - } else { - t.Errorf("unexpected HTTP method call: %s", req.Method) - } - return + if len(responses) <= testServer.calls-1 { + t.Errorf("Exceeded number of calls to test server (expected: %d)", len(responses)) } - if len(responses) <= testServer.calls-1 { - t.Fail() - t.Fatalf("Exceeded number of calls to test server (expected: %d)", len(responses)) + responseDef := responses[testServer.calls-1] + handlers := map[string]func(*testing.T, *http.Request) Response{ + http.MethodGet: responseDef.Get, + http.MethodPost: responseDef.Post, + http.MethodPut: responseDef.Put, + http.MethodDelete: responseDef.Delete, } - if res, found := responses[testServer.calls-1][req.Method]; found { - if res.ValidateRequestFunc != nil { - res.ValidateRequestFunc(req) - } - rw.WriteHeader(res.ResponseCode) - _, _ = rw.Write([]byte(res.ResponseBody)) // nosemgrep: go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter - } else { - t.Errorf("unexpected HTTP method call: %s", req.Method) + handlerFunc, found := handlers[req.Method] + if !found { + panic(fmt.Sprintf("No %s method defined for server call nr. %d", req.Method, testServer.calls)) } + response := handlerFunc(t, req) + rw.WriteHeader(response.ResponseCode) + _, _ = rw.Write([]byte(response.ResponseBody)) // nosemgrep: go.lang.security.audit.xss.no-direct-write-to-responsewriter.no-direct-write-to-responsewriter + responseDef.Validate(t, req) } testServer.Server = httptest.NewServer(http.HandlerFunc(handler)) return testServer } +type Response struct { + ResponseCode int + ResponseBody string +} + +type ResponseDef struct { + GET func(*testing.T, *http.Request) Response + PUT func(*testing.T, *http.Request) Response + POST func(*testing.T, *http.Request) Response + DELETE func(*testing.T, *http.Request) Response + ValidateRequest func(*testing.T, *http.Request) +} + +func (r ResponseDef) Get(t *testing.T, req *http.Request) Response { + if r.GET == nil { + panic("GET() function not defined") + } + return r.GET(t, req) +} +func (r ResponseDef) Put(t *testing.T, req *http.Request) Response { + if r.PUT == nil { + panic("PUT() function not defined") + } + return r.PUT(t, req) +} +func (r ResponseDef) Post(t *testing.T, req *http.Request) Response { + if r.POST == nil { + panic("POST() function not defined") + } + return r.POST(t, req) +} +func (r ResponseDef) Delete(t *testing.T, req *http.Request) Response { + if r.DELETE == nil { + panic("DELETE() function not defined") + } + return r.DELETE(t, req) +} + +func (r ResponseDef) Validate(t *testing.T, req *http.Request) { + if r.ValidateRequest != nil { + r.ValidateRequest(t, req) + } +} + // ErrorTransport is custom transport that always produces a simulated network error. type ErrorTransport struct{} @@ -107,3 +140,20 @@ type ErrorTransport struct{} func (t *ErrorTransport) RoundTrip(_ *http.Request) (*http.Response, error) { return nil, errors.New("simulated network error") } + +func checkExactlyOneHandlerSet(def ResponseDef) bool { + var count int + if def.GET != nil { + count++ + } + if def.POST != nil { + count++ + } + if def.PUT != nil { + count++ + } + if def.DELETE != nil { + count++ + } + return count == 1 +}