From 39bd724381dbafa4cd62691dd4151f4d459d358c Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Sat, 4 Jun 2022 14:48:24 +0200 Subject: [PATCH] Switch routing to chi Routing with httprouter and alice became a bit too complex. This commit replaces the routing and middleware composition with chi. --- cmd/boardvoting/handlers.go | 62 ++++---------- cmd/boardvoting/helpers.go | 29 +++---- cmd/boardvoting/main.go | 4 + cmd/boardvoting/middleware.go | 10 +-- cmd/boardvoting/middleware_test.go | 31 ------- cmd/boardvoting/routes.go | 125 ++++++++++++++++------------- go.mod | 3 +- go.sum | 5 +- internal/models/motions_test.go | 4 +- 9 files changed, 105 insertions(+), 168 deletions(-) diff --git a/cmd/boardvoting/handlers.go b/cmd/boardvoting/handlers.go index 725b766..393b32c 100644 --- a/cmd/boardvoting/handlers.go +++ b/cmd/boardvoting/handlers.go @@ -24,8 +24,6 @@ import ( "strings" "time" - "github.com/julienschmidt/httprouter" - "git.cacert.org/cacert-boardvoting/internal/forms" "git.cacert.org/cacert-boardvoting/internal/models" @@ -176,9 +174,7 @@ func (app *application) calculateMotionListOptions(r *http.Request) (*models.Mot } func (app *application) motionDetails(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } @@ -271,9 +267,7 @@ func (app *application) newMotionSubmit(w http.ResponseWriter, r *http.Request) } func (app *application) editMotionForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } @@ -292,9 +286,7 @@ func (app *application) editMotionForm(w http.ResponseWriter, r *http.Request) { } func (app *application) editMotionSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } @@ -371,9 +363,7 @@ func (app *application) editMotionSubmit(w http.ResponseWriter, r *http.Request) } func (app *application) withdrawMotionForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { app.notFound(w) @@ -388,9 +378,7 @@ func (app *application) withdrawMotionForm(w http.ResponseWriter, r *http.Reques } func (app *application) withdrawMotionSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { app.notFound(w) @@ -426,16 +414,14 @@ func (app *application) withdrawMotionSubmit(w http.ResponseWriter, r *http.Requ } func (app *application) voteForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { app.notFound(w) return } - choice := app.choiceFromRequestParam(w, params) + choice := app.choiceFromRequestParam(w, r) if choice == nil { app.notFound(w) @@ -454,14 +440,12 @@ func (app *application) voteForm(w http.ResponseWriter, r *http.Request) { } func (app *application) voteSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } - choice := app.choiceFromRequestParam(w, params) + choice := app.choiceFromRequestParam(w, r) if choice == nil { return } @@ -504,9 +488,7 @@ func (app *application) voteSubmit(w http.ResponseWriter, r *http.Request) { } func (app *application) proxyVoteForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } @@ -530,9 +512,7 @@ func (app *application) proxyVoteForm(w http.ResponseWriter, r *http.Request) { } func (app *application) proxyVoteSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - motion := app.motionFromRequestParam(w, r, params) + motion := app.motionFromRequestParam(w, r) if motion == nil { return } @@ -635,9 +615,7 @@ func (app *application) userList(w http.ResponseWriter, r *http.Request) { } func (app *application) editUserForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - userToEdit := app.userFromRequestParam(w, r, params, app.users.WithRoles(), app.users.WithEmailAddresses()) + userToEdit := app.userFromRequestParam(w, r, app.users.WithRoles(), app.users.WithEmailAddresses()) if userToEdit == nil { return } @@ -676,9 +654,7 @@ func (app *application) editUserForm(w http.ResponseWriter, r *http.Request) { } func (app *application) editUserSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - userToEdit := app.userFromRequestParam(w, r, params, app.users.WithRoles(), app.users.WithEmailAddresses()) + userToEdit := app.userFromRequestParam(w, r, app.users.WithRoles(), app.users.WithEmailAddresses()) if userToEdit == nil { app.notFound(w) @@ -753,9 +729,7 @@ func (app *application) editUserSubmit(w http.ResponseWriter, r *http.Request) { } func (app *application) userAddEmailForm(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses()) + userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses()) if userToEdit == nil { app.notFound(w) @@ -780,9 +754,7 @@ func (app *application) userAddEmailForm(w http.ResponseWriter, r *http.Request) } func (app *application) userAddEmailSubmit(w http.ResponseWriter, r *http.Request) { - params := httprouter.ParamsFromContext(r.Context()) - - userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses()) + userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses()) if userToEdit == nil { app.notFound(w) @@ -970,7 +942,7 @@ func (app *application) newUserSubmit(_ http.ResponseWriter, _ *http.Request) { } func (app *application) deleteUserForm(w http.ResponseWriter, r *http.Request) { - userToDelete := app.userFromRequestParam(w, r, httprouter.ParamsFromContext(r.Context()), app.users.CanDelete()) + userToDelete := app.userFromRequestParam(w, r, app.users.CanDelete()) if userToDelete == nil { return } @@ -991,7 +963,7 @@ func (app *application) deleteUserForm(w http.ResponseWriter, r *http.Request) { } func (app *application) deleteUserSubmit(w http.ResponseWriter, r *http.Request) { - userToDelete := app.userFromRequestParam(w, r, httprouter.ParamsFromContext(r.Context()), app.users.CanDelete()) + userToDelete := app.userFromRequestParam(w, r, app.users.CanDelete()) if userToDelete == nil { return } diff --git a/cmd/boardvoting/helpers.go b/cmd/boardvoting/helpers.go index a22c831..60d816c 100644 --- a/cmd/boardvoting/helpers.go +++ b/cmd/boardvoting/helpers.go @@ -32,8 +32,8 @@ import ( "strings" "github.com/Masterminds/sprig/v3" + "github.com/go-chi/chi/v5" "github.com/go-playground/form/v4" - "github.com/julienschmidt/httprouter" "github.com/justinas/nosurf" "git.cacert.org/cacert-boardvoting/internal/models" @@ -175,13 +175,10 @@ func (app *application) render(w http.ResponseWriter, status int, page string, d func (app *application) motionFromRequestParam( w http.ResponseWriter, r *http.Request, - params httprouter.Params, ) *models.Motion { - tag := params.ByName("tag") - withVotes := r.URL.Query().Has("showvotes") - motion, err := app.motions.ByTag(r.Context(), tag, withVotes) + motion, err := app.motions.ByTag(r.Context(), chi.URLParam(r, "tag"), withVotes) if err != nil { app.serverError(w, err) @@ -198,9 +195,9 @@ func (app *application) motionFromRequestParam( } func (app *application) userFromRequestParam( - w http.ResponseWriter, r *http.Request, params httprouter.Params, options ...models.UserListOption, + w http.ResponseWriter, r *http.Request, options ...models.UserListOption, ) *models.User { - userID, err := strconv.Atoi(params.ByName("id")) + userID, err := strconv.Atoi(chi.URLParam(r, "id")) if err != nil { app.clientError(w, http.StatusBadRequest) @@ -223,14 +220,14 @@ func (app *application) userFromRequestParam( return user } -func (app *application) emailFromRequestParam(w http.ResponseWriter, r *http.Request, params httprouter.Params, user *models.User) (string, error) { - emailParam := params.ByName("address") - +func (app *application) emailFromRequestParam(r *http.Request, user *models.User) (string, error) { emailAddresses, err := user.EmailAddresses() if err != nil { return "", fmt.Errorf("could not get email addresses: %w", err) } + emailParam := chi.URLParam(r, "address") + for _, address := range emailAddresses { if emailParam == address { return emailParam, nil @@ -241,14 +238,12 @@ func (app *application) emailFromRequestParam(w http.ResponseWriter, r *http.Req } func (app *application) deleteEmailParams(w http.ResponseWriter, r *http.Request) (*models.User, string, error) { - params := httprouter.ParamsFromContext(r.Context()) - - userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses()) + userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses()) if userToEdit == nil { return nil, "", nil } - emailAddress, err := app.emailFromRequestParam(w, r, params, userToEdit) + emailAddress, err := app.emailFromRequestParam(r, userToEdit) if err != nil { return nil, "", err } @@ -256,10 +251,8 @@ func (app *application) deleteEmailParams(w http.ResponseWriter, r *http.Request return userToEdit, emailAddress, nil } -func (app *application) choiceFromRequestParam(w http.ResponseWriter, params httprouter.Params) *models.VoteChoice { - choiceParam := params.ByName("choice") - - choice, err := models.VoteChoiceFromString(choiceParam) +func (app *application) choiceFromRequestParam(w http.ResponseWriter, r *http.Request) *models.VoteChoice { + choice, err := models.VoteChoiceFromString(chi.URLParam(r, "choice")) if err != nil { app.clientError(w, http.StatusBadRequest) diff --git a/cmd/boardvoting/main.go b/cmd/boardvoting/main.go index d11953c..1f229c0 100644 --- a/cmd/boardvoting/main.go +++ b/cmd/boardvoting/main.go @@ -237,6 +237,10 @@ func (app *application) getVoter(w http.ResponseWriter, r *http.Request, voterID return voter } +type logEntry struct { + request *http.Request +} + func setupHTTPRedirect(config *Config, errChan chan error) { redirect := &http.Server{ Addr: config.HTTPAddress, diff --git a/cmd/boardvoting/middleware.go b/cmd/boardvoting/middleware.go index b4fdf92..8bcc894 100644 --- a/cmd/boardvoting/middleware.go +++ b/cmd/boardvoting/middleware.go @@ -50,14 +50,6 @@ func secureHeaders(next http.Handler) http.Handler { }) } -func (app *application) logRequest(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - app.infoLog.Printf("%s - %s %s %s", r.RemoteAddr, r.Proto, r.Method, r.URL.RequestURI()) - - next.ServeHTTP(w, r) - }) -} - func (app *application) authenticateRequest(r *http.Request) (*models.User, *x509.Certificate, error) { if r.TLS == nil { return nil, nil, nil @@ -198,7 +190,7 @@ func (app *application) userCanEditVote(next http.Handler) http.Handler { return app.requireRole(next, models.RoleVoter) } -func (app *application) userCanChangeVoters(next http.Handler) http.Handler { +func (app *application) canManageUsers(next http.Handler) http.Handler { return app.requireRole(next, models.RoleSecretary, models.RoleAdmin) } diff --git a/cmd/boardvoting/middleware_test.go b/cmd/boardvoting/middleware_test.go index d41ff37..ddfca55 100644 --- a/cmd/boardvoting/middleware_test.go +++ b/cmd/boardvoting/middleware_test.go @@ -18,11 +18,9 @@ limitations under the License. package main import ( - "bytes" "context" "crypto/tls" "crypto/x509" - "fmt" "log" "net/http" "net/http/httptest" @@ -58,35 +56,6 @@ func Test_secureHeaders(t *testing.T) { assert.Equal(t, "max-age=63072000", rs.Header.Get("Strict-Transport-Security")) } -func TestApplication_logRequest(t *testing.T) { - rr := httptest.NewRecorder() - - r, err := http.NewRequest(http.MethodGet, "/", nil) - require.NoError(t, err) - - r.RemoteAddr = "arg" - - next := http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { - _, _ = w.Write([]byte("OK")) - }) - - buf := new(bytes.Buffer) - app := &application{infoLog: log.New(buf, "", log.LstdFlags)} - - app.logRequest(next).ServeHTTP(rr, r) - - rs := rr.Result() - assert.Equal(t, http.StatusOK, rs.StatusCode) - - assert.Contains(t, buf.String(), fmt.Sprintf( - "%s - %s %s %s", - r.RemoteAddr, - r.Proto, - r.Method, - r.URL.RequestURI(), - )) -} - func TestApplication_tryAuthenticate(t *testing.T) { db := prepareTestDb(t) diff --git a/cmd/boardvoting/routes.go b/cmd/boardvoting/routes.go index ad905c0..3e3aa8d 100644 --- a/cmd/boardvoting/routes.go +++ b/cmd/boardvoting/routes.go @@ -18,12 +18,11 @@ limitations under the License. package main import ( - "fmt" "io/fs" "net/http" - "github.com/julienschmidt/httprouter" - "github.com/justinas/alice" + "github.com/go-chi/chi/v5" + "github.com/go-chi/chi/v5/middleware" "github.com/vearutop/statigz" "github.com/vearutop/statigz/brotli" @@ -40,65 +39,75 @@ func (app *application) routes() http.Handler { fileServer := statigz.FileServer(staticData, brotli.AddEncoding, statigz.EncodeOnInit) - router := httprouter.New() + router := chi.NewRouter() - router.NotFound = http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { app.notFound(w) }) - router.PanicHandler = func(w http.ResponseWriter, _ *http.Request, err interface{}) { - w.Header().Set("Connection", "close") - app.serverError(w, fmt.Errorf("%s", err)) - } + router.Use(middleware.RealIP) + router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: app.infoLog})) + router.Use(middleware.Recoverer) + router.Use(secureHeaders) + + router.NotFound(func(w http.ResponseWriter, _ *http.Request) { app.notFound(w) }) - router.Handler( - http.MethodGet, + router.Get( "/", - http.RedirectHandler("/motions/", http.StatusMovedPermanently), + http.RedirectHandler("/motions/", http.StatusMovedPermanently).ServeHTTP, ) - router.Handler( - http.MethodGet, + router.Get( "/favicon.ico", - http.RedirectHandler("/static/images/favicon.ico", http.StatusMovedPermanently), + http.RedirectHandler("/static/images/favicon.ico", http.StatusMovedPermanently).ServeHTTP, ) - router.Handler(http.MethodGet, "/static/*filepath", http.StripPrefix("/static", fileServer)) - - dynamic := alice.New( - app.sessionManager.LoadAndSave, - app.tryAuthenticate, - ) - - canVote := dynamic.Append(app.userCanVote, noSurf) - canEditVote := dynamic.Append(app.userCanEditVote, noSurf) - canManageUsers := dynamic.Append(app.userCanChangeVoters, noSurf) - - router.Handler(http.MethodGet, "/motions/", dynamic.ThenFunc(app.motionList)) - router.Handler(http.MethodGet, "/motions/:tag", dynamic.ThenFunc(app.motionDetails)) - router.Handler(http.MethodGet, "/motions/:tag/edit", canEditVote.ThenFunc(app.editMotionForm)) - router.Handler(http.MethodPost, "/motions/:tag/edit", canEditVote.ThenFunc(app.editMotionSubmit)) - router.Handler(http.MethodGet, "/motions/:tag/withdraw", canEditVote.ThenFunc(app.withdrawMotionForm)) - router.Handler(http.MethodPost, "/motions/:tag/withdraw", canEditVote.ThenFunc(app.withdrawMotionSubmit)) - router.Handler(http.MethodGet, "/vote/:tag/:choice", canVote.ThenFunc(app.voteForm)) - router.Handler(http.MethodPost, "/vote/:tag/:choice", canVote.ThenFunc(app.voteSubmit)) - router.Handler(http.MethodGet, "/proxy/:tag", canVote.ThenFunc(app.proxyVoteForm)) - router.Handler(http.MethodPost, "/proxy/:tag", canVote.ThenFunc(app.proxyVoteSubmit)) - router.Handler(http.MethodGet, "/newmotion/", canEditVote.ThenFunc(app.newMotionForm)) - router.Handler(http.MethodPost, "/newmotion/", canEditVote.ThenFunc(app.newMotionSubmit)) - - router.Handler(http.MethodGet, "/users/", canManageUsers.ThenFunc(app.userList)) - router.Handler(http.MethodGet, "/new-user/", canManageUsers.ThenFunc(app.newUserForm)) - router.Handler(http.MethodPost, "/new-user/", canManageUsers.ThenFunc(app.newUserSubmit)) - router.Handler(http.MethodGet, "/users/:id/", canManageUsers.ThenFunc(app.editUserForm)) - router.Handler(http.MethodPost, "/users/:id/", canManageUsers.ThenFunc(app.editUserSubmit)) - router.Handler(http.MethodGet, "/users/:id/add-mail", canManageUsers.ThenFunc(app.userAddEmailForm)) - router.Handler(http.MethodPost, "/users/:id/add-mail", canManageUsers.ThenFunc(app.userAddEmailSubmit)) - router.Handler(http.MethodGet, "/users/:id/mail/:address/delete", - canManageUsers.ThenFunc(app.userDeleteEmailForm)) - router.Handler(http.MethodPost, "/users/:id/mail/:address/delete", - canManageUsers.ThenFunc(app.userDeleteEmailSubmit)) - router.Handler(http.MethodGet, "/users/:id/delete", canManageUsers.ThenFunc(app.deleteUserForm)) - router.Handler(http.MethodPost, "/users/:id/delete", canManageUsers.ThenFunc(app.deleteUserSubmit)) - - router.HandlerFunc(http.MethodGet, "/health", app.healthCheck) - - standard := alice.New(app.logRequest, secureHeaders) - - return standard.Then(router) + router.Get("/static/*", http.StripPrefix("/static", fileServer).ServeHTTP) + + router.Group(func(r chi.Router) { + r.Use(app.sessionManager.LoadAndSave, app.tryAuthenticate) + + r.Get("/motions/", app.motionList) + r.Get("/motions/{tag}", app.motionDetails) + + r.Group(func(r chi.Router) { + r.Use(app.userCanEditVote, noSurf) + + r.Get("/newmotion/", app.newMotionForm) + r.Post("/newmotion/", app.newMotionSubmit) + + r.Route("/motions/{tag}", func(r chi.Router) { + r.Get("/edit", app.editMotionForm) + r.Post("/edit", app.editMotionSubmit) + r.Get("/withdraw", app.withdrawMotionForm) + r.Post("/withdraw", app.withdrawMotionSubmit) + }) + }) + + r.Group(func(r chi.Router) { + r.Use(app.userCanVote, noSurf) + + r.Get("/vote/{tag}/{choice}", app.voteForm) + r.Post("/vote/{tag}/{choice}", app.voteSubmit) + r.Get("/proxy/{tag}", app.proxyVoteForm) + r.Post("/proxy/{tag}", app.proxyVoteSubmit) + }) + + r.Group(func(r chi.Router) { + r.Use(app.canManageUsers, noSurf) + + r.Get("/users/", app.userList) + r.Get("/new-user/", app.newUserForm) + r.Post("/new-user/", app.newUserSubmit) + + r.Route("/users/{id}", func(r chi.Router) { + r.Get("/", app.editUserForm) + r.Post("/", app.editUserSubmit) + r.Get("/add-mail", app.userAddEmailForm) + r.Post("/add-mail", app.userAddEmailSubmit) + r.Get("/mail/{address}/delete", app.userDeleteEmailForm) + r.Post("/mail/{address}/delete", app.userDeleteEmailSubmit) + r.Get("/delete", app.deleteUserForm) + r.Post("/delete", app.deleteUserSubmit) + }) + }) + }) + + router.Get("/health", app.healthCheck) + + return router } diff --git a/go.mod b/go.mod index 40a68ce..e56263a 100644 --- a/go.mod +++ b/go.mod @@ -24,9 +24,8 @@ require ( require ( github.com/alexedwards/scs/sqlite3store v0.0.0-20220216073957-c252878bcf5a github.com/alexedwards/scs/v2 v2.5.0 + github.com/go-chi/chi/v5 v5.0.7 github.com/go-playground/form/v4 v4.2.0 - github.com/julienschmidt/httprouter v1.3.0 - github.com/justinas/alice v1.2.0 github.com/justinas/nosurf v1.1.1 github.com/lestrrat-go/tcputil v0.0.0-20180223003554-d3c7f98154fb github.com/stretchr/testify v1.7.0 diff --git a/go.sum b/go.sum index 3538330..f3715bc 100644 --- a/go.sum +++ b/go.sum @@ -423,6 +423,8 @@ github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7/go.mod h1:NR3MbYis github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= +github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8= +github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= @@ -751,12 +753,9 @@ github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1 github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= -github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo= -github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA= github.com/justinas/nosurf v1.1.1 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk= github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k= diff --git a/internal/models/motions_test.go b/internal/models/motions_test.go index e01b717..a1800cb 100644 --- a/internal/models/motions_test.go +++ b/internal/models/motions_test.go @@ -60,7 +60,7 @@ func TestDecisionModel_Create(t *testing.T) { v := &models.User{ ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index. Name: "test voter", - Reminder: "test+voter@example.com", + Reminder: sql.NullString{String: "test+voter@example.com", Valid: true}, } id, err := dm.Create( @@ -93,7 +93,7 @@ func TestDecisionModel_GetNextPendingDecisionDue(t *testing.T) { v := &models.User{ ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index. Name: "test voter", - Reminder: "test+voter@example.com", + Reminder: sql.NullString{String: "test+voter@example.com", Valid: true}, } due := time.Now().Add(10 * time.Minute)