Compare commits

...

8 Commits

Author SHA1 Message Date
b47abbfc0b Adds put route tests for current plans
All checks were successful
gitea-deepak/gogmagog/pipeline/head This commit looks good
2021-01-31 14:29:53 -06:00
4905d18222 Adds tests for post current plan 2021-01-31 14:24:05 -06:00
eb8838ab75 Add store/postgres tests for current plan 2021-01-31 13:57:54 -06:00
6ad0112683 Adds tests for inmemory store 2021-01-31 12:26:39 -06:00
b9cea2347c Adds error store tests 2021-01-31 12:18:58 -06:00
e420bf303a Adds tests for model code 2021-01-31 12:15:30 -06:00
92ddd9e0fe Current plan beats primary plan 2021-01-31 11:54:41 -06:00
8c17aa9d6d Adds current plan implementation 2021-01-31 11:42:47 -06:00
16 changed files with 1231 additions and 4 deletions

23
models/current_plan.go Normal file
View File

@@ -0,0 +1,23 @@
package models
// CurrentPlan represents the primary plan ID for a particular user ID.
type CurrentPlan struct {
UserID int64 `json:"user_id"`
PlanID int64 `json:"plan_id"`
}
// CurrentPlan returns the primary plan for a provided user ID in the given model.
func (m *Model) CurrentPlan(userID int) (*CurrentPlan, error) {
pp, err := m.SelectCurrentPlan(userID)
return pp, wrapNotFound(err)
}
// AddCurrentPlan inserts a given primary plan into the store, returning nothing.
func (m *Model) AddCurrentPlan(pp *CurrentPlan, userID int) error {
return m.InsertCurrentPlan(pp, userID)
}
// SaveCurrentPlan saves and updates a primary plan.
func (m *Model) SaveCurrentPlan(pp *CurrentPlan, userID int) error {
return m.UpdateCurrentPlan(pp, userID)
}

View File

@@ -0,0 +1,45 @@
package models_test
import (
"gitea.deepak.science/deepak/gogmagog/models"
"gitea.deepak.science/deepak/gogmagog/store"
"github.com/stretchr/testify/assert"
"testing"
)
func TestModelCurrentPlan(t *testing.T) {
assert := assert.New(t)
a1 := &models.Action{ActionID: 3}
userID := 3
p := &models.Plan{PlanID: 6}
str, _ := store.GetInMemoryStore()
str.InsertAction(a1, userID)
str.InsertPlan(p, userID)
str.InsertPlan(p, userID)
m := models.New(str)
_, err := m.CurrentPlan(userID)
assert.NotNil(err)
assert.True(models.IsNotFoundError(err))
err = m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, userID)
assert.Nil(err)
pp, err := m.CurrentPlan(userID)
assert.Nil(err)
assert.EqualValues(1, pp.PlanID)
assert.EqualValues(userID, pp.UserID)
err = m.AddCurrentPlan(&models.CurrentPlan{PlanID: 2}, userID)
assert.NotNil(err)
err = m.SaveCurrentPlan(&models.CurrentPlan{PlanID: 2}, userID)
assert.Nil(err)
pp, err = m.CurrentPlan(userID)
assert.Nil(err)
assert.EqualValues(2, pp.PlanID)
assert.EqualValues(userID, pp.UserID)
}

View File

@@ -17,6 +17,9 @@ type Store interface {
SelectActionsByPlanID(plan *Plan, userID int) ([]*Action, error)
SelectUserByUsername(username string) (*User, error)
InsertUser(user *User) (int, error)
SelectCurrentPlan(userID int) (*CurrentPlan, error)
InsertCurrentPlan(currentPlan *CurrentPlan, userID int) error
UpdateCurrentPlan(currentPlan *CurrentPlan, userID int) error
}
// Model represents a current model item.

160
routes/current_plan.go Normal file
View File

