From e77d3c4d5e211c1fd6886eb7c9fbb15ff1b8058f Mon Sep 17 00:00:00 2001 From: Deepak Date: Thu, 31 Dec 2020 18:47:27 -0600 Subject: [PATCH] Adds health check and test --- routes/health.go | 58 +++++++++++++++++++++++++ routes/health_test.go | 87 ++++++++++++++++++++++++++++++++++++++ routes/plans_test.go | 2 +- routes/route_model_test.go | 4 +- routes/routes.go | 1 + 5 files changed, 149 insertions(+), 3 deletions(-) create mode 100644 routes/health.go create mode 100644 routes/health_test.go diff --git a/routes/health.go b/routes/health.go new file mode 100644 index 0000000..b03ddab --- /dev/null +++ b/routes/health.go @@ -0,0 +1,58 @@ +package routes + +import ( + "encoding/json" + "gitea.deepak.science/deepak/gogmagog/models" + "github.com/go-chi/chi" + "net/http" +) + +func newHealthRouter(m *models.Model) http.Handler { + router := chi.NewRouter() + router.Get("/", getHealthFunc(m)) + return router +} + +type healthCheck struct { + Name string `json:"name"` + Healthy bool `json:"healthy"` + Message string `json:"message"` +} + +func getHealthFunc(m *models.Model) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + var healths []*healthCheck + + healths = append(healths, dbHealth(m)) + + code := http.StatusOK + for _, h := range healths { + if !h.Healthy { + code = http.StatusInternalServerError + break + } + } + + w.WriteHeader(code) + w.Header().Add("Content-Type", "application/json") + if err := json.NewEncoder(w).Encode(healths); err != nil { + serverError(w, err) + } + } +} + +func dbHealth(m *models.Model) *healthCheck { + errMessage := "" + health := true + name := "Store" + err := m.Healthy() + if err != nil { + errMessage = err.Error() + health = false + } + return &healthCheck{ + Name: name, + Healthy: health, + Message: errMessage, + } +} diff --git a/routes/health_test.go b/routes/health_test.go new file mode 100644 index 0000000..92902b3 --- /dev/null +++ b/routes/health_test.go @@ -0,0 +1,87 @@ +package routes_test + +import ( + "fmt" + "gitea.deepak.science/deepak/gogmagog/routes" + "github.com/stretchr/testify/assert" + "net/http" + "net/http/httptest" + "testing" +) + +func TestEmptyHeatlhErrorWriter(t *testing.T) { + // set up + assert := assert.New(t) + + m := getEmptyModel() + + router := routes.NewRouter(m) + req, _ := http.NewRequest("GET", "/health", nil) + + rr := NewBadWriter() + + // function under test + router.ServeHTTP(rr, req) + + // check results + status := rr.Code + assert.Equal(http.StatusInternalServerError, status) + +} + +func TestEmptyHealth(t *testing.T) { + // set up + assert := assert.New(t) + m := getEmptyModel() + router := routes.NewRouter(m) + req, _ := http.NewRequest("GET", "/health", nil) + + rr := httptest.NewRecorder() + + // function under test + router.ServeHTTP(rr, req) + + // check results + status := rr.Code + assert.Equal(http.StatusOK, status) + expected := `[ + { + "name": "Store", + "healthy": true, + "message": "" + } + ]` + + assert.JSONEq(expected, rr.Body.String()) + contentType := rr.Header().Get("Content-Type") + assert.Equal("application/json", contentType) +} + +func TestUnhealthyDB(t *testing.T) { + // set up + assert := assert.New(t) + errorMsg := "error" + m := getErrorModel(errorMsg) + router := routes.NewRouter(m) + req, _ := http.NewRequest("GET", "/health", nil) + + rr := httptest.NewRecorder() + + // function under test + router.ServeHTTP(rr, req) + + // check results + status := rr.Code + assert.Equal(http.StatusInternalServerError, status) + expected := fmt.Sprintf(`[ + { + "name": "Store", + "healthy": false, + "message": "%s" + } + ]`, errorMsg) + + assert.JSONEq(expected, rr.Body.String()) + contentType := rr.Header().Get("Content-Type") + assert.Equal("application/json", contentType) +} diff --git a/routes/plans_test.go b/routes/plans_test.go index 62a30f6..5f0cc15 100644 --- a/routes/plans_test.go +++ b/routes/plans_test.go @@ -66,7 +66,7 @@ func TestErrorPlan(t *testing.T) { // set up assert := assert.New(t) - m := getErrorModel() + m := getErrorModel("Model always errors") router := routes.NewRouter(m) req, _ := http.NewRequest("GET", "/plans", nil) diff --git a/routes/route_model_test.go b/routes/route_model_test.go index 3742924..6146e2c 100644 --- a/routes/route_model_test.go +++ b/routes/route_model_test.go @@ -87,7 +87,7 @@ type errorStore struct { error error } -func getErrorModel() *models.Model { - e := &errorStore{error: fmt.Errorf("Model always errors")} +func getErrorModel(errorMsg string) *models.Model { + e := &errorStore{error: fmt.Errorf(errorMsg)} return models.New(e) } diff --git a/routes/routes.go b/routes/routes.go index e2a9f1e..15ee2d4 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -13,6 +13,7 @@ func NewRouter(m *models.Model) http.Handler { router.MethodNotAllowed(methodNotAllowedHandler) router.NotFound(notFoundHandler) router.Mount("/plans", newPlanRouter(m)) + router.Mount("/health", newHealthRouter(m)) router.Get("/ping", ping) return router }