Switch routing to chi

Routing with httprouter and alice became a bit too complex. This commit
replaces the routing and middleware composition with chi.
main
Jan Dittberner 2 years ago
parent de4c6faef6
commit 39bd724381

@ -24,8 +24,6 @@ import (
"strings" "strings"
"time" "time"
"github.com/julienschmidt/httprouter"
"git.cacert.org/cacert-boardvoting/internal/forms" "git.cacert.org/cacert-boardvoting/internal/forms"
"git.cacert.org/cacert-boardvoting/internal/models" "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) { func (app *application) motionDetails(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return 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) { func (app *application) editMotionForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return 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) { func (app *application) editMotionSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return 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) { func (app *application) withdrawMotionForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
app.notFound(w) 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) { func (app *application) withdrawMotionSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
app.notFound(w) 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) { func (app *application) voteForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
app.notFound(w) app.notFound(w)
return return
} }
choice := app.choiceFromRequestParam(w, params) choice := app.choiceFromRequestParam(w, r)
if choice == nil { if choice == nil {
app.notFound(w) 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) { func (app *application) voteSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return return
} }
choice := app.choiceFromRequestParam(w, params) choice := app.choiceFromRequestParam(w, r)
if choice == nil { if choice == nil {
return 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) { func (app *application) proxyVoteForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return 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) { func (app *application) proxyVoteSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) motion := app.motionFromRequestParam(w, r)
motion := app.motionFromRequestParam(w, r, params)
if motion == nil { if motion == nil {
return 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) { func (app *application) editUserForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) userToEdit := app.userFromRequestParam(w, r, app.users.WithRoles(), app.users.WithEmailAddresses())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithRoles(), app.users.WithEmailAddresses())
if userToEdit == nil { if userToEdit == nil {
return 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) { func (app *application) editUserSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) userToEdit := app.userFromRequestParam(w, r, app.users.WithRoles(), app.users.WithEmailAddresses())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithRoles(), app.users.WithEmailAddresses())
if userToEdit == nil { if userToEdit == nil {
app.notFound(w) 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) { func (app *application) userAddEmailForm(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses())
if userToEdit == nil { if userToEdit == nil {
app.notFound(w) 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) { func (app *application) userAddEmailSubmit(w http.ResponseWriter, r *http.Request) {
params := httprouter.ParamsFromContext(r.Context()) userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses())
if userToEdit == nil { if userToEdit == nil {
app.notFound(w) 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) { 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 { if userToDelete == nil {
return 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) { 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 { if userToDelete == nil {
return return
} }

@ -32,8 +32,8 @@ import (
"strings" "strings"
"github.com/Masterminds/sprig/v3" "github.com/Masterminds/sprig/v3"
"github.com/go-chi/chi/v5"
"github.com/go-playground/form/v4" "github.com/go-playground/form/v4"
"github.com/julienschmidt/httprouter"
"github.com/justinas/nosurf" "github.com/justinas/nosurf"
"git.cacert.org/cacert-boardvoting/internal/models" "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( func (app *application) motionFromRequestParam(
w http.ResponseWriter, w http.ResponseWriter,
r *http.Request, r *http.Request,
params httprouter.Params,
) *models.Motion { ) *models.Motion {
tag := params.ByName("tag")
withVotes := r.URL.Query().Has("showvotes") 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 { if err != nil {
app.serverError(w, err) app.serverError(w, err)
@ -198,9 +195,9 @@ func (app *application) motionFromRequestParam(
} }
func (app *application) userFromRequestParam( 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 { ) *models.User {
userID, err := strconv.Atoi(params.ByName("id")) userID, err := strconv.Atoi(chi.URLParam(r, "id"))
if err != nil { if err != nil {
app.clientError(w, http.StatusBadRequest) app.clientError(w, http.StatusBadRequest)
@ -223,14 +220,14 @@ func (app *application) userFromRequestParam(
return user return user
} }
func (app *application) emailFromRequestParam(w http.ResponseWriter, r *http.Request, params httprouter.Params, user *models.User) (string, error) { func (app *application) emailFromRequestParam(r *http.Request, user *models.User) (string, error) {
emailParam := params.ByName("address")
emailAddresses, err := user.EmailAddresses() emailAddresses, err := user.EmailAddresses()
if err != nil { if err != nil {
return "", fmt.Errorf("could not get email addresses: %w", err) return "", fmt.Errorf("could not get email addresses: %w", err)
} }
emailParam := chi.URLParam(r, "address")
for _, address := range emailAddresses { for _, address := range emailAddresses {
if emailParam == address { if emailParam == address {
return emailParam, nil 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) { func (app *application) deleteEmailParams(w http.ResponseWriter, r *http.Request) (*models.User, string, error) {
params := httprouter.ParamsFromContext(r.Context()) userToEdit := app.userFromRequestParam(w, r, app.users.WithEmailAddresses())
userToEdit := app.userFromRequestParam(w, r, params, app.users.WithEmailAddresses())
if userToEdit == nil { if userToEdit == nil {
return nil, "", nil return nil, "", nil
} }
emailAddress, err := app.emailFromRequestParam(w, r, params, userToEdit) emailAddress, err := app.emailFromRequestParam(r, userToEdit)
if err != nil { if err != nil {
return nil, "", err return nil, "", err
} }
@ -256,10 +251,8 @@ func (app *application) deleteEmailParams(w http.ResponseWriter, r *http.Request
return userToEdit, emailAddress, nil return userToEdit, emailAddress, nil
} }
func (app *application) choiceFromRequestParam(w http.ResponseWriter, params httprouter.Params) *models.VoteChoice { func (app *application) choiceFromRequestParam(w http.ResponseWriter, r *http.Request) *models.VoteChoice {
choiceParam := params.ByName("choice") choice, err := models.VoteChoiceFromString(chi.URLParam(r, "choice"))
choice, err := models.VoteChoiceFromString(choiceParam)
if err != nil { if err != nil {
app.clientError(w, http.StatusBadRequest) app.clientError(w, http.StatusBadRequest)

@ -237,6 +237,10 @@ func (app *application) getVoter(w http.ResponseWriter, r *http.Request, voterID
return voter return voter
} }
type logEntry struct {
request *http.Request
}
func setupHTTPRedirect(config *Config, errChan chan error) { func setupHTTPRedirect(config *Config, errChan chan error) {
redirect := &http.Server{ redirect := &http.Server{
Addr: config.HTTPAddress, Addr: config.HTTPAddress,

@ -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) { func (app *application) authenticateRequest(r *http.Request) (*models.User, *x509.Certificate, error) {
if r.TLS == nil { if r.TLS == nil {
return nil, nil, nil return nil, nil, nil
@ -198,7 +190,7 @@ func (app *application) userCanEditVote(next http.Handler) http.Handler {
return app.requireRole(next, models.RoleVoter) 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) return app.requireRole(next, models.RoleSecretary, models.RoleAdmin)
} }

@ -18,11 +18,9 @@ limitations under the License.
package main package main
import ( import (
"bytes"
"context" "context"
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"fmt"
"log" "log"
"net/http" "net/http"
"net/http/httptest" "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")) 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) { func TestApplication_tryAuthenticate(t *testing.T) {
db := prepareTestDb(t) db := prepareTestDb(t)

@ -18,12 +18,11 @@ limitations under the License.
package main package main
import ( import (
"fmt"
"io/fs" "io/fs"
"net/http" "net/http"
"github.com/julienschmidt/httprouter" "github.com/go-chi/chi/v5"
"github.com/justinas/alice" "github.com/go-chi/chi/v5/middleware"
"github.com/vearutop/statigz" "github.com/vearutop/statigz"
"github.com/vearutop/statigz/brotli" "github.com/vearutop/statigz/brotli"
@ -40,65 +39,75 @@ func (app *application) routes() http.Handler {
fileServer := statigz.FileServer(staticData, brotli.AddEncoding, statigz.EncodeOnInit) 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.Use(middleware.RealIP)
router.PanicHandler = func(w http.ResponseWriter, _ *http.Request, err interface{}) { router.Use(middleware.RequestLogger(&middleware.DefaultLogFormatter{Logger: app.infoLog}))
w.Header().Set("Connection", "close") router.Use(middleware.Recoverer)
app.serverError(w, fmt.Errorf("%s", err)) router.Use(secureHeaders)
}
router.NotFound(func(w http.ResponseWriter, _ *http.Request) { app.notFound(w) })
router.Handler( router.Get(
http.MethodGet,
"/", "/",
http.RedirectHandler("/motions/", http.StatusMovedPermanently), http.RedirectHandler("/motions/", http.StatusMovedPermanently).ServeHTTP,
) )
router.Handler( router.Get(
http.MethodGet,
"/favicon.ico", "/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)) router.Get("/static/*", http.StripPrefix("/static", fileServer).ServeHTTP)
dynamic := alice.New( router.Group(func(r chi.Router) {
app.sessionManager.LoadAndSave, r.Use(app.sessionManager.LoadAndSave, app.tryAuthenticate)
app.tryAuthenticate,
) r.Get("/motions/", app.motionList)
r.Get("/motions/{tag}", app.motionDetails)
canVote := dynamic.Append(app.userCanVote, noSurf)
canEditVote := dynamic.Append(app.userCanEditVote, noSurf) r.Group(func(r chi.Router) {
canManageUsers := dynamic.Append(app.userCanChangeVoters, noSurf) r.Use(app.userCanEditVote, noSurf)
router.Handler(http.MethodGet, "/motions/", dynamic.ThenFunc(app.motionList)) r.Get("/newmotion/", app.newMotionForm)
router.Handler(http.MethodGet, "/motions/:tag", dynamic.ThenFunc(app.motionDetails)) r.Post("/newmotion/", app.newMotionSubmit)
router.Handler(http.MethodGet, "/motions/:tag/edit", canEditVote.ThenFunc(app.editMotionForm))
router.Handler(http.MethodPost, "/motions/:tag/edit", canEditVote.ThenFunc(app.editMotionSubmit)) r.Route("/motions/{tag}", func(r chi.Router) {
router.Handler(http.MethodGet, "/motions/:tag/withdraw", canEditVote.ThenFunc(app.withdrawMotionForm)) r.Get("/edit", app.editMotionForm)
router.Handler(http.MethodPost, "/motions/:tag/withdraw", canEditVote.ThenFunc(app.withdrawMotionSubmit)) r.Post("/edit", app.editMotionSubmit)
router.Handler(http.MethodGet, "/vote/:tag/:choice", canVote.ThenFunc(app.voteForm)) r.Get("/withdraw", app.withdrawMotionForm)
router.Handler(http.MethodPost, "/vote/:tag/:choice", canVote.ThenFunc(app.voteSubmit)) r.Post("/withdraw", app.withdrawMotionSubmit)
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)) r.Group(func(r chi.Router) {
r.Use(app.userCanVote, noSurf)
router.Handler(http.MethodGet, "/users/", canManageUsers.ThenFunc(app.userList))
router.Handler(http.MethodGet, "/new-user/", canManageUsers.ThenFunc(app.newUserForm)) r.Get("/vote/{tag}/{choice}", app.voteForm)
router.Handler(http.MethodPost, "/new-user/", canManageUsers.ThenFunc(app.newUserSubmit)) r.Post("/vote/{tag}/{choice}", app.voteSubmit)
router.Handler(http.MethodGet, "/users/:id/", canManageUsers.ThenFunc(app.editUserForm)) r.Get("/proxy/{tag}", app.proxyVoteForm)
router.Handler(http.MethodPost, "/users/:id/", canManageUsers.ThenFunc(app.editUserSubmit)) r.Post("/proxy/{tag}", app.proxyVoteSubmit)
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", r.Group(func(r chi.Router) {
canManageUsers.ThenFunc(app.userDeleteEmailForm)) r.Use(app.canManageUsers, noSurf)
router.Handler(http.MethodPost, "/users/:id/mail/:address/delete",
canManageUsers.ThenFunc(app.userDeleteEmailSubmit)) r.Get("/users/", app.userList)
router.Handler(http.MethodGet, "/users/:id/delete", canManageUsers.ThenFunc(app.deleteUserForm)) r.Get("/new-user/", app.newUserForm)
router.Handler(http.MethodPost, "/users/:id/delete", canManageUsers.ThenFunc(app.deleteUserSubmit)) r.Post("/new-user/", app.newUserSubmit)
router.HandlerFunc(http.MethodGet, "/health", app.healthCheck) r.Route("/users/{id}", func(r chi.Router) {
r.Get("/", app.editUserForm)
standard := alice.New(app.logRequest, secureHeaders) r.Post("/", app.editUserSubmit)
r.Get("/add-mail", app.userAddEmailForm)
return standard.Then(router) 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
} }

@ -24,9 +24,8 @@ require (
require ( require (
github.com/alexedwards/scs/sqlite3store v0.0.0-20220216073957-c252878bcf5a github.com/alexedwards/scs/sqlite3store v0.0.0-20220216073957-c252878bcf5a
github.com/alexedwards/scs/v2 v2.5.0 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/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/justinas/nosurf v1.1.1
github.com/lestrrat-go/tcputil v0.0.0-20180223003554-d3c7f98154fb github.com/lestrrat-go/tcputil v0.0.0-20180223003554-d3c7f98154fb
github.com/stretchr/testify v1.7.0 github.com/stretchr/testify v1.7.0

@ -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/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 v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/ghodss/yaml v1.0.0/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/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/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks=
github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= 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/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/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.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/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.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/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 h1:92Aw44hjSK4MxJeMSyDa7jwuI9GR2J/JCQiaKvXXSlk=
github.com/justinas/nosurf v1.1.1/go.mod h1:ALpWdSbuNGy2lZWtyXdjkYv4edL23oSEgfBT1gPJ5BQ= 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= github.com/k0kubun/colorstring v0.0.0-20150214042306-9440f1994b88/go.mod h1:3w7q1U84EfirKl04SVQ/s7nPm1ZPhiXd34z40TNz36k=

@ -60,7 +60,7 @@ func TestDecisionModel_Create(t *testing.T) {
v := &models.User{ v := &models.User{
ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index. ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index.
Name: "test voter", Name: "test voter",
Reminder: "test+voter@example.com", Reminder: sql.NullString{String: "test+voter@example.com", Valid: true},
} }
id, err := dm.Create( id, err := dm.Create(
@ -93,7 +93,7 @@ func TestDecisionModel_GetNextPendingDecisionDue(t *testing.T) {
v := &models.User{ v := &models.User{
ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index. ID: 1, // sqlite does not check referential integrity. Might fail with a foreign key index.
Name: "test voter", 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) due := time.Now().Add(10 * time.Minute)

Loading…
Cancel
Save