I cant really figure out why I’m keep on getting StatusSeeOther for other errors which is supposed to throw StatusUnauthorized. Can someone help on this?
This is my Handler:
func (h *UserHandler) HandleIndex(w http.ResponseWriter, r *http.Request) {
// Check for the “Authorization” cookie
cookie, err := r.Cookie(“Authorization”)
if err != nil || cookie.Value == “” {
http.Redirect(w, r, loginRoute, http.StatusFound)
return
}
// Verify the JWT token from the cookie
token, err := jwt.Parse(cookie.Value, func(token *jwt.Token) (interface{}, error) {
return []byte(os.Getenv("JWTSECRET")), nil
})
if err != nil || !token.Valid {
http.Redirect(w, r, loginRoute, http.StatusFound)
return
}
// Extract user ID from token claims
claims, ok := token.Claims.(jwt.MapClaims)
if !ok || !token.Valid {
http.Error(w, "Unauthorized user INDEX", http.StatusUnauthorized)
return
}
userID, ok := claims["sub"].(float64)
if !ok {
http.Error(w, "Invalid user ID", http.StatusUnauthorized)
return
}
// Retrieve the full user object from the database
user, err := h.Repo.RetrieveUserObject(int(userID))
if err != nil {
log.Println("Error fetching user:", err)
http.Error(w, "Unable to fetch user", http.StatusInternalServerError)
return
}
// Query the user's uploaded songs and playlists
songs, err := h.Repo.QueryUserSong(user)
if err != nil {
log.Println("Error fetching user songs:", err)
http.Error(w, "Unable to fetch songs", http.StatusInternalServerError)
return
}
playlists, err := h.Repo.QueryUserPlaylist(user)
if err != nil {
log.Println("Error fetching user playlists:", err)
http.Error(w, "Unable to fetch playlists", http.StatusInternalServerError)
return
}
// Pass user, songs, and playlists to the template
tmpl, err := template.ParseFiles("static/index.html")
if err != nil {
log.Printf("Error parsing index template: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := struct {
User models.RegisteredUser
Songs []models.Song
Playlists []models.Playlist
}{
User: user,
Songs: songs,
Playlists: playlists,
}
// Execute the template with the correct data structure
if err := tmpl.Execute(w, data); err != nil {
log.Printf("Error executing template: %v", err)
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
This the unit test for it:
func TestHandleIndex(t *testing.T) {
tests := struct {
name string
setupRequest func() *http.Request
setupRepoMocks func(repo *repomock.MockUserRepository)
expectedStatusCode int
expectedRedirect string
expectedError string
}{
{
name: “Missing Cookie”,
setupRequest: func() *http.Request {
return httptest.NewRequest(http.MethodGet, “/”, nil)
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {},
expectedStatusCode: http.StatusFound,
expectedRedirect: “/login”,
},
{
name: “Empty Cookie Value”,
setupRequest: func() *http.Request {
req := httptest.NewRequest(http.MethodGet, “/”, nil)
req.AddCookie(&http.Cookie{Name: “Authorization”, Value: “”})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {},
expectedStatusCode: http.StatusFound,
expectedRedirect: “/login”,
},
{
name: “Invalid JWT Token”,
setupRequest: func() *http.Request {
req := httptest.NewRequest(http.MethodGet, “/”, nil)
req.AddCookie(&http.Cookie{Name: “Authorization”, Value: “invalid-token”})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {},
expectedStatusCode: http.StatusFound,
expectedRedirect: “/login”,
},
{
name: "Valid JWT with No Songs or Playlists",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{ID: 1, Email: "test@example.com"}, nil)
repo.On("QueryUserSong", mock.Anything).Return([]models.Song{}, nil)
repo.On("QueryUserPlaylist", mock.Anything).Return([]models.Playlist{}, nil)
},
expectedStatusCode: 302,
expectedError: "", // No error expected
},
{
name: "Missing JWT Secret",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("WRONGSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {},
expectedStatusCode: http.StatusFound,
expectedRedirect: "/login",
},
{
name: "Invalid User ID Claim",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": "not-a-number",
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {},
expectedStatusCode: http.StatusFound,
expectedError: "<a href=\"/login\">Found</a>.\n\n",
},
{
name: "User Retrieval Error",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{}, errors.New("user retrieval error"))
},
expectedStatusCode: http.StatusFound,
expectedError: "<a href=\"/login\">Found</a>.\n\n",
},
{
name: "Song Query Error",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{ID: 1, Email: "test@example.com"}, nil)
repo.On("QueryUserSong", mock.Anything).Return(nil, errors.New("song query error"))
},
expectedStatusCode: http.StatusFound,
expectedError: "<a href=\"/login\">Found</a>.\n\n",
},
{
name: "Playlist Query Error",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{ID: 1, Email: "test@example.com"}, nil)
repo.On("QueryUserSong", mock.Anything).Return([]models.Song{{ID: 1, Name: "Song 1"}}, nil)
repo.On("QueryUserPlaylist", mock.Anything).Return(nil, errors.New("playlist query error"))
},
expectedStatusCode: http.StatusFound,
expectedError: "<a href=\"/login\">Found</a>.\n\n",
},
{
name: "Template Parsing Error",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{ID: 1, Email: "test@example.com"}, nil)
repo.On("QueryUserSong", mock.Anything).Return([]models.Song{{ID: 1, Name: "Song 1"}}, nil)
repo.On("QueryUserPlaylist", mock.Anything).Return([]models.Playlist{{ID: 1, Name: "Playlist 1"}}, nil)
},
expectedStatusCode: http.StatusFound,
expectedError: "<a href=\"/login\">Found</a>.\n\n",
},
{
name: "Success",
setupRequest: func() *http.Request {
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"sub": 1.0,
})
tokenString, _ := token.SignedString([]byte("JWTSECRET"))
req := httptest.NewRequest(http.MethodGet, "/", nil)
req.AddCookie(&http.Cookie{Name: "Authorization", Value: tokenString})
return req
},
setupRepoMocks: func(repo *repomock.MockUserRepository) {
repo.On("RetrieveUserObject", 1).Return(models.RegisteredUser{ID: 1, Email: "test@example.com"}, nil)
repo.On("QueryUserSong", mock.Anything).Return([]models.Song{{ID: 1, Name: "Song 1"}}, nil)
repo.On("QueryUserPlaylist", mock.Anything).Return([]models.Playlist{{ID: 1, Name: "Playlist 1"}}, nil)
},
expectedStatusCode: http.StatusFound,
},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
// Mock repository
mockRepo := &repomock.MockUserRepository{}
tc.setupRepoMocks(mockRepo)
// Create handler
handler := UserHandler{Repo: mockRepo}
// Setup request and response recorder
req := tc.setupRequest()
rr := httptest.NewRecorder()
// Call the handler
handler.HandleIndex(rr, req)
// Assert status code
assert.Equal(t, tc.expectedStatusCode, rr.Code)
// Assert redirects
if tc.expectedRedirect != "" {
assert.Equal(t, tc.expectedRedirect, rr.Header().Get("Location"))
}
// Assert error messages
if tc.expectedError != "" {
assert.Contains(t, rr.Body.String(), tc.expectedError)
}
})
}
}