0

I am attempting to test a golang API built on the echo framework/router. I have the following test.....

func TestLogout(t *testing.T) {
loadConfig()
db := stubDBs(t)
Convey("When you post to /logout", t, func() {
    Convey("with a valid token, you should get aa success msg and be logged out", func() {
        e := echo.New()
        e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
            SigningKey:  []byte("secret"),
            TokenLookup: "query:user",
        }))
        req, err := http.NewRequest(echo.POST, "auth/logout", strings.NewReader(""))
        if err == nil {
            req.Header.Set(echo.HeaderAuthorization, fmt.Sprintf("Bearer %v", Token))
            rec := httptest.NewRecorder()
            c := e.NewContext(req, rec)
            Logout(db, Models.Redis)(c)
            body := GetJsonBody(rec.Body.String())
            error := body.Path("error").Data()
            msg := body.Path("error").Data().(string)
            pw := body.Path("data.user.password").Data().(string)
            token := body.Path("data.user.token").Data()
            So(error, ShouldEqual, nil)
            So(msg, ShouldEqual, "Logged Out")
            So(rec.Code, ShouldEqual, 200)
            So(pw, ShouldEqual, "")
            So(token, ShouldEqual, nil)
        }

    })
})
}

and in the controller....

//Logout ...
func Logout(db *sqlx.DB, r *redis.Client) echo.HandlerFunc {
    return func(c echo.Context) error {
        var user Models.User
        log.Println(c.Get("user")) //<-----This Logs NIL only on testing
        if c.Get("user") == nil {
            res := createAuthErrorResponse(user, "Invalid Token")
            return c.JSON(http.StatusOK, res)
        }
        token := c.Get("user").(*jwt.Token)
        err := Models.Redis.Del(token.Raw).Err()
        if err != nil {
            handleErr(err)
            res := createAuthErrorResponse(user, "Token not in storage")
            return c.JSON(http.StatusOK, res)
        }
        user.Password = ""
        user.Token = null.StringFrom("")
        res := createAuthSuccessResponse(user, "Logged Out.")
        return c.JSON(http.StatusOK, res)
    }
}

Any advice on how I can test this functionality in the cho framework? This is a behavior I would not expect (c.Get("user") does not act the same on testing as in live env).

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
Chris L
  • 1,051
  • 1
  • 7
  • 20
  • it is the default access method using the echo jwt middleware https://echo.labstack.com/cookbook/jwt – Chris L Apr 07 '17 at 17:44
  • Also, if it is any help to anyone. log.Println(c.Request().Header) DOES have the header info in it for the test, is is just not able to be accessed by c.Get() – Chris L Apr 07 '17 at 17:47

1 Answers1

2

Through much pain I was able to figure this out. Here is working code. Explanation to follow.

        Convey("with a valid token, you should get a success msg and be logged out", func() {
        e := echo.New()
        req, err := http.NewRequest(echo.POST, "auth/logout", strings.NewReader(""))
        if err == nil {
            req.Header.Set(echo.HeaderAuthorization, fmt.Sprintf("Bearer %v", Token))
            rec := httptest.NewRecorder()
            c := e.NewContext(req, rec)
            middleware.JWTWithConfig(middleware.JWTConfig{
                SigningKey: []byte("secret"),
            })(Logout(db, Models.Redis))(c)
            body := GetJsonBody(rec.Body.String())
            error := body.Path("error").Data()
            msg := body.Path("msg").Data().(string)
            pw := body.Path("data.user.password").Data().(string)
            token := body.Path("data.user.token").Data()
            So(error, ShouldEqual, nil)
            So(msg, ShouldEqual, "Logged Out")
            So(rec.Code, ShouldEqual, 200)
            So(pw, ShouldEqual, "")
            So(token, ShouldEqual, nil)
        }

    })

I had set my Header properly but was not executing the JWT echo middleware which meant that my token was not validated and made accesisible using the c.Get() command.

The key here is that testing echo middlware without running the server requires you to execute the middleware function, which then takes a handler function (this is your function you want to test) and then takes the context. So, the function should look as follows to test your middleware routes. If it is all caps you need to replace it with your specific case.

A: middleware.MIDDLEWARE_FUNC(NECESSARY_CONFIGS) // <--- this returns echo.MiddlewareFunc

B: (YOUR_HANDLER(req, res)) // <---echo.MiddlewareFunc takes an echo.HanlderFunc as its only argument and you pass your handler you test req, res

(c) // <--Finally, you call the resulting compose function with your test context

A(B())(c)
Chris L
  • 1,051
  • 1
  • 7
  • 20