From 59e664edf0da10516138711249c6d6a9ef0d4134 Mon Sep 17 00:00:00 2001 From: vipul-rawat Date: Fri, 13 Dec 2024 13:38:41 +0530 Subject: [PATCH 1/3] add check for cloud account name before printing --- cloud/handler/handler.go | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/cloud/handler/handler.go b/cloud/handler/handler.go index 633e5e4..07e7f87 100644 --- a/cloud/handler/handler.go +++ b/cloud/handler/handler.go @@ -33,6 +33,7 @@ func (h *Handler) Import(ctx *gofr.Context) (any, error) { return successMessage, nil } +// List is a handler for listing all cloud accounts. func (h *Handler) List(ctx *gofr.Context) (any, error) { accounts, err := h.accountGetter.GetAccounts(ctx) if err != nil { @@ -48,8 +49,12 @@ func (h *Handler) List(ctx *gofr.Context) (any, error) { rows := "" for _, account := range accounts { + if len(account.Name) > 20 { + account.Name = account.Name[:17] + "..." + } + rows += fmt.Sprintf("%-20s %-20s %-20s %-20s %-20s\n", - account.Name[:17]+"...", account.Provider, account.ProviderID, account.UpdatedAt, account.CreatedAt) + account.Name, account.Provider, account.ProviderID, account.UpdatedAt, account.CreatedAt) } return header + rows, nil From c1bf03dd1495d3b358852f863f6aeb3125c2eac2 Mon Sep 17 00:00:00 2001 From: vipul-rawat Date: Fri, 13 Dec 2024 13:56:12 +0530 Subject: [PATCH 2/3] add test for List handler --- cloud/handler/handler.go | 7 ++- cloud/handler/handler_test.go | 91 ++++++++++++++++++++++++++++++++- cloud/handler/mock_interface.go | 6 ++- 3 files changed, 99 insertions(+), 5 deletions(-) diff --git a/cloud/handler/handler.go b/cloud/handler/handler.go index 07e7f87..b8654a2 100644 --- a/cloud/handler/handler.go +++ b/cloud/handler/handler.go @@ -9,7 +9,10 @@ import ( "gofr.dev/pkg/gofr" ) -const successMessage = "Successfully Imported!" +const ( + successMessage = "Successfully Imported!" + maxNameLength = 20 +) type Handler struct { accountService AccountImporter @@ -49,7 +52,7 @@ func (h *Handler) List(ctx *gofr.Context) (any, error) { rows := "" for _, account := range accounts { - if len(account.Name) > 20 { + if len(account.Name) > maxNameLength { account.Name = account.Name[:17] + "..." } diff --git a/cloud/handler/handler_test.go b/cloud/handler/handler_test.go index 206fc5e..25e40fb 100644 --- a/cloud/handler/handler_test.go +++ b/cloud/handler/handler_test.go @@ -4,14 +4,18 @@ import ( "errors" "testing" + "github.com/stretchr/testify/assert" "go.uber.org/mock/gomock" "gofr.dev/pkg/gofr" "gofr.dev/pkg/gofr/container" "gofr.dev/pkg/gofr/logging" + + "zop.dev/cli/zop/cloud/service/list" ) var ( - errTest = errors.New("import error") + errTest = errors.New("import error") + errFailedToFetchAccounts = errors.New("failed to fetch accounts") ) func TestImport_Success(t *testing.T) { @@ -58,3 +62,88 @@ func TestImport_Failure(t *testing.T) { t.Errorf("expected 'import error', got %v", err.Error()) } } + +func TestHandler_List(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockAccGetter := NewMockAccountGetter(ctrl) + + tests := []struct { + name string + accounts []*list.CloudAccountResponse + expectedResp string + expectedErr error + mocks []*gomock.Call + }{ + { + name: "No accounts", + accounts: []*list.CloudAccountResponse{}, + expectedResp: "No accounts found", + expectedErr: nil, + mocks: []*gomock.Call{ + mockAccGetter.EXPECT().GetAccounts(gomock.Any()).Return([]*list.CloudAccountResponse{}, nil), + }, + }, + { + name: "Single account", + accounts: []*list.CloudAccountResponse{ + {Name: "Account1", Provider: "AWS", ProviderID: "12345", UpdatedAt: "2024-01-01", CreatedAt: "2023-01-01"}, + }, + expectedResp: "Name Provider ProviderID UpdateAt CreatedAt \n" + + "Account1 AWS 12345 2024-01-01 2023-01-01 \n", + expectedErr: nil, + mocks: []*gomock.Call{ + mockAccGetter.EXPECT().GetAccounts(gomock.Any()).Return([]*list.CloudAccountResponse{ + {Name: "Account1", Provider: "AWS", ProviderID: "12345", UpdatedAt: "2024-01-01", CreatedAt: "2023-01-01"}, + }, nil), + }, + }, + { + name: "Multiple accounts with truncation", + accounts: []*list.CloudAccountResponse{ + {Name: "ThisIsAVeryLongAccountNameThatShouldBeTruncated", + Provider: "GCP", ProviderID: "67890", UpdatedAt: "2024-02-02", CreatedAt: "2023-02-02"}, + {Name: "Account2", Provider: "Azure", + ProviderID: "11111", UpdatedAt: "2024-03-03", CreatedAt: "2023-03-03"}, + }, + expectedResp: "Name Provider ProviderID UpdateAt CreatedAt \n" + + "ThisIsAVeryLongAc... GCP 67890 2024-02-02 2023-02-02 \n" + + "Account2 Azure 11111 2024-03-03 2023-03-03 \n", + expectedErr: nil, + mocks: []*gomock.Call{ + mockAccGetter.EXPECT().GetAccounts(gomock.Any()).Return([]*list.CloudAccountResponse{ + {Name: "ThisIsAVeryLongAccountNameThatShouldBeTruncated", + Provider: "GCP", ProviderID: "67890", UpdatedAt: "2024-02-02", CreatedAt: "2023-02-02"}, + {Name: "Account2", Provider: "Azure", ProviderID: "11111", UpdatedAt: "2024-03-03", CreatedAt: "2023-03-03"}, + }, nil), + }, + }, + { + name: "Error from GetAccounts", + accounts: nil, + expectedResp: "", + expectedErr: errFailedToFetchAccounts, + mocks: []*gomock.Call{ + mockAccGetter.EXPECT().GetAccounts(gomock.Any()).Return(nil, errFailedToFetchAccounts), + }, + }, + } + + handler := New(nil, mockAccGetter) + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + ctx := &gofr.Context{} + + resp, err := handler.List(ctx) + + if tt.expectedErr != nil { + assert.EqualError(t, err, tt.expectedErr.Error()) + } else { + assert.NoError(t, err) + assert.Equal(t, tt.expectedResp, resp) + } + }) + } +} diff --git a/cloud/handler/mock_interface.go b/cloud/handler/mock_interface.go index 4305108..0a59bd5 100644 --- a/cloud/handler/mock_interface.go +++ b/cloud/handler/mock_interface.go @@ -3,8 +3,10 @@ // // Generated by this command: // +// mockgen -source=interface.go -destination=mock_interface.go -package=handler // +// Package handler is a generated GoMock package. package handler import ( @@ -78,10 +80,10 @@ func (m *MockAccountGetter) EXPECT() *MockAccountGetterMockRecorder { } // GetAccounts mocks base method. -func (m *MockAccountGetter) GetAccounts(ctx *gofr.Context) ([]list.CloudAccountResponse, error) { +func (m *MockAccountGetter) GetAccounts(ctx *gofr.Context) ([]*list.CloudAccountResponse, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "GetAccounts", ctx) - ret0, _ := ret[0].([]list.CloudAccountResponse) + ret0, _ := ret[0].([]*list.CloudAccountResponse) ret1, _ := ret[1].(error) return ret0, ret1 } From b407d7815e602b51c2bcd3b56d084968e23e703c Mon Sep 17 00:00:00 2001 From: vipul-rawat Date: Fri, 13 Dec 2024 14:01:42 +0530 Subject: [PATCH 3/3] add list service tests --- cloud/service/list/service_test.go | 90 ++++++++++++++++++++++++++++++ 1 file changed, 90 insertions(+) create mode 100644 cloud/service/list/service_test.go diff --git a/cloud/service/list/service_test.go b/cloud/service/list/service_test.go new file mode 100644 index 0000000..0e5fdca --- /dev/null +++ b/cloud/service/list/service_test.go @@ -0,0 +1,90 @@ +package list + +import ( + "bytes" + "errors" + "io" + "net/http" + "testing" + + "github.com/stretchr/testify/require" + "go.uber.org/mock/gomock" + "gofr.dev/pkg/gofr" + "gofr.dev/pkg/gofr/cmd/terminal" + "gofr.dev/pkg/gofr/container" + "gofr.dev/pkg/gofr/service" +) + +var ( + errFailFetch = errors.New("failed to fetch") + errUnmarshall = errors.New("invalid character 'i' looking for beginning of value") +) + +func Test_Service_GetAccounts(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + + mockCont, mocks := container.NewMockContainer(t, func(_ *container.Container, ctrl *gomock.Controller) any { + return service.NewMockHTTP(ctrl) + }) + mockCont.Services["api-service"] = mocks.HTTPService + ctx := &gofr.Context{Container: mockCont, Out: terminal.New()} + + testCases := []struct { + name string + mockCalls []*gomock.Call + expResult []*CloudAccountResponse + expError error + }{ + { + name: "successful GET call", + mockCalls: []*gomock.Call{ + mocks.HTTPService.EXPECT().Get(ctx, "/cloud-accounts", nil). + Return(&http.Response{ + Body: io.NopCloser(bytes.NewBufferString( + `{"data": [{"Name": "Account1", "Provider": "AWS", "ProviderID": "12345", "UpdatedAt": "2024-01-01", "CreatedAt": "2023-01-01"}]}`)), + }, nil), + }, + expResult: []*CloudAccountResponse{ + {Name: "Account1", Provider: "AWS", ProviderID: "12345", UpdatedAt: "2024-01-01", CreatedAt: "2023-01-01"}, + }, + expError: nil, + }, + { + name: "error during GET call", + mockCalls: []*gomock.Call{ + mocks.HTTPService.EXPECT().Get(ctx, "/cloud-accounts", nil). + Return(nil, errFailFetch), + }, + expResult: nil, + expError: errFailFetch, + }, + { + name: "invalid JSON response", + mockCalls: []*gomock.Call{ + mocks.HTTPService.EXPECT().Get(ctx, "/cloud-accounts", nil). + Return(&http.Response{ + Body: io.NopCloser(bytes.NewBufferString("invalid-json")), + }, nil), + }, + expResult: nil, + expError: errUnmarshall, + }, + } + + svc := New() + + for _, tt := range testCases { + t.Run(tt.name, func(t *testing.T) { + result, err := svc.GetAccounts(ctx) + + if tt.expError != nil { + require.EqualError(t, err, tt.expError.Error()) + require.Nil(t, result) + } else { + require.NoError(t, err) + require.Equal(t, tt.expResult, result) + } + }) + } +}