package middleware_test import ( "net/http" "net/http/httptest" "path/filepath" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/fx" "git.eeqj.de/sneak/upaas/internal/config" "git.eeqj.de/sneak/upaas/internal/database" "git.eeqj.de/sneak/upaas/internal/globals" "git.eeqj.de/sneak/upaas/internal/logger" "git.eeqj.de/sneak/upaas/internal/middleware" "git.eeqj.de/sneak/upaas/internal/models" "git.eeqj.de/sneak/upaas/internal/service/auth" ) // setupMiddleware creates a Middleware with a real SQLite database for // integration testing. func setupMiddleware(t *testing.T) (*middleware.Middleware, *auth.Service, *database.Database) { t.Helper() tmpDir := t.TempDir() globals.SetAppname("upaas-test") globals.SetVersion("test") globalsInst, err := globals.New(fx.Lifecycle(nil)) require.NoError(t, err) loggerInst, err := logger.New( fx.Lifecycle(nil), logger.Params{Globals: globalsInst}, ) require.NoError(t, err) cfg := &config.Config{ Port: 8080, DataDir: tmpDir, SessionSecret: "test-secret-key-at-least-32-chars!!", } _ = filepath.Join(tmpDir, "upaas.db") dbInst, err := database.New(fx.Lifecycle(nil), database.Params{ Logger: loggerInst, Config: cfg, }) require.NoError(t, err) authSvc, err := auth.New(fx.Lifecycle(nil), auth.ServiceParams{ Logger: loggerInst, Config: cfg, Database: dbInst, }) require.NoError(t, err) mw, err := middleware.New(fx.Lifecycle(nil), middleware.Params{ Logger: loggerInst, Globals: globalsInst, Config: cfg, Auth: authSvc, Database: dbInst, }) require.NoError(t, err) return mw, authSvc, dbInst } func TestAPISessionAuth_BearerTokenSetsUserContext(t *testing.T) { t.Parallel() mw, authSvc, dbInst := setupMiddleware(t) ctx := t.Context() // Create a user. user, err := authSvc.CreateUser(ctx, "testuser", "password123") require.NoError(t, err) require.NotNil(t, user) // Create an API token for the user. rawToken, err := models.GenerateToken() require.NoError(t, err) tokenHash := database.HashAPIToken(rawToken) apiToken := models.NewAPIToken(dbInst) apiToken.UserID = user.ID apiToken.Name = "test-token" apiToken.TokenHash = tokenHash err = apiToken.Save(ctx) require.NoError(t, err) // Build a handler behind APISessionAuth that checks user context. var gotUser *models.User var getUserErr error handler := mw.APISessionAuth()(http.HandlerFunc( func(w http.ResponseWriter, r *http.Request) { gotUser, getUserErr = authSvc.GetCurrentUser(r.Context(), r) w.WriteHeader(http.StatusOK) }, )) // Make request with bearer token. req := httptest.NewRequest(http.MethodGet, "/api/test", nil) req.Header.Set("Authorization", "Bearer "+rawToken) rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code) require.NoError(t, getUserErr) require.NotNil(t, gotUser, "GetCurrentUser should return the user for bearer auth") assert.Equal(t, user.ID, gotUser.ID) assert.Equal(t, "testuser", gotUser.Username) } func TestAPISessionAuth_NoBearerTokenReturns401(t *testing.T) { t.Parallel() mw, _, _ := setupMiddleware(t) handler := mw.APISessionAuth()(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }, )) req := httptest.NewRequest(http.MethodGet, "/api/test", nil) rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusUnauthorized, rec.Code) } func TestAPISessionAuth_InvalidBearerTokenReturns401(t *testing.T) { t.Parallel() mw, _, _ := setupMiddleware(t) handler := mw.APISessionAuth()(http.HandlerFunc( func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) }, )) req := httptest.NewRequest(http.MethodGet, "/api/test", nil) req.Header.Set("Authorization", "Bearer invalid-token") rec := httptest.NewRecorder() handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusUnauthorized, rec.Code) }