From 8c8a9e93ff252809a9d1eac33e083ac74fb3f0f1 Mon Sep 17 00:00:00 2001 From: Stavros Date: Thu, 12 Mar 2026 16:17:16 +0200 Subject: [PATCH] wip --- .../controller/context_controller_test.go | 182 ++++++------------ internal/controller/controller_test.go | 89 +++++++++ internal/controller/health_controller.go | 2 +- internal/controller/health_controller_test.go | 49 +++++ 4 files changed, 201 insertions(+), 121 deletions(-) create mode 100644 internal/controller/controller_test.go create mode 100644 internal/controller/health_controller_test.go diff --git a/internal/controller/context_controller_test.go b/internal/controller/context_controller_test.go index 5f5e6e9..e03ae0a 100644 --- a/internal/controller/context_controller_test.go +++ b/internal/controller/context_controller_test.go @@ -2,77 +2,77 @@ package controller_test import ( "encoding/json" - "net/http/httptest" + "io" + "net/http" "testing" - "github.com/steveiliop56/tinyauth/internal/config" - "github.com/steveiliop56/tinyauth/internal/controller" - "github.com/steveiliop56/tinyauth/internal/utils/tlog" - "github.com/gin-gonic/gin" + "github.com/steveiliop56/tinyauth/internal/controller" "gotest.tools/v3/assert" ) -var contextControllerCfg = controller.ContextControllerConfig{ - Providers: []controller.Provider{ - { - Name: "Local", - ID: "local", - OAuth: false, +func TestUserContextController(t *testing.T) { + // Controller setup + suite := NewControllerTest(func(router *gin.RouterGroup) *controller.ContextController { + ctrl := controller.NewContextController(contextControllerCfg, router) + ctrl.SetupRoutes() + return ctrl + }) + + // Test user context + req, err := http.NewRequest("GET", "/api/context/user", nil) + assert.NilError(t, err) + + ctx := testContext + ctx.IsLoggedIn = true + ctx.Provider = "local" + + expected, err := json.Marshal(controller.UserContextResponse{ + Status: 200, + Message: "Success", + IsLoggedIn: ctx.IsLoggedIn, + Username: ctx.Username, + Name: ctx.Name, + Email: ctx.Email, + Provider: ctx.Provider, + OAuth: ctx.OAuth, + TotpPending: ctx.TotpPending, + OAuthName: ctx.OAuthName, + }) + assert.NilError(t, err) + + resp := suite.RequestWithMiddleware(req, []gin.HandlerFunc{ + func(c *gin.Context) { + c.Set("context", &ctx) }, - { - Name: "Google", - ID: "google", - OAuth: true, - }, - }, - Title: "Test App", - AppURL: "http://localhost:8080", - CookieDomain: "localhost", - ForgotPasswordMessage: "Contact admin to reset your password.", - BackgroundImage: "/assets/bg.jpg", - OAuthAutoRedirect: "google", - WarningsEnabled: true, -} + }) -var contextCtrlTestContext = config.UserContext{ - Username: "testuser", - Name: "testuser", - Email: "test@example.com", - IsLoggedIn: true, - IsBasicAuth: false, - OAuth: false, - Provider: "local", - TotpPending: false, - OAuthGroups: "", - TotpEnabled: false, - OAuthSub: "", -} + assert.Equal(t, http.StatusOK, resp.Code) + bytes, err := io.ReadAll(resp.Body) + assert.NilError(t, err) + assert.DeepEqual(t, expected, bytes) -func setupContextController(middlewares *[]gin.HandlerFunc) (*gin.Engine, *httptest.ResponseRecorder) { - tlog.NewSimpleLogger().Init() + // Ensure user context is not available when not logged in + req, err = http.NewRequest("GET", "/api/context/user", nil) + assert.NilError(t, err) - // Setup - gin.SetMode(gin.TestMode) - router := gin.Default() - recorder := httptest.NewRecorder() + expected, err = json.Marshal(controller.UserContextResponse{ + Status: http.StatusUnauthorized, + Message: "Unauthorized", + }) + assert.NilError(t, err) - if middlewares != nil { - for _, m := range *middlewares { - router.Use(m) - } - } + resp = suite.RequestWithMiddleware(req, nil) + assert.Equal(t, 200, resp.Code) + bytes, err = io.ReadAll(resp.Body) + assert.NilError(t, err) + assert.DeepEqual(t, expected, bytes) - group := router.Group("/api") + // Test app context + req, err = http.NewRequest("GET", "/api/context/app", nil) + assert.NilError(t, err) - ctrl := controller.NewContextController(contextControllerCfg, group) - ctrl.SetupRoutes() - - return router, recorder -} - -func TestAppContextHandler(t *testing.T) { - expectedRes := controller.AppContextResponse{ + expected, err = json.Marshal(controller.AppContextResponse{ Status: 200, Message: "Success", Providers: contextControllerCfg.Providers, @@ -83,71 +83,13 @@ func TestAppContextHandler(t *testing.T) { BackgroundImage: contextControllerCfg.BackgroundImage, OAuthAutoRedirect: contextControllerCfg.OAuthAutoRedirect, WarningsEnabled: contextControllerCfg.WarningsEnabled, - } - - router, recorder := setupContextController(nil) - req := httptest.NewRequest("GET", "/api/context/app", nil) - router.ServeHTTP(recorder, req) - - assert.Equal(t, 200, recorder.Code) - - var ctrlRes controller.AppContextResponse - - err := json.Unmarshal(recorder.Body.Bytes(), &ctrlRes) - - assert.NilError(t, err) - assert.DeepEqual(t, expectedRes, ctrlRes) -} - -func TestUserContextHandler(t *testing.T) { - expectedRes := controller.UserContextResponse{ - Status: 200, - Message: "Success", - IsLoggedIn: contextCtrlTestContext.IsLoggedIn, - Username: contextCtrlTestContext.Username, - Name: contextCtrlTestContext.Name, - Email: contextCtrlTestContext.Email, - Provider: contextCtrlTestContext.Provider, - OAuth: contextCtrlTestContext.OAuth, - TotpPending: contextCtrlTestContext.TotpPending, - OAuthName: contextCtrlTestContext.OAuthName, - } - - // Test with context - router, recorder := setupContextController(&[]gin.HandlerFunc{ - func(c *gin.Context) { - c.Set("context", &contextCtrlTestContext) - c.Next() - }, }) - - req := httptest.NewRequest("GET", "/api/context/user", nil) - router.ServeHTTP(recorder, req) - - assert.Equal(t, 200, recorder.Code) - - var ctrlRes controller.UserContextResponse - - err := json.Unmarshal(recorder.Body.Bytes(), &ctrlRes) - assert.NilError(t, err) - assert.DeepEqual(t, expectedRes, ctrlRes) - // Test no context - expectedRes = controller.UserContextResponse{ - Status: 401, - Message: "Unauthorized", - IsLoggedIn: false, - } - - router, recorder = setupContextController(nil) - req = httptest.NewRequest("GET", "/api/context/user", nil) - router.ServeHTTP(recorder, req) - - assert.Equal(t, 200, recorder.Code) - - err = json.Unmarshal(recorder.Body.Bytes(), &ctrlRes) + resp = suite.RequestWithMiddleware(req, nil) + assert.Equal(t, http.StatusOK, resp.Code) + bytes, err = io.ReadAll(resp.Body) assert.NilError(t, err) - assert.DeepEqual(t, expectedRes, ctrlRes) + assert.DeepEqual(t, expected, bytes) } diff --git a/internal/controller/controller_test.go b/internal/controller/controller_test.go new file mode 100644 index 0000000..510e789 --- /dev/null +++ b/internal/controller/controller_test.go @@ -0,0 +1,89 @@ +package controller_test + +import ( + "net/http" + "net/http/httptest" + + "github.com/gin-gonic/gin" + "github.com/steveiliop56/tinyauth/internal/config" + "github.com/steveiliop56/tinyauth/internal/controller" +) + +// Testing suite + +type ControllerTest[T any] struct { + ctrlSetup func(router *gin.RouterGroup) T +} + +func NewControllerTest[T any](setup func(router *gin.RouterGroup) T) *ControllerTest[T] { + return &ControllerTest[T]{ctrlSetup: setup} +} + +func (ctrlt *ControllerTest[T]) newEngine(middlewares []gin.HandlerFunc) *gin.Engine { + gin.SetMode(gin.TestMode) + + engine := gin.New() + + for _, mw := range middlewares { + engine.Use(mw) + } + + return engine +} + +func (ctrlrt *ControllerTest[T]) newControllerInstance(engine *gin.Engine) T { + ctrl := ctrlrt.ctrlSetup(engine.Group("/api")) + return ctrl +} + +func (ctrlt *ControllerTest[T]) RequestWithMiddleware(http *http.Request, middlewares []gin.HandlerFunc) *httptest.ResponseRecorder { + engine := ctrlt.newEngine(middlewares) + ctrlt.newControllerInstance(engine) + recorder := httptest.NewRecorder() + engine.ServeHTTP(recorder, http) + return recorder +} + +func (ctrlt *ControllerTest[T]) Request(http *http.Request) *httptest.ResponseRecorder { + return ctrlt.RequestWithMiddleware(http, nil) +} + +// Controller configs + +var contextControllerCfg = controller.ContextControllerConfig{ + Providers: []controller.Provider{ + { + Name: "Local", + ID: "local", + OAuth: false, + }, + { + Name: "Google", + ID: "google", + OAuth: true, + }, + }, + Title: "Tinyauth Testing", + AppURL: "http://tinyauth.example.com:3000", + CookieDomain: "example.com", + ForgotPasswordMessage: "Foo bar", + BackgroundImage: "/background.jpg", + OAuthAutoRedirect: "google", + WarningsEnabled: true, +} + +var testContext = config.UserContext{ + Username: "user", + Name: "User", + Email: "user@example.com", + IsLoggedIn: false, + IsBasicAuth: false, + OAuth: false, + Provider: "", + TotpPending: false, + OAuthGroups: "group1,group2", + TotpEnabled: false, + OAuthName: "test", + OAuthSub: "test", + LdapGroups: "group1,group2", +} diff --git a/internal/controller/health_controller.go b/internal/controller/health_controller.go index 8ad67b5..1b9adbf 100644 --- a/internal/controller/health_controller.go +++ b/internal/controller/health_controller.go @@ -19,7 +19,7 @@ func (controller *HealthController) SetupRoutes() { func (controller *HealthController) healthHandler(c *gin.Context) { c.JSON(200, gin.H{ - "status": "ok", + "status": 200, "message": "Healthy", }) } diff --git a/internal/controller/health_controller_test.go b/internal/controller/health_controller_test.go new file mode 100644 index 0000000..edf0dce --- /dev/null +++ b/internal/controller/health_controller_test.go @@ -0,0 +1,49 @@ +package controller_test + +import ( + "encoding/json" + "io" + "net/http" + "testing" + + "github.com/gin-gonic/gin" + "github.com/steveiliop56/tinyauth/internal/controller" + "gotest.tools/v3/assert" +) + +func TestHealthController(t *testing.T) { + // Controller setup + suite := NewControllerTest(func(router *gin.RouterGroup) *controller.HealthController { + ctrl := controller.NewHealthController(router) + ctrl.SetupRoutes() + return ctrl + }) + + expected, err := json.Marshal(map[string]any{ + "status": 200, + "message": "Healthy", + }) + assert.NilError(t, err) + + // Test we are healthy with GET + req, err := http.NewRequest("GET", "/api/healthz", nil) + assert.NilError(t, err) + + resp := suite.Request(req) + + assert.Equal(t, http.StatusOK, resp.Code) + bytes, err := io.ReadAll(resp.Body) + assert.NilError(t, err) + assert.DeepEqual(t, bytes, expected) + + // Test we are healthy with HEAD + req, err = http.NewRequest("HEAD", "/api/healthz", nil) + assert.NilError(t, err) + + resp = suite.Request(req) + + assert.Equal(t, http.StatusOK, resp.Code) + bytes, err = io.ReadAll(resp.Body) + assert.NilError(t, err) + assert.DeepEqual(t, expected, bytes) +}