Skip to content

Commit d81403d

Browse files
chore: Add test for health handler (#7)
1 parent 5eac14e commit d81403d

File tree

8 files changed

+136
-6
lines changed

8 files changed

+136
-6
lines changed

api/server.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func (s *Server) Start() {
3636
s.apiEcho.Use(middleware.CORSWithConfig(middleware.DefaultCORSConfig))
3737

3838
// init health routes
39-
s.apiEcho.POST("/health", s.healthHandlers.Health)
39+
s.apiEcho.GET("/health", s.healthHandlers.Health)
4040

4141
// init API routes
4242
groupV1 := s.apiEcho.Group("/v1")

dao/inmemory_mock_impl.go

+84
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
package dao
2+
3+
import (
4+
"context"
5+
"fmt"
6+
daoErr "github.com/go-feature-flag/app-api/dao/err"
7+
"github.com/go-feature-flag/app-api/model"
8+
_ "github.com/lib/pq" // we import the driver used by sqlx
9+
)
10+
11+
func NewInMemoryMockDao() (*InMemoryMockDao, error) {
12+
return &InMemoryMockDao{
13+
flags: []model.FeatureFlag{},
14+
}, nil
15+
}
16+
17+
type InMemoryMockDao struct {
18+
flags []model.FeatureFlag
19+
20+
errorOnPing bool
21+
}
22+
23+
// GetFlags return all the flags
24+
func (m *InMemoryMockDao) GetFlags(ctx context.Context) ([]model.FeatureFlag, daoErr.DaoError) {
25+
return m.flags, nil
26+
}
27+
28+
// GetFlagByID return a flag by its ID
29+
func (m *InMemoryMockDao) GetFlagByID(ctx context.Context, id string) (model.FeatureFlag, daoErr.DaoError) {
30+
for _, flag := range m.flags {
31+
if flag.ID == id {
32+
return flag, nil
33+
}
34+
}
35+
return model.FeatureFlag{}, daoErr.NewDaoError(daoErr.NotFound, fmt.Errorf("flag with id %s not found", id))
36+
}
37+
38+
// GetFlagByName return a flag by its name
39+
func (m *InMemoryMockDao) GetFlagByName(ctx context.Context, name string) (model.FeatureFlag, daoErr.DaoError) {
40+
for _, flag := range m.flags {
41+
if flag.Name == name {
42+
return flag, nil
43+
}
44+
}
45+
return model.FeatureFlag{}, daoErr.NewDaoError(daoErr.NotFound, fmt.Errorf("flag with name %s not found", name))
46+
}
47+
48+
// CreateFlag create a new flag, return the id of the flag
49+
func (m *InMemoryMockDao) CreateFlag(ctx context.Context, flag model.FeatureFlag) (string, daoErr.DaoError) {
50+
m.flags = append(m.flags, flag)
51+
return flag.ID, nil
52+
}
53+
54+
func (m *InMemoryMockDao) UpdateFlag(ctx context.Context, flag model.FeatureFlag) daoErr.DaoError {
55+
for index, f := range m.flags {
56+
if f.ID == flag.ID {
57+
m.flags[index] = flag
58+
return nil
59+
}
60+
}
61+
return daoErr.NewDaoError(daoErr.NotFound, fmt.Errorf("flag with id %s not found", flag.ID))
62+
}
63+
64+
func (m *InMemoryMockDao) DeleteFlagByID(ctx context.Context, id string) daoErr.DaoError {
65+
newInmemoryFlagList := []model.FeatureFlag{}
66+
for _, f := range m.flags {
67+
if f.ID != id {
68+
newInmemoryFlagList = append(newInmemoryFlagList, f)
69+
}
70+
}
71+
m.flags = newInmemoryFlagList
72+
return nil
73+
}
74+
75+
func (m *InMemoryMockDao) Ping() daoErr.DaoError {
76+
if m.errorOnPing {
77+
return daoErr.NewDaoError(daoErr.DatabaseNotInitialized, fmt.Errorf("error on ping"))
78+
}
79+
return nil
80+
}
81+
82+
func (m *InMemoryMockDao) OnPingReturnError(v bool) {
83+
m.errorOnPing = v
84+
}

docs/docs.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ const docTemplate = `{
2727
"basePath": "{{.BasePath}}",
2828
"paths": {
2929
"/health": {
30-
"post": {
30+
"get": {
3131
"description": "Check if the API is up and running and that the database is available.",
3232
"tags": [
3333
"Feature Monitoring"

docs/swagger.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@
1919
"basePath": "/",
2020
"paths": {
2121
"/health": {
22-
"post": {
22+
"get": {
2323
"description": "Check if the API is up and running and that the database is available.",
2424
"tags": [
2525
"Feature Monitoring"

docs/swagger.yaml

+1-1
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ info:
173173
url: https://raw.githubusercontent.com/thomaspoignant/go-feature-flag/main/logo_128.png
174174
paths:
175175
/health:
176-
post:
176+
get:
177177
description: Check if the API is up and running and that the database is available.
178178
responses:
179179
"200":

go.mod

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ require (
88
github.com/jmoiron/sqlx v1.4.0
99
github.com/labstack/echo/v4 v4.12.0
1010
github.com/lib/pq v1.10.9
11+
github.com/stretchr/testify v1.9.0
1112
github.com/swaggo/echo-swagger v1.4.1
1213
github.com/swaggo/swag v1.16.3
1314
)
@@ -32,7 +33,6 @@ require (
3233
github.com/mattn/go-isatty v0.0.20 // indirect
3334
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
3435
github.com/rogpeppe/go-internal v1.13.1 // indirect
35-
github.com/stretchr/testify v1.9.0 // indirect
3636
github.com/swaggo/files/v2 v2.0.0 // indirect
3737
github.com/valyala/bytebufferpool v1.0.0 // indirect
3838
github.com/valyala/fasttemplate v1.2.2 // indirect
@@ -43,4 +43,5 @@ require (
4343
golang.org/x/time v0.5.0 // indirect
4444
golang.org/x/tools v0.22.0 // indirect
4545
gopkg.in/yaml.v2 v2.4.0 // indirect
46+
gopkg.in/yaml.v3 v3.0.1 // indirect
4647
)

handler/health.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ type successResponse struct {
2828
// @Description Check if the API is up and running and that the database is available.
2929
// @Success 200 {object} successResponse "Created"
3030
// @Failure 500 {object} model.HTTPError "Internal server error"
31-
// @Router /health [post]
31+
// @Router /health [get]
3232
func (f Health) Health(c echo.Context) error {
3333
err := f.dao.Ping()
3434
if err != nil {

handler/health_test.go

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package handler
2+
3+
import (
4+
"github.com/go-feature-flag/app-api/dao"
5+
"github.com/labstack/echo/v4"
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
"net/http"
9+
"net/http/httptest"
10+
"testing"
11+
)
12+
13+
func TestHealthHandler_Health(t *testing.T) {
14+
e := echo.New()
15+
16+
t.Run("API is up and running", func(t *testing.T) {
17+
mockDao, err := dao.NewInMemoryMockDao()
18+
require.NoError(t, err)
19+
mockDao.OnPingReturnError(false)
20+
21+
h := NewHealth(mockDao)
22+
req := httptest.NewRequest(http.MethodGet, "/health", nil)
23+
rec := httptest.NewRecorder()
24+
c := e.NewContext(req, rec)
25+
26+
require.NoError(t, h.Health(c))
27+
assert.Equal(t, http.StatusOK, rec.Code)
28+
assert.JSONEq(t, `{"message":"API is up and running","code":200}`, rec.Body.String())
29+
})
30+
31+
t.Run("Database not available", func(t *testing.T) {
32+
mockDao, err := dao.NewInMemoryMockDao()
33+
require.NoError(t, err)
34+
mockDao.OnPingReturnError(true)
35+
36+
h := NewHealth(mockDao)
37+
req := httptest.NewRequest(http.MethodGet, "/health", nil)
38+
rec := httptest.NewRecorder()
39+
c := e.NewContext(req, rec)
40+
41+
require.NoError(t, h.Health(c))
42+
assert.Equal(t, http.StatusInternalServerError, rec.Code)
43+
assert.JSONEq(t, `{"errorDetails":"error on ping","code":500}`, rec.Body.String())
44+
})
45+
}

0 commit comments

Comments
 (0)