@@ -0,0 +1,160 @@
package routes
import (
"encoding/json"
"gitea.deepak.science/deepak/gogmagog/models"
"gitea.deepak.science/deepak/gogmagog/tokens"
"github.com/go-chi/chi"
"io"
"net/http"
)
// NewCurrentPlanRouter returns a new primary plan router
func NewCurrentPlanRouter(m *models.Model) http.Handler {
router := chi.NewRouter()
router.Get("/", getCurrentPlanFunc(m))
router.Post("/", postCurrentPlanFunc(m))
router.Put("/", putCurrentPlanFunc(m))
return router
}
func getCurrentPlanFunc(m *models.Model) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, userErr := tokens.GetUserID(r.Context())
if userErr != nil {
unauthorizedHandler(w, r)
return
}
pp, err := m.CurrentPlan(userID)
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(pp); err != nil {
serverError(w, err)
}
}
}
type createCurrentPlanResponse struct {
CreatedCurrentPlan *models.CurrentPlan `json:"created_current_plan"`
}
func postCurrentPlanFunc(m *models.Model) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, err := tokens.GetUserID(r.Context())
if err != nil {
unauthorizedHandler(w, r)
return
}
r.Body = http.MaxBytesReader(w, r.Body, 1024)
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
var pp models.CurrentPlan
err = dec.Decode(&pp)
if err != nil {
badRequestError(w, err)
return
}
err = dec.Decode(&struct{}{})
if err != io.EOF {
badRequestError(w, err)
return
}
newPP := &models.CurrentPlan{
PlanID: pp.PlanID,
UserID: int64(userID),
}
err = m.AddCurrentPlan(newPP, userID)
if err != nil {
serverError(w, err)
return
}
finishedPP, err := m.CurrentPlan(userID)
if err != nil {
serverError(w, err)
return
}
response := &createCurrentPlanResponse{
CreatedCurrentPlan: finishedPP,
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusCreated)
if err := json.NewEncoder(w).Encode(response); err != nil {
serverError(w, err)
}
}
}
type updateCurrentPlanResponse struct {
UpdatedCurrentPlan *models.CurrentPlan `json:"updated_current_plan"`
}
func putCurrentPlanFunc(m *models.Model) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
userID, err := tokens.GetUserID(r.Context())
if err != nil {
unauthorizedHandler(w, r)
return
}
_, err = m.CurrentPlan(userID)
if models.IsNotFoundError(err) {
notFoundHandler(w, r)
return
}
r.Body = http.MaxBytesReader(w, r.Body, 1024)
dec := json.NewDecoder(r.Body)
dec.DisallowUnknownFields()
var pp models.CurrentPlan
err = dec.Decode(&pp)
if err != nil {
badRequestError(w, err)
return
}
err = dec.Decode(&struct{}{})
if err != io.EOF {
badRequestError(w, err)
return
}
newPP := &models.CurrentPlan{
PlanID: pp.PlanID,
UserID: int64(userID),
}
err = m.SaveCurrentPlan(newPP, userID)
if err != nil {
serverError(w, err)
return
}
newPP, err = m.CurrentPlan(userID)
if err != nil {
serverError(w, err)
return
}
response := &updateCurrentPlanResponse{
UpdatedCurrentPlan: newPP,
}
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
if err := json.NewEncoder(w).Encode(response); err != nil {
serverError(w, err)
}
}
}

View File

@@ -0,0 +1,228 @@
package routes_test
import (
"bytes"
"context"
"encoding/json"
"gitea.deepak.science/deepak/gogmagog/models"
"gitea.deepak.science/deepak/gogmagog/routes"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestPostSinglePlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
router := routes.NewCurrentPlanRouter(m)
plan := &models.Plan{}
m.AddPlan(plan, 3)
pp := &models.CurrentPlan{PlanID: 1}
data, _ := json.Marshal(pp)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusCreated, status)
}
func TestPostCurrentPlanUnauth(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
pp := &models.CurrentPlan{PlanID: 1}
data, _ := json.Marshal(pp)
req, _ := http.NewRequestWithContext(context.Background(), "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusUnauthorized, status)
}
func TestExtraFieldJSONCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(`{
"plan_id": 5,
"sabotage": "omg"
}`)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestEmptyBodyJSONCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(``)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestTwoBodyCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(`{
"plan_id": 5
}, {
"plan_id": 7
}`)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorCreateCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getErrorModel("error model")
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
expected := `Internal Server Error`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorOnRetrieveCreateCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getErrorOnGetModel("error model")
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
expected := `Internal Server Error`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorWriterCreateCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "POST", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := NewBadWriter()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
}

View File

