hako/internal/server/handlers/auth_test.go

332 lines
12 KiB
Go

package handlers
import (
"context"
"encoding/json"
"net/http"
"testing"
archivalDomain "git.nakama.town/fmartingr/hako/internal/archival/domain"
authDomain "git.nakama.town/fmartingr/hako/internal/auth/domain"
"git.nakama.town/fmartingr/hako/internal/server/middleware"
"git.nakama.town/fmartingr/hako/internal/testutil"
"github.com/stretchr/testify/require"
)
func TestHandleLogin_Success(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user first
_, _, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create login request
loginReq := map[string]string{
"email": "test@example.com",
"password": "testpassword123",
}
body, _ := json.Marshal(loginReq)
// Perform request
w := testutil.PerformRequest(authHandler.HandleLogin, http.MethodPost, "/api/v1/auth/login",
testutil.WithBody(string(body)),
testutil.WithHeader("Content-Type", "application/json"),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
// Check response structure
tokenValue := resp.AssertJSONContainsKey(t, "token")
require.NotEmpty(t, tokenValue)
userValue := resp.AssertJSONContainsKey(t, "user")
userMap, ok := userValue.(map[string]any)
require.True(t, ok)
require.Equal(t, "test@example.com", userMap["email"])
// Verify role is returned (should default to "user")
require.Equal(t, "user", userMap["role"])
}
func TestHandleLogin_InvalidCredentials(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user first
_, _, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create login request with wrong password
loginReq := map[string]string{
"email": "test@example.com",
"password": "wrongpassword",
}
body, _ := json.Marshal(loginReq)
// Perform request
w := testutil.PerformRequest(authHandler.HandleLogin, http.MethodPost, "/api/v1/auth/login",
testutil.WithBody(string(body)),
testutil.WithHeader("Content-Type", "application/json"),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusUnauthorized)
resp.AssertBodyContains(t, "Invalid credentials")
}
func TestHandleLogin_MissingFields(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Test with empty email
loginReq := map[string]string{
"email": "",
"password": "testpassword123",
}
body, _ := json.Marshal(loginReq)
w := testutil.PerformRequest(authHandler.HandleLogin, http.MethodPost, "/api/v1/auth/login",
testutil.WithBody(string(body)),
testutil.WithHeader("Content-Type", "application/json"),
)
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusBadRequest)
resp.AssertBodyContains(t, "Email and password are required")
// Test with empty password
loginReq = map[string]string{
"email": "test@example.com",
"password": "",
}
body, _ = json.Marshal(loginReq)
w = testutil.PerformRequest(authHandler.HandleLogin, http.MethodPost, "/api/v1/auth/login",
testutil.WithBody(string(body)),
testutil.WithHeader("Content-Type", "application/json"),
)
resp = testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusBadRequest)
resp.AssertBodyContains(t, "Email and password are required")
}
func TestHandleLogin_InvalidMethod(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Try GET instead of POST
w := testutil.PerformRequest(authHandler.HandleLogin, http.MethodGet, "/api/v1/auth/login")
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusMethodNotAllowed)
resp.AssertBodyContains(t, "Method not allowed")
}
func TestHandleGetCurrentUser_Success(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test user and get token
user, token, err := testutil.NewTestUser(t, deps.Dependencies, "test@example.com", "testpassword123")
require.NoError(t, err)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create handlers with new middleware pattern
// Global auth middleware sets context if token is present
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
// Require auth middleware blocks if not authenticated
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(authHandler.HandleGetCurrentUser)))
// Perform authenticated request
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/auth/me",
testutil.WithAuthToken(token),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
// Check response structure
emailValue := resp.AssertJSONContainsKey(t, "email")
require.Equal(t, user.Email, emailValue)
// Verify role is returned
roleValue := resp.AssertJSONContainsKey(t, "role")
require.Equal(t, "user", roleValue)
}
func TestHandleGetCurrentUser_Unauthorized(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create handlers with new middleware pattern
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(authHandler.HandleGetCurrentUser)))
// Perform request without token
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/auth/me")
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusUnauthorized)
resp.AssertBodyContains(t, "Authorization required")
}
func TestHandleGetCurrentUser_InvalidToken(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create handlers with new middleware pattern
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(authHandler.HandleGetCurrentUser)))
// Perform request with invalid token
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/auth/me",
testutil.WithAuthToken("invalid-token"),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusUnauthorized)
resp.AssertBodyContains(t, "Authorization required")
}
func TestHandleGetCurrentUser_InvalidMethod(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Try POST instead of GET
w := testutil.PerformRequest(authHandler.HandleGetCurrentUser, http.MethodPost, "/api/v1/auth/me")
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusMethodNotAllowed)
resp.AssertBodyContains(t, "Method not allowed")
}
func TestHandleLogin_ReturnsRole(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test admin user
_, _, err := testutil.NewTestAdminUser(t, deps.Dependencies, "admin@example.com", "adminpassword123")
require.NoError(t, err)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create login request
loginReq := map[string]string{
"email": "admin@example.com",
"password": "adminpassword123",
}
body, _ := json.Marshal(loginReq)
// Perform request
w := testutil.PerformRequest(authHandler.HandleLogin, http.MethodPost, "/api/v1/auth/login",
testutil.WithBody(string(body)),
testutil.WithHeader("Content-Type", "application/json"),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
// Check response structure
userValue := resp.AssertJSONContainsKey(t, "user")
userMap, ok := userValue.(map[string]any)
require.True(t, ok)
require.Equal(t, "admin@example.com", userMap["email"])
require.Equal(t, "admin", userMap["role"])
}
func TestHandleGetCurrentUser_ReturnsRole(t *testing.T) {
ctx := context.Background()
deps := testutil.GetTestConfigurationAndDependencies(t, ctx)
// Initialize domains
deps.Dependencies.Domains().SetLinks(archivalDomain.NewLinkDomain(deps.Dependencies))
deps.Dependencies.Domains().SetArchives(archivalDomain.NewArchiveDomain(deps.Dependencies))
deps.Dependencies.Domains().SetCategories(archivalDomain.NewCategoryDomain(deps.Dependencies))
deps.Dependencies.Domains().SetAuth(authDomain.NewAuthDomain(deps.Dependencies))
// Create a test admin user and get token
user, token, err := testutil.NewTestAdminUser(t, deps.Dependencies, "admin@example.com", "adminpassword123")
require.NoError(t, err)
// Create auth handler
authHandler := NewAuthHandler(deps.Dependencies)
// Create handlers with new middleware pattern
globalAuthMiddleware := middleware.AuthMiddleware(deps.Dependencies.GetJWTService(), deps.Dependencies.UserStore)
requireAuthMiddleware := middleware.RequireAuthMiddleware()
handler := globalAuthMiddleware(requireAuthMiddleware(http.HandlerFunc(authHandler.HandleGetCurrentUser)))
// Perform authenticated request
w := testutil.PerformRequestWithHandler(handler, http.MethodGet, "/api/v1/auth/me",
testutil.WithAuthToken(token),
)
// Assert response
resp := testutil.NewTestResponse(w)
resp.AssertStatus(t, http.StatusOK)
// Check response structure
emailValue := resp.AssertJSONContainsKey(t, "email")
require.Equal(t, user.Email, emailValue)
// Verify admin role is returned
roleValue := resp.AssertJSONContainsKey(t, "role")
require.Equal(t, "admin", roleValue)
}