Search code examples
gomiddlewarego-httpgo-testing

Middleware HTTP test passing when it shouldn't


I've written some middleware that checks to make sure a JWT token in valid:

func JwtVerify(next http.Handler) http.Handler {
    return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {

        //Get the token from the header
        header := r.Header.Get("Authorization")

        //If Authorization is empty, return a 403
        if header == "" {
            rw.WriteHeader(http.StatusForbidden)
            json.NewEncoder(rw).Encode(ErrorMsg{ Message: "Missing Authentication Token" })
            return
        }

        header = strings.Split(header, "Bearer ")[1]

        token, err := jwt.Parse(header, func(token *jwt.Token) (i interface{}, err error) {
            if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
                 return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
            }
            // TODO: REMOVE THE SECRET
            return []byte("MyTestSecret"), nil
        })

       // Return the error
        if err != nil {
            rw.WriteHeader(http.StatusForbidden)
            json.NewEncoder(rw).Encode(ErrorMsg{Message: err.Error()})
            return
        }

        if token.Valid {
            log.Println("JWT Token is valid")
            next.ServeHTTP(rw, r)
        }
    })
}

I've Tried to write some tests for this, but I just can grasp where I'm going wrong, the test should fail because the token is invalid but its still passing:

func TestJwtVerify(t *testing.T) {
    token := "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpYXQiOjE1ODcwNTIzMTcsImV4cCI6MTU4NzA1MjkxNywic2Vzc2lvbi1kYXRhIjoiVGVzdC5NY1Rlc3RGYWNlQG1haWwuY29tIn0.f0oM4fSH_b1Xi5zEF0VK-t5uhpVidk5HY1O0EGR4SQQ"
    req, err := http.NewRequest("GET", "/jwt", nil)
    if err != nil {
        t.Fatal(err)
    }
    req.Header.Set("Authorization", token)

    testHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        if r.Response.StatusCode == 403 {
            t.Fatalf("Response should be 200 for a valid JWT Token")
        }
    })

    rw := httptest.NewRecorder()
    handler := JwtVerify(testHandler)
    handler.ServeHTTP(rw, req)
}

Can someone explain where I'm going wrong?


Solution

  • You've got a couple of issues.

    First, your test provides a testHandler function which should run on any errors, but your JwtVerify function only runs the next handler if the token is valid:

    if token.Valid {
        log.Println("JWT Token is valid")
        next.ServeHTTP(rw, r)
    }
    

    so you've made sure testHandler will never be run when the token is invalid, and therefore your test will not fail.

    Your second problem is here:

    if r.Response.StatusCode == 403 {
        t.Fatalf("Response should be 200 for a valid JWT Token")
    }
    

    Per the documentation for http.Request:

    Response is the redirect response which caused this request to be created. This field is only populated during client redirects.

    and since this request is not in response to a client redirect, the Response field will not be populated, and in particular the status code will not be determined by the headers you wrote to your http.ResponseWriter. In general you can't obtain the status code in this way, and the normal way would be to have an earlier middleware wrap the http.ResponseWriter, and obtain the status from that wrapper writer after the middleware that wrote the status returns.

    As mentioned in a comment, the right way to go about this is to examine your httptest.ResponseRecorder for the status code, since its purpose is to examine HTTP responses during tests.