@@ -0,0 +1,269 @@
package routes_test
import (
"bytes"
"context"
"encoding/json"
"gitea.deepak.science/deepak/gogmagog/models"
"gitea.deepak.science/deepak/gogmagog/routes"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestPutSinglePlanNotFound(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
router := routes.NewCurrentPlanRouter(m)
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
pp := &models.CurrentPlan{PlanID: 1}
data, _ := json.Marshal(pp)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusNotFound, status)
}
func TestPutSinglePlanNonDup(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
router := routes.NewCurrentPlanRouter(m)
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
pp := &models.CurrentPlan{PlanID: 2}
data, _ := json.Marshal(pp)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusOK, status)
}
func TestPutCurrentPlanUnauth(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
pp := &models.CurrentPlan{PlanID: 1}
data, _ := json.Marshal(pp)
req, _ := http.NewRequestWithContext(context.Background(), "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusUnauthorized, status)
}
func TestExtraFieldJSONPutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(`{
"plan_id": 5,
"sabotage": "omg"
}`)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestEmptyBodyJSONPutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(``)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestTwoBodyPutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(`{
"plan_id": 5
}, {
"plan_id": 7
}`)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusBadRequest, status)
expected := `Bad Request`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorPutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getErrorModel("error model")
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
expected := `Internal Server Error`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorOnRetrievePutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getErrorOnGetModel("error model")
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := httptest.NewRecorder()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
expected := `Internal Server Error`
assert.Equal(expected, strings.TrimSpace(rr.Body.String()))
}
func TestErrorWriterPutCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
plan := &models.Plan{}
m.AddPlan(plan, 3)
m.AddPlan(plan, 3)
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 1}, 3)
data := []byte(`{
"plan_id": 5
}`)
req, _ := http.NewRequestWithContext(sampleContext, "PUT", "/", bytes.NewBuffer(data))
req.Header.Set("Content-Type", "application/json")
rr := NewBadWriter()
router := routes.NewCurrentPlanRouter(m)
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
}

117
routes/current_plan_test.go Normal file
View File

