diff --git a/rebac-admin-backend/v1/identities_test.go b/rebac-admin-backend/v1/identities_test.go index 536ef3643..6bdd96f52 100644 --- a/rebac-admin-backend/v1/identities_test.go +++ b/rebac-admin-backend/v1/identities_test.go @@ -4,7 +4,6 @@ package v1 import ( - "bytes" "encoding/json" "errors" "fmt" @@ -13,9 +12,10 @@ import ( "net/http/httptest" "testing" - qt "github.com/frankban/quicktest" "go.uber.org/mock/gomock" + qt "github.com/frankban/quicktest" + "github.com/canonical/identity-platform-admin-ui/rebac-admin-backend/v1/interfaces" "github.com/canonical/identity-platform-admin-ui/rebac-admin-backend/v1/resources" ) @@ -28,655 +28,451 @@ var ( //go:generate mockgen -package interfaces -destination ./interfaces/mock_identities.go -source=./interfaces/identities.go //go:generate mockgen -package v1 -destination ./mock_error_response.go -source=./error.go -func TestHandler_GetIdentities(t *testing.T) { +func TestHandler_Identities_ServiceBackendFailures(t *testing.T) { c := qt.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockParams := resources.GetIdentitiesParams{} - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentitiesReturn := resources.PaginatedResponse[resources.Identity]{Data: []resources.Identity{ - {FirstName: &mockFirstName}, - }} - mockIdentityService.EXPECT().ListIdentities(gomock.Any(), gomock.Any()).Return(&mockIdentitiesReturn, nil) - - expectedResponse := resources.GetIdentitiesResponse{ - Data: mockIdentitiesReturn.Data, - Status: 200, + mockErrorResponse := resources.Response{ + Message: "mock-error", + Status: http.StatusInternalServerError, } - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodGet, "/identities", nil) - - sut := handler{Identities: mockIdentityService} - sut.GetIdentities(mockWriter, mockRequest, mockParams) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.GetIdentitiesResponse) - - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &expectedResponse) -} - -func TestHandler_PostIdentitiesSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockIdentityReturn := resources.Identity{FirstName: &mockFirstName} - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().CreateIdentity(gomock.Any(), gomock.Any()).Return(&mockIdentityReturn, nil) - - marshalledIdentity, err := json.Marshal(mockIdentityReturn) - c.Assert(err, qt.IsNil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodPost, "/identities", bytes.NewReader(marshalledIdentity)) - - sut := handler{Identities: mockIdentityService} - sut.PostIdentities(mockWriter, mockRequest) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.Identity) - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusCreated) - c.Assert(response, qt.DeepEquals, &mockIdentityReturn) -} - -func TestHandler_DeleteIdentitiesItemSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().DeleteIdentity(gomock.Any(), gomock.Eq(mockIdentityId)).Return(true, nil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/identities/%s", mockIdentityId), nil) - - sut := handler{Identities: mockIdentityService} - sut.DeleteIdentitiesItem(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(len(data), qt.Equals, 0) -} - -func TestHandler_GetIdentitiesItemSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityReturn := resources.Identity{FirstName: &mockFirstName} - mockIdentityService.EXPECT().GetIdentity(gomock.Any(), gomock.Eq(mockIdentityId)).Return(&mockIdentityReturn, nil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s", mockIdentityId), nil) - - sut := handler{Identities: mockIdentityService} - sut.GetIdentitiesItem(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.Identity) - - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &mockIdentityReturn) -} - -func TestHandler_PutIdentitiesItemSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() + mockError := errors.New("test-error") - mockIdentityReturn := resources.Identity{ - Id: &mockIdentityId, - FirstName: &mockFirstName, + mockIdentity := resources.Identity{ + Email: "foo@bar.com", + Source: "some-source", + AddedBy: "some-added-by", } - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().UpdateIdentity(gomock.Any(), gomock.Any()).Return(&mockIdentityReturn, nil) - - marshalledIdentity, err := json.Marshal(mockIdentityReturn) - c.Assert(err, qt.IsNil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/identities/%s", mockIdentityId), bytes.NewReader(marshalledIdentity)) - - sut := handler{Identities: mockIdentityService} - sut.PutIdentitiesItem(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.Identity) - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &mockIdentityReturn) -} - -func TestHandler_GetIdentitiesItemEntitlementsSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - var ( - entitlementType = "mock-entl-type" - entityName = "mock-entity-name" - entityType = "mock-entity-type" - ) - mockIdentityEntitlements := resources.PaginatedResponse[resources.EntityEntitlement]{ - Data: []resources.EntityEntitlement{ - { - EntitlementType: entitlementType, - EntityName: entityName, - EntityType: entityType, - }, - }, + mockIdentityWithId := resources.Identity{ + Id: stringPtr("some-id"), + Email: "foo@bar.com", + Source: "some-source", + AddedBy: "some-added-by", } - expectedResponse := resources.GetIdentityEntitlementsResponse{ - Data: mockIdentityEntitlements.Data, - Status: http.StatusOK, + type EndpointTest struct { + name string + setupServiceMock func(mockService *interfaces.MockIdentitiesService) + triggerFunc func(h handler, w *httptest.ResponseRecorder) } - params := resources.GetIdentitiesItemEntitlementsParams{} - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().GetIdentityEntitlements(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(¶ms)).Return(&mockIdentityEntitlements, nil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), nil) - - sut := handler{Identities: mockIdentityService} - sut.GetIdentitiesItemEntitlements(mockWriter, mockRequest, mockIdentityId, params) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.GetIdentityEntitlementsResponse) - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &expectedResponse) -} - -func TestHandler_PatchIdentitiesItemEntitlementsSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - identityEntitlementPatches := []resources.IdentityEntitlementsPatchItem{ + tests := []EndpointTest{ { - Entitlement: resources.EntityEntitlement{}, - Op: "add", + name: "TestGetIdentitiesFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().ListIdentities(gomock.Any(), gomock.Any()).Return(nil, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + mockParams := resources.GetIdentitiesParams{} + mockRequest := httptest.NewRequest(http.MethodGet, "/identities", nil) + h.GetIdentities(w, mockRequest, mockParams) + }, }, - } - identityEntitlementPatchRequest := resources.IdentityEntitlementsPatchRequestBody{ - Patches: identityEntitlementPatches, - } - - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().PatchIdentityEntitlements(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(identityEntitlementPatches)).Return(true, nil) - - marshalledPatchReq, err := json.Marshal(identityEntitlementPatchRequest) - c.Assert(err, qt.IsNil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), bytes.NewReader(marshalledPatchReq)) - - sut := handler{Identities: mockIdentityService} - sut.PatchIdentitiesItemEntitlements(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) -} - -func TestHandler_GetIdentitiesItemGroupsSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - var ( - mockId = "mock-id" - mockName = "test-groupname" - ) - - mockIdentityGroups := resources.PaginatedResponse[resources.Group]{ - Data: []resources.Group{ - { - Id: &mockId, - Name: mockName, + { + name: "TestPostIdentitiesFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().CreateIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + request := newTestRequest(http.MethodPost, "/identities", &mockIdentity) + h.PostIdentities(w, request) }, }, - } - expectedResponse := resources.GetIdentityGroupsResponse{ - Data: mockIdentityGroups.Data, - Status: http.StatusOK, - } - - params := resources.GetIdentitiesItemGroupsParams{} - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().GetIdentityGroups(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(¶ms)).Return(&mockIdentityGroups, nil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/groups", mockIdentityId), nil) - - sut := handler{Identities: mockIdentityService} - sut.GetIdentitiesItemGroups(mockWriter, mockRequest, mockIdentityId, params) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.GetIdentityGroupsResponse) - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &expectedResponse) -} - -func TestHandler_PatchIdentitiesItemGroupsSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - identityGroupsPatches := []resources.IdentityGroupsPatchItem{ { - Group: "test-group-identifier", - Op: "add", + name: "TestDeleteIdentitiesItemFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().DeleteIdentity(gomock.Any(), gomock.Any()).Return(false, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + mockRequest := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/identities/%s", mockIdentityId), nil) + h.DeleteIdentitiesItem(w, mockRequest, "test-id") + }, }, - } - identityGroupsPatchRequest := resources.IdentityGroupsPatchRequestBody{ - Patches: identityGroupsPatches, - } - - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().PatchIdentityGroups(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(identityGroupsPatches)).Return(true, nil) - - marshalledPatchReq, err := json.Marshal(identityGroupsPatchRequest) - c.Assert(err, qt.IsNil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/groups", mockIdentityId), bytes.NewReader(marshalledPatchReq)) - - sut := handler{Identities: mockIdentityService} - sut.PatchIdentitiesItemGroups(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) -} - -func TestHandler_GetIdentitiesItemRolesSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - var ( - mockId = "mock-id" - mockName = "test-rolename" - ) - - mockIdentityRoles := resources.PaginatedResponse[resources.Role]{ - Data: []resources.Role{ - { - Id: &mockId, - Name: mockName, + { + name: "TestGetIdentitiesItemFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().GetIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s", mockIdentityId), nil) + h.GetIdentitiesItem(w, mockRequest, "test-id") }, }, - } - expectedResponse := resources.GetIdentityRolesResponse{ - Data: mockIdentityRoles.Data, - Status: http.StatusOK, - } - - params := resources.GetIdentitiesItemRolesParams{} - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().GetIdentityRoles(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(¶ms)).Return(&mockIdentityRoles, nil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/roles", mockIdentityId), nil) - - sut := handler{Identities: mockIdentityService} - sut.GetIdentitiesItemRoles(mockWriter, mockRequest, mockIdentityId, params) - - result := mockWriter.Result() - defer result.Body.Close() - - data, err := io.ReadAll(result.Body) - c.Assert(err, qt.IsNil) - - response := new(resources.GetIdentityRolesResponse) - err = json.Unmarshal(data, response) - - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) - c.Assert(response, qt.DeepEquals, &expectedResponse) -} - -func TestHandler_PatchIdentitiesItemRolesSuccess(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - identityRolesPatches := []resources.IdentityRolesPatchItem{ { - Role: "test-role-identifier", - Op: "add", + name: "TestPutIdentitiesItemFailureUpdate", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().UpdateIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + request := newTestRequest(http.MethodPut, fmt.Sprintf("/identities/%s", mockIdentityId), &mockIdentityWithId) + h.PutIdentitiesItem(w, request, "test-id") + }, + }, + { + name: "TestGetIdentitiesItemEntitlementsFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().GetIdentityEntitlements(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + }, + triggerFunc: func(h handler, w *httptest.ResponseRecorder) { + params := resources.GetIdentitiesItemEntitlementsParams{} + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), nil) + h.GetIdentitiesItemEntitlements(w, mockRequest, "test-id", params) + }, }, - } - identityRolesPatchRequest := resources.IdentityRolesPatchRequestBody{ - Patches: identityRolesPatches, - } - - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - mockIdentityService.EXPECT().PatchIdentityRoles(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(identityRolesPatches)).Return(true, nil) - - marshalledPatchReq, err := json.Marshal(identityRolesPatchRequest) - c.Assert(err, qt.IsNil) - - mockWriter := httptest.NewRecorder() - mockRequest := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/roles", mockIdentityId), bytes.NewReader(marshalledPatchReq)) - - sut := handler{Identities: mockIdentityService} - sut.PatchIdentitiesItemRoles(mockWriter, mockRequest, mockIdentityId) - - result := mockWriter.Result() - - c.Assert(result.StatusCode, qt.Equals, http.StatusOK) -} - -func TestHandler_Identities_ValidationErrors(t *testing.T) { - c := qt.New(t) - ctrl := gomock.NewController(t) - defer ctrl.Finish() - - // need a value that is not a struct to trigger Decode error - mockInvalidRequestBody := true - - invalidRequestBody, _ := json.Marshal(mockInvalidRequestBody) - - type EndpointTest struct { - name string - triggerFunc func(h handler, w *httptest.ResponseRecorder) - } - - tests := []EndpointTest{ { - name: "TestPatchIdentitiesEntitlementsFailureInvalidRequest", + name: "TestPatchIdentitiesItemEntitlementsFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().PatchIdentityEntitlements(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - req := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), bytes.NewReader(invalidRequestBody)) - h.PatchIdentitiesItemEntitlements(w, req, mockIdentityId) + patches := &resources.IdentityEntitlementsPatchRequestBody{} + request := newTestRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), patches) + h.PatchIdentitiesItemEntitlements(w, request, "test-id") }, }, { - name: "TestPatchIdentitiesGroupsFailureInvalidRequest", + name: "TestGetIdentitiesItemGroupsFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().GetIdentityGroups(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - req := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/groups", mockIdentityId), bytes.NewReader(invalidRequestBody)) - h.PatchIdentitiesItemGroups(w, req, mockIdentityId) + params := resources.GetIdentitiesItemGroupsParams{} + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/groups", mockIdentityId), nil) + h.GetIdentitiesItemGroups(w, mockRequest, "test-id", params) }, }, { - name: "TestPatchIdentitiesRolesFailureInvalidRequest", + name: "TestPatchIdentitiesItemGroupsFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().PatchIdentityGroups(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - req := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/roles", mockIdentityId), bytes.NewReader(invalidRequestBody)) - h.PatchIdentitiesItemRoles(w, req, mockIdentityId) + patches := &resources.IdentityGroupsPatchRequestBody{} + request := newTestRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/groups", mockIdentityId), patches) + h.PatchIdentitiesItemGroups(w, request, "test-id") }, }, { - name: "TestPostIdentitiesFailureInvalidRequest", + name: "TestGetIdentitiesItemRolesFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().GetIdentityRoles(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - req := httptest.NewRequest(http.MethodPost, fmt.Sprintf("/identities/%s", mockIdentityId), bytes.NewReader(invalidRequestBody)) - h.PostIdentities(w, req) + params := resources.GetIdentitiesItemRolesParams{} + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/roles", mockIdentityId), nil) + h.GetIdentitiesItemRoles(w, mockRequest, "test-id", params) }, }, { - name: "TestPutIdentitiesFailureInvalidRequest", + name: "TestPatchIdentitiesItemRolesFailure", + setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { + mockService.EXPECT().PatchIdentityRoles(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - req := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/identities/%s", mockIdentityId), bytes.NewReader(invalidRequestBody)) - h.PutIdentitiesItem(w, req, mockIdentityId) + patches := &resources.IdentityRolesPatchRequestBody{} + request := newTestRequest(http.MethodPatch, "/identities/test-id/roles", patches) + h.PatchIdentitiesItemRoles(w, request, "test-id") }, }, } - for _, test := range tests { tt := test c.Run(tt.name, func(c *qt.C) { + mockErrorResponseMapper := NewMockErrorResponseMapper(ctrl) + mockErrorResponseMapper.EXPECT().MapError(gomock.Any()).Return(&mockErrorResponse) + + mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) + tt.setupServiceMock(mockIdentityService) + mockWriter := httptest.NewRecorder() - sut := handler{} + sut := handler{ + Identities: mockIdentityService, + IdentitiesErrorMapper: mockErrorResponseMapper, + } tt.triggerFunc(sut, mockWriter) result := mockWriter.Result() defer result.Body.Close() - c.Assert(result.StatusCode, qt.Equals, http.StatusBadRequest) + c.Assert(result.StatusCode, qt.Equals, http.StatusInternalServerError) data, err := io.ReadAll(result.Body) c.Assert(err, qt.IsNil) response := new(resources.Response) - err = json.Unmarshal(data, response) - c.Assert(err, qt.IsNil) - c.Assert(response.Status, qt.Equals, http.StatusBadRequest) - c.Assert(response.Message, qt.Equals, "Bad Request: request doesn't match the expected schema") + c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) + c.Assert(response.Status, qt.Equals, 500) + c.Assert(response.Message, qt.Equals, "mock-error") }) } } -func TestHandler_Identities_ServiceBackendFailures(t *testing.T) { +func TestHandler_Identities_Success(t *testing.T) { c := qt.New(t) ctrl := gomock.NewController(t) defer ctrl.Finish() - mockErrorResponse := resources.Response{ - Message: "mock-error", - Status: http.StatusInternalServerError, + mockIdentityObject := resources.Identity{ + Email: "foo@bar.com", + Source: "some-source", + AddedBy: "some-added-by", } - mockError := errors.New("test-error") + mockEntitlements := resources.PaginatedResponse[resources.EntityEntitlement]{ + Data: []resources.EntityEntitlement{ + { + EntitlementType: "mock-entl-type", + EntityName: "mock-entity-name", + EntityType: "mock-entity-type", + }, + }, + } + + mockGroups := resources.PaginatedResponse[resources.Group]{ + Data: []resources.Group{{ + Id: stringPtr("some-identity-group-id"), + Name: "some-identity-group-name", + }}, + } + + mockRoles := resources.PaginatedResponse[resources.Role]{ + Data: []resources.Role{{ + Id: &mockGroupRoleId, + Name: mockGroupRoleName, + }}, + } type EndpointTest struct { name string setupServiceMock func(mockService *interfaces.MockIdentitiesService) triggerFunc func(h handler, w *httptest.ResponseRecorder) + expectedStatus int + expectedBody any } tests := []EndpointTest{ { - name: "TestGetIdentitiesFailure", + name: "TestHandler_Identities_ListIdentitiesSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().ListIdentities(gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + ListIdentities(gomock.Any(), gomock.Eq(&resources.GetIdentitiesParams{})). + Return(&resources.PaginatedResponse[resources.Identity]{ + Data: []resources.Identity{mockIdentityObject}, + }, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - mockParams := resources.GetIdentitiesParams{} mockRequest := httptest.NewRequest(http.MethodGet, "/identities", nil) - h.GetIdentities(w, mockRequest, mockParams) + h.GetIdentities(w, mockRequest, resources.GetIdentitiesParams{}) + }, + expectedStatus: http.StatusOK, + expectedBody: resources.GetIdentitiesResponse{ + Data: []resources.Identity{mockIdentityObject}, + Status: http.StatusOK, }, }, { - name: "TestPostIdentitiesFailure", + name: "TestHandler_Identities_CreateIdentitySuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().CreateIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + CreateIdentity(gomock.Any(), gomock.Eq(&mockIdentityObject)). + Return(&mockIdentityObject, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - identity, _ := json.Marshal(&resources.Identity{}) - request := httptest.NewRequest(http.MethodPost, "/identities", bytes.NewReader(identity)) - h.PostIdentities(w, request) + mockRequest := newTestRequest(http.MethodPost, "/identities", &mockIdentityObject) + h.PostIdentities(w, mockRequest) }, + expectedStatus: http.StatusCreated, + expectedBody: mockIdentityObject, }, { - name: "TestDeleteIdentitiesItemFailure", + name: "TestHandler_Identities_GetIdentitySuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().DeleteIdentity(gomock.Any(), gomock.Any()).Return(false, mockError) + mockService.EXPECT(). + GetIdentity(gomock.Any(), gomock.Eq(mockIdentityId)). + Return(&mockIdentityObject, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - mockRequest := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/identities/%s", mockIdentityId), nil) - h.DeleteIdentitiesItem(w, mockRequest, "test-id") + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s", mockIdentityId), nil) + h.GetIdentitiesItem(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, + expectedBody: mockIdentityObject, }, { - name: "TestGetIdentitiesItemFailure", + name: "TestHandler_Identities_UpdateIdentitySuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().GetIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + UpdateIdentity(gomock.Any(), gomock.Eq(&mockIdentityObject)). + Return(&mockIdentityObject, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s", mockIdentityId), nil) - h.GetIdentitiesItem(w, mockRequest, "test-id") + mockRequest := newTestRequest(http.MethodPut, fmt.Sprintf("/identities/%s", mockIdentityId), &mockIdentityObject) + h.PutIdentitiesItem(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, + expectedBody: mockIdentityObject, }, { - name: "TestPutIdentitiesItemFailureUpdate", + name: "TestHandler_Identities_DeleteIdentitySuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().UpdateIdentity(gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + DeleteIdentity(gomock.Any(), gomock.Eq(mockIdentityId)). + Return(true, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - identity, _ := json.Marshal(&resources.Identity{Id: &mockIdentityId}) - request := httptest.NewRequest(http.MethodPut, fmt.Sprintf("/identities/%s", mockIdentityId), bytes.NewReader(identity)) - h.PutIdentitiesItem(w, request, "test-id") + mockRequest := httptest.NewRequest(http.MethodDelete, fmt.Sprintf("/identities/%s", mockIdentityId), nil) + h.DeleteIdentitiesItem(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, }, { - name: "TestGetIdentitiesItemEntitlementsFailure", + name: "TestHandler_Identities_GetIdentityGroupsSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().GetIdentityEntitlements(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + GetIdentityGroups(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(&resources.GetIdentitiesItemGroupsParams{})). + Return(&mockGroups, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - params := resources.GetIdentitiesItemEntitlementsParams{} - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), nil) - h.GetIdentitiesItemEntitlements(w, mockRequest, "test-id", params) + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/groups", mockIdentityId), nil) + h.GetIdentitiesItemGroups(w, mockRequest, mockIdentityId, resources.GetIdentitiesItemGroupsParams{}) + }, + expectedStatus: http.StatusOK, + expectedBody: resources.GetIdentityGroupsResponse{ + Data: mockGroups.Data, + Status: http.StatusOK, }, }, { - name: "TestPatchIdentitiesItemEntitlementsFailure", + name: "TestHandler_Identities_PatchIdentityGroupsSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().PatchIdentityEntitlements(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + mockService.EXPECT(). + PatchIdentityGroups(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Any()). + Return(true, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - patches, _ := json.Marshal(&resources.IdentityEntitlementsPatchRequestBody{}) - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), bytes.NewReader(patches)) - h.PatchIdentitiesItemEntitlements(w, request, "test-id") + patches := resources.IdentityGroupsPatchRequestBody{ + Patches: []resources.IdentityGroupsPatchItem{ + { + Group: *mockGroups.Data[0].Id, + Op: "add", + }, + }, + } + mockRequest := newTestRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/groups", mockIdentityId), &patches) + h.PatchIdentitiesItemGroups(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, }, { - name: "TestGetIdentitiesItemGroupsFailure", + name: "TestHandler_Identities_GetIdentityRolesSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().GetIdentityGroups(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + GetIdentityRoles(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(&resources.GetIdentitiesItemRolesParams{})). + Return(&mockRoles, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - params := resources.GetIdentitiesItemGroupsParams{} - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/groups", mockIdentityId), nil) - h.GetIdentitiesItemGroups(w, mockRequest, "test-id", params) + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/roles", mockIdentityId), nil) + h.GetIdentitiesItemRoles(w, mockRequest, mockGroupId, resources.GetIdentitiesItemRolesParams{}) + }, + expectedStatus: http.StatusOK, + expectedBody: resources.GetIdentityRolesResponse{ + Data: mockRoles.Data, + Status: http.StatusOK, }, }, { - name: "TestPatchIdentitiesItemGroupsFailure", + name: "TestHandler_Identities_PatchIdentityRolesSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().PatchIdentityGroups(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + mockService.EXPECT(). + PatchIdentityRoles(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Any()). + Return(true, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - patches, _ := json.Marshal(&resources.IdentityGroupsPatchRequestBody{}) - request := httptest.NewRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/groups", mockIdentityId), bytes.NewReader(patches)) - h.PatchIdentitiesItemGroups(w, request, "test-id") + patches := resources.IdentityRolesPatchRequestBody{ + Patches: []resources.IdentityRolesPatchItem{ + { + Role: *mockRoles.Data[0].Id, + Op: "add", + }, + }, + } + mockRequest := newTestRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/roles", mockIdentityId), &patches) + h.PatchIdentitiesItemRoles(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, }, { - name: "TestGetIdentitiesItemRolesFailure", + name: "TestHandler_Identities_GetIdentityEntitlementsSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().GetIdentityRoles(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil, mockError) + mockService.EXPECT(). + GetIdentityEntitlements(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Eq(&resources.GetIdentitiesItemEntitlementsParams{})). + Return(&mockEntitlements, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - params := resources.GetIdentitiesItemRolesParams{} - mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/roles", mockIdentityId), nil) - h.GetIdentitiesItemRoles(w, mockRequest, "test-id", params) + mockRequest := httptest.NewRequest(http.MethodGet, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), nil) + h.GetIdentitiesItemEntitlements(w, mockRequest, mockGroupId, resources.GetIdentitiesItemEntitlementsParams{}) + }, + expectedStatus: http.StatusOK, + expectedBody: resources.GetIdentityEntitlementsResponse{ + Data: mockEntitlements.Data, + Status: http.StatusOK, }, }, { - name: "TestPatchIdentitiesItemRolesFailure", + name: "TestHandler_Identities_PatchIdentityEntitlementsSuccess", setupServiceMock: func(mockService *interfaces.MockIdentitiesService) { - mockService.EXPECT().PatchIdentityRoles(gomock.Any(), gomock.Any(), gomock.Any()).Return(false, mockError) + mockService.EXPECT(). + PatchIdentityEntitlements(gomock.Any(), gomock.Eq(mockIdentityId), gomock.Any()). + Return(true, nil) }, triggerFunc: func(h handler, w *httptest.ResponseRecorder) { - patches, _ := json.Marshal(&resources.IdentityRolesPatchRequestBody{}) - request := httptest.NewRequest(http.MethodPatch, "/identities/test-id/roles", bytes.NewReader(patches)) - h.PatchIdentitiesItemRoles(w, request, "test-id") + patches := resources.IdentityEntitlementsPatchRequestBody{ + Patches: []resources.IdentityEntitlementsPatchItem{ + { + Entitlement: mockEntitlements.Data[0], + Op: "add", + }, + }, + } + mockRequest := newTestRequest(http.MethodPatch, fmt.Sprintf("/identities/%s/entitlements", mockIdentityId), &patches) + h.PatchIdentitiesItemEntitlements(w, mockRequest, mockIdentityId) }, + expectedStatus: http.StatusOK, }, } + for _, test := range tests { tt := test c.Run(tt.name, func(c *qt.C) { - mockErrorResponseMapper := NewMockErrorResponseMapper(ctrl) - mockErrorResponseMapper.EXPECT().MapError(gomock.Any()).Return(&mockErrorResponse) + mockIdentitiesService := interfaces.NewMockIdentitiesService(ctrl) + tt.setupServiceMock(mockIdentitiesService) - mockIdentityService := interfaces.NewMockIdentitiesService(ctrl) - tt.setupServiceMock(mockIdentityService) + sut := handler{Identities: mockIdentitiesService} mockWriter := httptest.NewRecorder() - sut := handler{ - Identities: mockIdentityService, - IdentitiesErrorMapper: mockErrorResponseMapper, - } - tt.triggerFunc(sut, mockWriter) result := mockWriter.Result() defer result.Body.Close() - c.Assert(result.StatusCode, qt.Equals, http.StatusInternalServerError) + c.Assert(result.StatusCode, qt.Equals, tt.expectedStatus) - data, err := io.ReadAll(result.Body) + body, err := io.ReadAll(result.Body) c.Assert(err, qt.IsNil) - response := new(resources.Response) - err = json.Unmarshal(data, response) - c.Assert(err, qt.IsNil, qt.Commentf("Unexpected err while unmarshaling response, got: %v", err)) - c.Assert(response.Status, qt.Equals, 500) - c.Assert(response.Message, qt.Equals, "mock-error") + + if tt.expectedBody != nil { + c.Assert(string(body), qt.JSONEquals, tt.expectedBody) + } else { + c.Assert(len(body), qt.Equals, 0) + } }) } }