Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions application/handler/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package handler

import (
"errors"

"gofr.dev/pkg/gofr"
)

var (
ErrorApplicationNameNotProvided = errors.New("please enter application name, -name=<application_name>")
)

type Handler struct {
appAdd ApplicationAdder
}

func New(appAdd ApplicationAdder) *Handler {
return &Handler{
appAdd: appAdd,
}
}

func (h *Handler) Add(ctx *gofr.Context) (any, error) {
name := ctx.Param("name")
if name == "" {
return nil, ErrorApplicationNameNotProvided
}

err := h.appAdd.AddApplication(ctx, name)
if err != nil {
return nil, err
}

return "Application " + name + " added successfully!", nil
}
70 changes: 70 additions & 0 deletions application/handler/handler_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package handler

import (
"errors"
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/mock/gomock"
"gofr.dev/pkg/gofr"
"gofr.dev/pkg/gofr/cmd"
"gofr.dev/pkg/gofr/container"
)

var errAPICall = errors.New("error in API call")

func TestHandler_Add(t *testing.T) {
ctrl := gomock.NewController(t)
defer ctrl.Finish()

mockAppAdder := NewMockApplicationAdder(ctrl)

testCases := []struct {
name string
appName string
mockCalls []*gomock.Call
expected any
expErr error
}{
{
name: "success",
appName: "test-app",
mockCalls: []*gomock.Call{
mockAppAdder.EXPECT().AddApplication(gomock.Any(), "test-app").Return(nil),
},
expected: "Application test-app added successfully!",
expErr: nil,
},
{
name: "missing name parameter",
appName: "",
expected: nil,
expErr: ErrorApplicationNameNotProvided,
},
{
name: "error adding application",
appName: "test-app",
mockCalls: []*gomock.Call{
mockAppAdder.EXPECT().AddApplication(gomock.Any(), "test-app").Return(errAPICall),
},
expected: nil,
expErr: errAPICall,
},
}

for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mockCont, _ := container.NewMockContainer(t)
ctx := &gofr.Context{
Container: mockCont,
Request: cmd.NewRequest([]string{"", "-name=" + tc.appName}),
}

h := New(mockAppAdder)
res, err := h.Add(ctx)

require.Equal(t, tc.expErr, err)
require.Equal(t, tc.expected, res)
})
}
}
7 changes: 7 additions & 0 deletions application/handler/interface.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package handler

import "gofr.dev/pkg/gofr"

type ApplicationAdder interface {
AddApplication(ctx *gofr.Context, name string) error
}
55 changes: 55 additions & 0 deletions application/handler/mock_interface.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

67 changes: 67 additions & 0 deletions application/service/application.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package service

import (
"encoding/json"
"fmt"
"net/http"

"gofr.dev/pkg/gofr"
)

type Service struct {
}

func New() *Service {
return &Service{}
}

func (*Service) AddApplication(ctx *gofr.Context, name string) error {
var (
envs []Environment
input string
)

app := &Application{Name: name}
api := ctx.GetHTTPService("api-service")
order := 1

ctx.Out.Print("Do you wish to add environments to the application? (y/n) ")

_, _ = fmt.Scanf("%s", &input)

for {
if input != "y" {
break
}

ctx.Out.Print("Enter environment name: ")

_, _ = fmt.Scanf("%s", &input)
envs = append(envs, Environment{Name: input, Order: order})
order++

ctx.Out.Print("Do you wish to add more? (y/n) ")

_, _ = fmt.Scanf("%s", &input)

if input == "n" {
break
}
}

app.Envs = envs
body, _ := json.Marshal(app)

resp, err := api.PostWithHeaders(ctx, "application", nil, body, nil)
if err != nil {
return err
}

defer resp.Body.Close()

if resp.StatusCode != http.StatusCreated {
return getAPIError(resp)
}

return nil
}
154 changes: 154 additions & 0 deletions application/service/application_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
package service

import (
"bytes"
"encoding/json"
"errors"
"io"
"net/http"
"os"
"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 errAPICall = errors.New("error in API call")

func Test_AddApplication(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()}

b, err := json.Marshal(MockErrorResponse{Error: "Something went wrong"})
if err != nil {
t.Fatalf("Failed to marshal test response body: %v", err)
}

testCases := []struct {
name string
mockCalls []*gomock.Call
expError error
}{
{
name: "success Post call",
mockCalls: []*gomock.Call{
mocks.HTTPService.EXPECT().PostWithHeaders(ctx, "application", nil, gomock.Any(), nil).
Return(&http.Response{StatusCode: http.StatusCreated, Body: io.NopCloser(&errorReader{})}, nil),
},
expError: nil,
},
{
name: "error in Post call",
mockCalls: []*gomock.Call{
mocks.HTTPService.EXPECT().PostWithHeaders(ctx, "application", nil, gomock.Any(), nil).
Return(nil, errAPICall),
},
expError: errAPICall,
},
{
name: "unexpected response",
mockCalls: []*gomock.Call{
mocks.HTTPService.EXPECT().PostWithHeaders(ctx, "application", nil, gomock.Any(), nil).
Return(&http.Response{StatusCode: http.StatusInternalServerError, Body: io.NopCloser(bytes.NewBuffer(b))}, nil),
},
expError: &ErrAPIService{StatusCode: http.StatusInternalServerError, Message: "Something went wrong"},
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
s := New()

errSvc := s.AddApplication(ctx, "test")

require.Equal(t, tt.expError, errSvc)
})
}
}

func Test_AddApplication_WithEnvs(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
userInput string
expectedEnvs []Environment
expError error
}{
{
name: "success with environments",
userInput: "y\nprod\ny\ndev\nn\n",
expectedEnvs: []Environment{
{Name: "prod", Order: 1},
{Name: "dev", Order: 2},
},
mockCalls: []*gomock.Call{
mocks.HTTPService.EXPECT().PostWithHeaders(ctx, "application", nil, gomock.Any(), nil).
DoAndReturn(func(_ *gofr.Context, _ string, _, body, _ interface{}) (*http.Response, error) {
var app Application
_ = json.Unmarshal(body.([]byte), &app)
require.Equal(t, "test", app.Name)
require.Equal(t, []Environment{
{Name: "prod", Order: 1},
{Name: "dev", Order: 2},
}, app.Envs)
return &http.Response{StatusCode: http.StatusCreated, Body: io.NopCloser(bytes.NewBuffer(nil))}, nil
}),
},
expError: nil,
},
{
name: "no environments added",
userInput: "n\n",
expectedEnvs: []Environment{},
mockCalls: []*gomock.Call{
mocks.HTTPService.EXPECT().PostWithHeaders(ctx, "application", nil, gomock.Any(), nil).
DoAndReturn(func(_ *gofr.Context, _ string, _, body, _ interface{}) (*http.Response, error) {
var app Application
_ = json.Unmarshal(body.([]byte), &app)
require.Equal(t, "test", app.Name)
require.Empty(t, app.Envs)
return &http.Response{StatusCode: http.StatusCreated, Body: io.NopCloser(bytes.NewBuffer(nil))}, nil
}),
},
expError: nil,
},
}

for _, tt := range testCases {
t.Run(tt.name, func(t *testing.T) {
s := New()

// Mock user input
r, w, _ := os.Pipe()
_, _ = w.WriteString(tt.userInput)

oldStdin := os.Stdin
os.Stdin = r

defer func() { os.Stdin = oldStdin }()

errSvc := s.AddApplication(ctx, "test")
require.Equal(t, tt.expError, errSvc)
})
}
}
Loading
Loading