diff --git a/go.mod b/go.mod index fc4973d..04b4394 100644 --- a/go.mod +++ b/go.mod @@ -7,6 +7,7 @@ require ( github.com/Masterminds/sprig v2.22.0+incompatible github.com/PuerkitoBio/goquery v1.8.0 github.com/eko/gocache/v2 v2.1.0 + github.com/go-playground/assert/v2 v2.0.1 github.com/go-playground/validator/v10 v10.9.0 github.com/go-redis/redis/v8 v8.11.4 github.com/gorilla/sessions v1.2.1 @@ -17,6 +18,7 @@ require ( github.com/labstack/gommon v0.3.1 github.com/stretchr/testify v1.7.0 golang.org/x/crypto v0.0.0-20211117183948-ae814b36b871 + k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 ) require ( @@ -30,8 +32,6 @@ require ( github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368 // indirect - github.com/go-openapi/inflect v0.19.0 // indirect github.com/go-playground/locales v0.14.0 // indirect github.com/go-playground/universal-translator v0.18.0 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect @@ -41,7 +41,6 @@ require ( github.com/gorilla/securecookie v1.1.1 // indirect github.com/huandu/xstrings v1.3.2 // indirect github.com/imdario/mergo v0.3.12 // indirect - github.com/inconshreveable/mousetrap v1.0.0 // indirect github.com/jackc/chunkreader/v2 v2.0.1 // indirect github.com/jackc/pgconn v1.10.1 // indirect github.com/jackc/pgio v1.0.0 // indirect @@ -53,11 +52,9 @@ require ( github.com/leodido/go-urn v1.2.1 // indirect github.com/mattn/go-colorable v0.1.12 // indirect github.com/mattn/go-isatty v0.0.14 // indirect - github.com/mattn/go-runewidth v0.0.9 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect github.com/mitchellh/copystructure v1.2.0 // indirect github.com/mitchellh/reflectwalk v1.0.2 // indirect - github.com/olekukonko/tablewriter v0.0.5 // indirect github.com/pegasus-kv/thrift v0.13.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.10.0 // indirect @@ -66,22 +63,16 @@ require ( github.com/prometheus/procfs v0.6.0 // indirect github.com/sirupsen/logrus v1.6.0 // indirect github.com/spf13/cast v1.3.1 // indirect - github.com/spf13/cobra v1.1.3 // indirect - github.com/spf13/pflag v1.0.5 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.1 // indirect github.com/vmihailenco/msgpack v4.0.4+incompatible // indirect - golang.org/x/mod v0.4.2 // indirect golang.org/x/net v0.0.0-20211123203042-d83791d6bcd9 // indirect golang.org/x/sys v0.0.0-20211124211545-fe61309f8881 // indirect golang.org/x/text v0.3.7 // indirect golang.org/x/time v0.0.0-20201208040808-7e3f01d25324 // indirect - golang.org/x/tools v0.1.5 // indirect - golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect google.golang.org/appengine v1.6.1 // indirect google.golang.org/protobuf v1.26.0 // indirect gopkg.in/natefinch/lumberjack.v2 v2.0.0 // indirect gopkg.in/tomb.v2 v2.0.0-20161208151619-d5d1b5820637 // indirect gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b // indirect - k8s.io/apimachinery v0.0.0-20191123233150-4c4803ed55e3 // indirect ) diff --git a/go.sum b/go.sum index 2c3fbe9..f9187dd 100644 --- a/go.sum +++ b/go.sum @@ -140,7 +140,6 @@ github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWo github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= 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-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368 h1:WNHfSP1q2vuAa9vF54RrhCl4nqxCjVcXhlbsRXbGOSY= github.com/go-bindata/go-bindata v1.0.1-0.20190711162640-ee3c2418e368/go.mod h1:7xCgX1lzlrXPHkfvn3EhumqHkmSlzt8at9q7v0ax19c= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= @@ -151,7 +150,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9 github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= -github.com/go-openapi/inflect v0.19.0 h1:9jCH9scKIbHeV9m12SmPilScz6krDxKRasNNSNPXu/4= github.com/go-openapi/inflect v0.19.0/go.mod h1:lHpZVlpIQqLyKwJ4N+YSc9hchQy/i12fJykb83CRBH4= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= @@ -282,7 +280,6 @@ github.com/huandu/xstrings v1.3.2/go.mod h1:y5/lhBue+AyNmUVz9RLU9xbLR0o4KIIExikq github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg= github.com/imdario/mergo v0.3.12 h1:b6R2BslTbIEToALKP7LxUvijTsNI9TAe80pLWN2g/HU= github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= -github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/influxdata/influxdb1-client v0.0.0-20191209144304-8bf82d3c094d/go.mod h1:qj24IKcXYK6Iy9ceXlo3Tc+vtHo9lIhSX5JddghvEPo= github.com/jackc/chunkreader v1.0.0 h1:4s39bBR8ByfqH+DKm8rQA3E1LHZWB9XWcrz8fqaZbe0= @@ -406,7 +403,6 @@ github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Ky github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y= github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94= github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= github.com/mattn/go-sqlite3 v1.14.8/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU= github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU= @@ -449,7 +445,6 @@ github.com/oklog/oklog v0.3.2/go.mod h1:FCV+B7mhrz4o+ueLpx+KqkyXRGMWOYEvfiXtdGtb github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= github.com/olekukonko/tablewriter v0.0.0-20170122224234-a0225b3f23b5/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= @@ -567,13 +562,11 @@ github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= -github.com/spf13/cobra v1.1.3 h1:xghbfqPkxzxP3C/f3n5DdpAbdKLj4ZE4BWQI362l53M= github.com/spf13/cobra v1.1.3/go.mod h1:pGADOWyqRD/YMrPZigI/zbliZ2wVD/23d+is3pSWzOo= github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/streadway/amqp v0.0.0-20190404075320-75d898a42a94/go.mod h1:AZpEONHx3DKn8O/DFsRAY58/XVQiIPMTMB1SddzLXVw= @@ -673,7 +666,6 @@ golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKG golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -820,7 +812,6 @@ golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtn golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= -golang.org/x/tools v0.1.5 h1:ouewzE6p+/VEB31YYnTbEJdi8pFqKp4P4n85vwo3DHA= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/middleware/auth.go b/middleware/auth.go index c17adfd..8b20037 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -11,17 +11,18 @@ import ( "github.com/labstack/echo/v4" ) +// LoadAuthenticatedUser loads the authenticated user, if one, and stores in context func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { u, err := authClient.GetAuthenticatedUser(c) switch err.(type) { case *ent.NotFoundError: - c.Logger().Debug("auth user not found") + c.Logger().Warn("auth user not found") case services.NotAuthenticatedError: case nil: c.Set(context.AuthenticatedUserKey, u) - c.Logger().Info("auth user loaded in to context: %d", u.ID) + c.Logger().Infof("auth user loaded in to context: %d", u.ID) default: c.Logger().Errorf("error querying for authenticated user: %v", err) } @@ -31,41 +32,42 @@ func LoadAuthenticatedUser(authClient *services.AuthClient) echo.MiddlewareFunc } } +// LoadValidPasswordToken loads a valid password token entity that matches the user and token +// provided in path parameters +// If the token is invalid, the user will be redirected to the forgot password route +// This requires that the user owning the token is loaded in to context func LoadValidPasswordToken(authClient *services.AuthClient) echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { - var usr *ent.User - + // Extract the user parameter if c.Get(context.UserKey) == nil { - return echo.NewHTTPError(http.StatusInternalServerError, "Internal server error") + return echo.NewHTTPError(http.StatusInternalServerError) } - usr = c.Get(context.UserKey).(*ent.User) + usr := c.Get(context.UserKey).(*ent.User) - tokenParam := c.Param("password_token") - token, err := authClient.GetValidPasswordToken(c, tokenParam, usr.ID) + token, err := authClient.GetValidPasswordToken(c, c.Param("password_token"), usr.ID) switch err.(type) { case nil: + c.Set(context.PasswordTokenKey, token) + return next(c) case services.InvalidPasswordTokenError: msg.Warning(c, "The link is either invalid or has expired. Please request a new one.") return c.Redirect(http.StatusFound, c.Echo().Reverse("forgot_password")) default: c.Logger().Error(err) - return echo.NewHTTPError(http.StatusInternalServerError, "Internal server error") + return echo.NewHTTPError(http.StatusInternalServerError) } - - c.Set(context.PasswordTokenKey, token) - - return next(c) } } } +// RequireAuthentication requires that the user be authenticated in order to proceed func RequireAuthentication() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if u := c.Get(context.AuthenticatedUserKey); u == nil { - return echo.NewHTTPError(http.StatusUnauthorized, "Unauthorized") + return echo.NewHTTPError(http.StatusUnauthorized) } return next(c) @@ -73,11 +75,12 @@ func RequireAuthentication() echo.MiddlewareFunc { } } +// RequireNoAuthentication requires that the user not be authenticated in order to proceed func RequireNoAuthentication() echo.MiddlewareFunc { return func(next echo.HandlerFunc) echo.HandlerFunc { return func(c echo.Context) error { if u := c.Get(context.AuthenticatedUserKey); u != nil { - return echo.NewHTTPError(http.StatusForbidden, "Forbidden") + return echo.NewHTTPError(http.StatusForbidden) } return next(c) diff --git a/middleware/auth_test.go b/middleware/auth_test.go index f5e3d73..62fcb93 100644 --- a/middleware/auth_test.go +++ b/middleware/auth_test.go @@ -1,6 +1,7 @@ package middleware import ( + "fmt" "net/http" "testing" @@ -8,8 +9,6 @@ import ( "goweb/ent" "goweb/tests" - "github.com/labstack/echo/v4" - "github.com/stretchr/testify/require" "github.com/stretchr/testify/assert" @@ -42,9 +41,7 @@ func TestRequireAuthentication(t *testing.T) { // Not logged in err := tests.ExecuteMiddleware(ctx, RequireAuthentication()) - httpError, ok := err.(*echo.HTTPError) - require.True(t, ok) - assert.Equal(t, http.StatusUnauthorized, httpError.Code) + tests.AssertHTTPErrorCode(t, err, http.StatusUnauthorized) // Login err = c.Auth.Login(ctx, usr.ID) @@ -53,9 +50,7 @@ func TestRequireAuthentication(t *testing.T) { // Logged in err = tests.ExecuteMiddleware(ctx, RequireAuthentication()) - httpError, ok = err.(*echo.HTTPError) - require.True(t, ok) - assert.NotEqual(t, http.StatusUnauthorized, httpError.Code) + tests.AssertHTTPErrorCodeNot(t, err, http.StatusUnauthorized) } func TestRequireNoAuthentication(t *testing.T) { @@ -64,9 +59,7 @@ func TestRequireNoAuthentication(t *testing.T) { // Not logged in err := tests.ExecuteMiddleware(ctx, RequireNoAuthentication()) - httpError, ok := err.(*echo.HTTPError) - require.True(t, ok) - assert.NotEqual(t, http.StatusForbidden, httpError.Code) + tests.AssertHTTPErrorCodeNot(t, err, http.StatusForbidden) // Login err = c.Auth.Login(ctx, usr.ID) @@ -75,11 +68,44 @@ func TestRequireNoAuthentication(t *testing.T) { // Logged in err = tests.ExecuteMiddleware(ctx, RequireNoAuthentication()) - httpError, ok = err.(*echo.HTTPError) - require.True(t, ok) - assert.Equal(t, http.StatusForbidden, httpError.Code) + tests.AssertHTTPErrorCode(t, err, http.StatusForbidden) } func TestLoadValidPasswordToken(t *testing.T) { + ctx, _ := tests.NewContext(c.Web, "/") + tests.InitSession(ctx) + // Missing user context + err := tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) + tests.AssertHTTPErrorCode(t, err, http.StatusInternalServerError) + + // Add user context but no password token and expect a redirect + ctx.SetParamNames("user") + ctx.SetParamValues(fmt.Sprintf("%d", usr.ID)) + _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) + err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) + assert.NoError(t, err) + assert.Equal(t, http.StatusFound, ctx.Response().Status) + + // Add user context and invalid password token and expect a redirect + ctx.SetParamNames("user", "password_token") + ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), "faketoken") + _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) + err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) + assert.NoError(t, err) + assert.Equal(t, http.StatusFound, ctx.Response().Status) + + // Create a valid token + token, pt, err := c.Auth.GeneratePasswordResetToken(ctx, usr.ID) + require.NoError(t, err) + + // Add user and valid password token + ctx.SetParamNames("user", "password_token") + ctx.SetParamValues(fmt.Sprintf("%d", usr.ID), token) + _ = tests.ExecuteMiddleware(ctx, LoadUser(c.ORM)) + err = tests.ExecuteMiddleware(ctx, LoadValidPasswordToken(c.Auth)) + tests.AssertHTTPErrorCode(t, err, http.StatusNotFound) + ctxPt, ok := ctx.Get(context.PasswordTokenKey).(*ent.PasswordToken) + require.True(t, ok) + assert.Equal(t, pt.ID, ctxPt.ID) } diff --git a/tests/tests.go b/tests/tests.go index e512c38..1c717d6 100644 --- a/tests/tests.go +++ b/tests/tests.go @@ -6,10 +6,14 @@ import ( "net/http" "net/http/httptest" "strings" + "testing" "time" "goweb/ent" + "github.com/go-playground/assert/v2" + "github.com/stretchr/testify/require" + "k8s.io/apimachinery/pkg/util/rand" "github.com/gorilla/sessions" @@ -33,6 +37,18 @@ func ExecuteMiddleware(ctx echo.Context, mw echo.MiddlewareFunc) error { return handler(ctx) } +func AssertHTTPErrorCode(t *testing.T, err error, code int) { + httpError, ok := err.(*echo.HTTPError) + require.True(t, ok) + assert.Equal(t, code, httpError.Code) +} + +func AssertHTTPErrorCodeNot(t *testing.T, err error, code int) { + httpError, ok := err.(*echo.HTTPError) + require.True(t, ok) + assert.NotEqual(t, code, httpError.Code) +} + func CreateUser(orm *ent.Client) (*ent.User, error) { seed := fmt.Sprintf("%d-%d", time.Now().UnixMilli(), rand.IntnRange(10, 1000000)) return orm.User.