Added plan by id route and error handling and organisation
This commit is contained in:
2
do.sh
2
do.sh
@@ -12,7 +12,7 @@ build() {
|
||||
|
||||
test() {
|
||||
echo "I am ${FUNCNAME[0]}ing"
|
||||
_lint && _vet && _test
|
||||
fmt && _lint && _vet && _test
|
||||
}
|
||||
|
||||
run() {
|
||||
|
||||
@@ -10,3 +10,12 @@ func serverError(w http.ResponseWriter, err error) {
|
||||
log.Printf("received error: {%v}", err)
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
|
||||
func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
|
||||
code := http.StatusMethodNotAllowed
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||
code := http.StatusNotFound
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
|
||||
@@ -5,11 +5,13 @@ import (
|
||||
"gitea.deepak.science/deepak/gogmagog/models"
|
||||
"github.com/go-chi/chi"
|
||||
"net/http"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func newPlanRouter(m *models.Model) http.Handler {
|
||||
router := chi.NewRouter()
|
||||
router.Get("/", getAllPlansFunc(m))
|
||||
router.Get("/{planid}", getPlanByIDFunc(m))
|
||||
return router
|
||||
}
|
||||
|
||||
@@ -26,3 +28,27 @@ func getAllPlansFunc(m *models.Model) http.HandlerFunc {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getPlanByIDFunc(m *models.Model) http.HandlerFunc {
|
||||
return func(w http.ResponseWriter, r *http.Request) {
|
||||
id, err := strconv.Atoi(chi.URLParam(r, "planid"))
|
||||
if err != nil {
|
||||
notFoundHandler(w, r)
|
||||
return
|
||||
}
|
||||
plan, err := m.Plan(id)
|
||||
if err != nil {
|
||||
if models.IsNotFoundError(err) {
|
||||
notFoundHandler(w, r)
|
||||
return
|
||||
}
|
||||
serverError(w, err)
|
||||
return
|
||||
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if err := json.NewEncoder(w).Encode(plan); err != nil {
|
||||
serverError(w, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,3 +103,113 @@ func TestEmptyPlanErrorWriter(t *testing.T) {
|
||||
assert.Equal(http.StatusInternalServerError, status)
|
||||
|
||||
}
|
||||
|
||||
func TestOnePlanByID(t *testing.T) {
|
||||
// set up
|
||||
assert := assert.New(t)
|
||||
planDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||
p := &models.Plan{PlanID: 6, PlanDate: planDate}
|
||||
m := getModel([]*models.Plan{p}, []*models.Action{})
|
||||
router := routes.NewRouter(m)
|
||||
req, _ := http.NewRequest("GET", "/plans/6", nil)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// function under test
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
// check results
|
||||
status := rr.Code
|
||||
assert.Equal(http.StatusOK, status)
|
||||
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||
expected := `{
|
||||
"plan_id": 6,
|
||||
"plan_date": "2021-01-01T00:00:00Z"
|
||||
}`
|
||||
assert.JSONEq(expected, rr.Body.String())
|
||||
contentType := rr.Header().Get("Content-Type")
|
||||
assert.Equal("application/json", contentType)
|
||||
}
|
||||
|
||||
func TestErrorPlanByID(t *testing.T) {
|
||||
// set up
|
||||
assert := assert.New(t)
|
||||
|
||||
m := getErrorModel("Model always errors")
|
||||
|
||||
router := routes.NewRouter(m)
|
||||
req, _ := http.NewRequest("GET", "/plans/5", nil)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// function under test
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
// check results
|
||||
status := rr.Code
|
||||
assert.Equal(http.StatusInternalServerError, status)
|
||||
// We pass in the date as a time.time so it makes sense that it comes back with a midnight timestamp.
|
||||
expected := `Internal Server Error`
|
||||
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
|
||||
}
|
||||
|
||||
func TestEmptyPlanErrorWriterByID(t *testing.T) {
|
||||
// set up
|
||||
assert := assert.New(t)
|
||||
planDate, _ := time.Parse("2006-01-02", "2021-01-01")
|
||||
|
||||
p := &models.Plan{PlanID: 6, PlanDate: planDate}
|
||||
m := getModel([]*models.Plan{p}, []*models.Action{})
|
||||
|
||||
router := routes.NewRouter(m)
|
||||
req, _ := http.NewRequest("GET", "/plans/6", nil)
|
||||
|
||||
rr := NewBadWriter()
|
||||
|
||||
// function under test
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
// check results
|
||||
status := rr.Code
|
||||
assert.Equal(http.StatusInternalServerError, status)
|
||||
|
||||
}
|
||||
|
||||
func TestNotFoundPlanByIDText(t *testing.T) {
|
||||
// set up
|
||||
assert := assert.New(t)
|
||||
|
||||
m := getEmptyModel()
|
||||
|
||||
router := routes.NewRouter(m)
|
||||
req, _ := http.NewRequest("GET", "/plans/wo", nil)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// function under test
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
// check results
|
||||
status := rr.Code
|
||||
assert.Equal(http.StatusNotFound, status)
|
||||
|
||||
}
|
||||
func TestNotFoundPlanByIDEmpty(t *testing.T) {
|
||||
// set up
|
||||
assert := assert.New(t)
|
||||
|
||||
m := getEmptyModel()
|
||||
|
||||
router := routes.NewRouter(m)
|
||||
req, _ := http.NewRequest("GET", "/plans/1", nil)
|
||||
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// function under test
|
||||
router.ServeHTTP(rr, req)
|
||||
|
||||
// check results
|
||||
status := rr.Code
|
||||
assert.Equal(http.StatusNotFound, status)
|
||||
|
||||
}
|
||||
|
||||
@@ -10,11 +10,23 @@ type multiStore struct {
|
||||
plans []*models.Plan
|
||||
}
|
||||
|
||||
type testNotFoundError struct {
|
||||
error
|
||||
}
|
||||
|
||||
func (t *testNotFoundError) NotFound() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (ms *multiStore) SelectActions() ([]*models.Action, error) {
|
||||
return ms.actions, nil
|
||||
}
|
||||
|
||||
func (ms *multiStore) SelectActionByID(id int) (*models.Action, error) {
|
||||
if len(ms.actions) < 1 {
|
||||
err := &testNotFoundError{fmt.Errorf("too small")}
|
||||
return nil, err
|
||||
}
|
||||
return ms.actions[0], nil
|
||||
}
|
||||
|
||||
@@ -23,6 +35,10 @@ func (ms *multiStore) SelectPlans() ([]*models.Plan, error) {
|
||||
}
|
||||
|
||||
func (ms *multiStore) SelectPlanByID(id int) (*models.Plan, error) {
|
||||
if len(ms.plans) < 1 {
|
||||
err := &testNotFoundError{fmt.Errorf("too small")}
|
||||
return nil, err
|
||||
}
|
||||
return ms.plans[0], nil
|
||||
}
|
||||
|
||||
|
||||
@@ -17,14 +17,6 @@ func NewRouter(m *models.Model) http.Handler {
|
||||
router.Get("/ping", ping)
|
||||
return router
|
||||
}
|
||||
func methodNotAllowedHandler(w http.ResponseWriter, r *http.Request) {
|
||||
code := http.StatusMethodNotAllowed
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
func notFoundHandler(w http.ResponseWriter, r *http.Request) {
|
||||
code := http.StatusNotFound
|
||||
http.Error(w, http.StatusText(code), code)
|
||||
}
|
||||
|
||||
func ping(w http.ResponseWriter, r *http.Request) {
|
||||
// A very simple health check.
|
||||
|
||||
Reference in New Issue
Block a user