I've run up against an "invalid memory address or nil pointer dereference" error while writing unit tests for my http handlers--specifically, authentication middleware.
Here is the function to be tested:
func Authenticate(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
var err error
ck, err := req.Cookie("session")
if err != nil {
w.Header().Add("Authorization", "expired/invalid")
next.ServeHTTP(w, req)
return
}
r, err := env.Db.VerifySession(ck.Value)
if err != nil {
http.Error(w, http.StatusText(500), http.StatusInternalServerError)
return
}
if r == "expired/invalid" {
ck.MaxAge = -1
http.SetCookie(w, ck)
w.Header().Add("Authorization", r)
next.ServeHTTP(w, req)
return
}
w.Header().Add("Authorization", r)
next.ServeHTTP(w, req)
})
}
The specific test case on which the error is thrown:
rec = httptest.NewRecorder()
req, _ = http.NewRequest("GET", "/", nil)
c := &http.Cookie{
Name: "session",
Value: "success",
}
req.AddCookie(c)
routes.Authenticate(http.NotFoundHandler()).ServeHTTP(rec, req)
assert.Equal("admin", rec.Header().Get("Authorization"))
The mocked version of env.DB.VerifySession used for testing:
func (mdb *mockdb) VerifySession(val string) (string, error) {
switch val {
case "success":
return "admin", nil
case "failure":
return "expired/invalid", nil
default:
return "", errors.New("500")
}
}
I have written a passing test for the the first part of Authenticate(): if no "session" cookie is found, the test confirms that the response header's been appropriately updated. However, when I pass in a "session" cookie, as with the test case above, Authenticate() will run up tor, err := env.Db.VerifySession(ck.Value)
. Attempting to step over or into that function call in debug mode results in the nil pointer error being thrown.
I don't think the problem is the env.Db variable, as I've successfully run a suite of tests on my non-middleware handlers using the same variable without issue.
I can also confirm through debugging that the "session" cookie does actually exist.
Any guidance on what's going on here would be greatly appreciated.
Nevermind, I realized what the issue is. For my other routes I pass the env variable in as an argument. I need to find a way to provide the middleware with access to it as well.
I suppose I'll define the middleware as a method on env.