@@ -0,0 +1,117 @@
package routes_test
import (
"context"
"gitea.deepak.science/deepak/gogmagog/models"
"gitea.deepak.science/deepak/gogmagog/routes"
// "gitea.deepak.science/deepak/gogmagog/tokens"
"github.com/stretchr/testify/assert"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func TestEmptyCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
router := routes.NewCurrentPlanRouter(m)
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
rr := httptest.NewRecorder()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusNotFound, status)
}
func TestEmptyCurrentPlanUnauth(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
router := routes.NewCurrentPlanRouter(m)
req, _ := http.NewRequestWithContext(context.Background(), "GET", "/", nil)
rr := httptest.NewRecorder()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusUnauthorized, status)
}
func TestErrorCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getErrorModel("Model always errors")
router := routes.NewCurrentPlanRouter(m)
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", 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 TestEmptyCurrentPlanErrorWriter(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 3}, 3)
router := routes.NewCurrentPlanRouter(m)
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", nil)
rr := NewBadWriter()
// function under test
router.ServeHTTP(rr, req)
// check results
status := rr.Code
assert.Equal(http.StatusInternalServerError, status)
}
func TestSingleCurrentPlan(t *testing.T) {
// set up
assert := assert.New(t)
m := getEmptyModel()
m.AddCurrentPlan(&models.CurrentPlan{PlanID: 3}, 3)
router := routes.NewCurrentPlanRouter(m)
req, _ := http.NewRequestWithContext(sampleContext, "GET", "/", 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": 3,
"user_id": 3
}`
assert.JSONEq(expected, rr.Body.String())
contentType := rr.Header().Get("Content-Type")
assert.Equal("application/json", contentType)
}

View File

@@ -18,6 +18,7 @@ func NewRouter(m *models.Model, tok tokens.Toker) http.Handler {
r.Mount("/actions", NewActionRouter(m))
r.Mount("/plans", NewPlanRouter(m))
r.Mount("/me", NewCurrentUserRouter(m))
r.Mount("/currentPlan", NewCurrentPlanRouter(m))
})
router.Mount("/auth", NewAuthRouter(m, tok))
router.Mount("/health", newHealthRouter(m))

View File

@@ -57,6 +57,24 @@ func (e *errorStore) InsertUser(user *models.User) (int, error) {
return 0, nil
}
func (e *errorStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
return nil, e.error
}
func (e *errorStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
if e.errorOnInsert {
return e.error
}
return nil
}
func (e *errorStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
if e.errorOnInsert {
return e.error
}
return nil
}
func (e *errorStore) ConnectionLive() error {
return e.error
}

View File

@@ -82,3 +82,25 @@ func TestErrorUserMethods(t *testing.T) {
_, err = str.SelectUserByUsername("snth")
assert.NotNil(err)
}
func TestErrorCurrentPlanMethods(t *testing.T) {
assert := assert.New(t)
str := store.GetErrorStore("error", true)
str2 := store.GetErrorStore("error", false)
cp := &models.CurrentPlan{}
_, err := str.SelectCurrentPlan(1)
assert.NotNil(err)
err = str.InsertCurrentPlan(cp, 1)
assert.NotNil(err)
err = str2.InsertCurrentPlan(cp, 1)
assert.Nil(err)
replace := &models.CurrentPlan{}
err = str.UpdateCurrentPlan(replace, 1)
assert.NotNil(err)
err = str2.UpdateCurrentPlan(replace, 1)
assert.Nil(err)
}

View File

@@ -2,6 +2,7 @@ package store
import (
"database/sql"
"fmt"
"gitea.deepak.science/deepak/gogmagog/models"
)
@@ -9,6 +10,7 @@ type inMemoryStore struct {
actions []*models.Action
plans []*models.Plan
users []*models.User
currentPlans []*models.CurrentPlan
}
// GetInMemoryStore provides a purely in memory store, for testing purposes only, with no persistence.
@@ -98,6 +100,38 @@ func (store *inMemoryStore) InsertPlan(plan *models.Plan, userID int) (int, erro
return id, nil
}
func (store *inMemoryStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
for _, currentPlan := range store.currentPlans {
if userID == int(currentPlan.UserID) {
return currentPlan, nil
}
}
return nil, sql.ErrNoRows
}
func (store *inMemoryStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
_, err := store.SelectCurrentPlan(userID)
if err == nil {
return fmt.Errorf("Can't insert primary plan")
}
// actually impossible, but at this point it must be a not found error.
// if err != sql.ErrNoRows {
// return err
// }
store.currentPlans = append(store.currentPlans, &models.CurrentPlan{PlanID: int64(currentPlan.PlanID), UserID: int64(userID)})
return nil
}
func (store *inMemoryStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
current, err := store.SelectCurrentPlan(userID)
if err != nil {
return err
}
current.PlanID = currentPlan.PlanID
return nil
}
func (store *inMemoryStore) ConnectionLive() error {
return nil
}

View File

@@ -105,3 +105,37 @@ func TestInMemoryUserMethods(t *testing.T) {
_, err = str.SelectUserByUsername("bad username")
assert.NotNil(err)
}
func TestInMemoryCurrentPlanMethods(t *testing.T) {
assert := assert.New(t)
str, _ := store.GetInMemoryStore()
userID := 10
cp1 := &models.CurrentPlan{PlanID: 1}
cp2 := &models.CurrentPlan{PlanID: 2}
err := str.UpdateCurrentPlan(cp1, userID)
assert.NotNil(err)
err = str.InsertCurrentPlan(cp1, userID)
assert.Nil(err)
receivedCp, err := str.SelectCurrentPlan(userID)
assert.Nil(err)
assert.EqualValues(1, receivedCp.PlanID)
_, err = str.SelectCurrentPlan(userID + 1)
assert.NotNil(err)
str.InsertCurrentPlan(cp2, userID)
assert.NotNil(err)
err = str.UpdateCurrentPlan(cp2, userID)
assert.Nil(err)
receivedCp, err = str.SelectCurrentPlan(userID)
assert.Nil(err)
assert.EqualValues(2, receivedCp.PlanID)
}

View File

@@ -1,4 +1,5 @@
DROP TABLE IF EXISTS actions;
DROP TABLE IF EXISTS user_current_plan;
DROP TABLE IF EXISTS plans;
DROP TABLE IF EXISTS users;

View File

@@ -12,7 +12,14 @@ CREATE TABLE IF NOT EXISTS plans(
plan_date DATE NOT NULL,
user_id int REFERENCES users(user_id),
created_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL
updated_at TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
UNIQUE (user_id, plan_id)
);
CREATE TABLE IF NOT EXISTS user_current_plan(
user_id int PRIMARY KEY,
plan_id int,
FOREIGN KEY (user_id, plan_id) REFERENCES plans(user_id, plan_id)
);
CREATE TABLE IF NOT EXISTS actions(

View File

@@ -174,3 +174,51 @@ func (store *postgresStore) InsertUser(user *models.User) (int, error) {
}
return id, nil
}
func (store *postgresStore) SelectCurrentPlan(userID int) (*models.CurrentPlan, error) {
pp := models.CurrentPlan{}
queryString := store.db.Rebind(`SELECT user_id, plan_id FROM user_current_plan WHERE user_id = ?`)
err := store.db.Get(&pp, queryString, userID)
if err != nil {
return nil, err
}
return &pp, nil
}
func (store *postgresStore) InsertCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
queryString := store.db.Rebind("INSERT INTO user_current_plan (user_id, plan_id) VALUES (?, ?) RETURNING user_id")
tx := store.db.MustBegin()
var id int
err := tx.Get(&id, queryString, userID, currentPlan.PlanID)
if err != nil {
tx.Rollback()
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}
func (store *postgresStore) UpdateCurrentPlan(currentPlan *models.CurrentPlan, userID int) error {
query := `UPDATE user_current_plan SET
plan_id = :plan_id
WHERE user_id = :user_id`
tx := store.db.MustBegin()
ppToUse := &models.CurrentPlan{
PlanID: currentPlan.PlanID,
UserID: int64(userID),
}
_, err := store.db.NamedExec(query, ppToUse)
if err != nil {
tx.Rollback()
return err
}
err = tx.Commit()
if err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,217 @@
package store_test
import (
"fmt"
"gitea.deepak.science/deepak/gogmagog/models"
"github.com/DATA-DOG/go-sqlmock"
"github.com/stretchr/testify/assert"
"testing"
)
func TestSelectCurrentPlan(t *testing.T) {
assert := assert.New(t)
planIDToUse := 1
userIDToUse := 2
str, mock := getDbMock(t)
rows := sqlmock.NewRows([]string{"plan_id", "user_id"}).AddRow(planIDToUse, userIDToUse)
mock.ExpectQuery(`^SELECT user_id, plan_id FROM user_current_plan WHERE user_id = \$1`).
WithArgs(userIDToUse).
WillReturnRows(rows)
currPlan, err := str.SelectCurrentPlan(userIDToUse)
assert.Nil(err)
assert.EqualValues(planIDToUse, currPlan.PlanID)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestSelectCurrentPlanErr(t *testing.T) {
assert := assert.New(t)
userIDToUse := 2
str, mock := getDbMock(t)
mock.ExpectQuery(`^SELECT user_id, plan_id FROM user_current_plan WHERE user_id = \$1`).
WithArgs(userIDToUse).
WillReturnError(fmt.Errorf("example error"))
currPlan, err := str.SelectCurrentPlan(userIDToUse)
assert.NotNil(err)
assert.Nil(currPlan)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertCurrentPlan(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
planID := 1
userID := 2
badUserID := 7
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(badUserID)}
rows := sqlmock.NewRows([]string{"userID"}).AddRow(userID)
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
WithArgs(userID, planID).
WillReturnRows(rows)
mock.ExpectCommit()
// function under test
err := str.InsertCurrentPlan(cp, userID)
// check results
assert.Nil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertCurrentPlanErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
userID := 2
badUserID := 7
planID := 1
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(badUserID)}
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
WithArgs(userID, planID).
WillReturnError(fmt.Errorf("example error"))
mock.ExpectRollback()
// function under test
err := str.InsertCurrentPlan(cp, userID)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestInsertCurrentPlanCommitErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
planID := 1
userID := 2
cp := &models.CurrentPlan{PlanID: int64(planID), UserID: int64(userID)}
rows := sqlmock.NewRows([]string{"user_id"}).AddRow(userID)
mock.ExpectBegin()
mock.ExpectQuery(`^INSERT INTO user_current_plan \(user_id, plan_id\) VALUES \(\$1, \$2\) RETURNING user_id$`).
WithArgs(userID, planID).
WillReturnRows(rows)
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
// function under test
err := str.InsertCurrentPlan(cp, userID)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestUpdateCurrentPlan(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
userIDToUse := 1
planIDToUse := 2
mock.ExpectBegin()
mock.ExpectExec(`
UPDATE user_current_plan SET
plan_id = \$1
WHERE user_id = \$2`).
WithArgs(planIDToUse, userIDToUse).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit()
// function under test
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
// check results
assert.Nil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestUpdateCurrentPlanErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
userIDToUse := 1
planIDToUse := 2
mock.ExpectBegin()
mock.ExpectExec(`
UPDATE user_current_plan SET
plan_id = \$1
WHERE user_id = \$2`).
WithArgs(planIDToUse, userIDToUse).
WillReturnError(fmt.Errorf("example error"))
mock.ExpectRollback()
// function under test
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}
func TestUpdateCurrentPlanCommitErr(t *testing.T) {
// setup
assert := assert.New(t)
str, mock := getDbMock(t)
userIDToUse := 1
planIDToUse := 2
mock.ExpectBegin()
mock.ExpectExec(`
UPDATE user_current_plan SET
plan_id = \$1
WHERE user_id = \$2`).
WithArgs(planIDToUse, userIDToUse).
WillReturnResult(sqlmock.NewResult(1, 1))
mock.ExpectCommit().WillReturnError(fmt.Errorf("another error example"))
// function under test
err := str.UpdateCurrentPlan(&models.CurrentPlan{PlanID: int64(planIDToUse)}, userIDToUse)
// check results
assert.NotNil(err)
if err := mock.ExpectationsWereMet(); err != nil {
t.Errorf("unfulfilled expectations: %s", err)
}
}