mirror of
https://github.com/steveiliop56/tinyauth.git
synced 2026-03-25 16:07:54 +00:00
Compare commits
6 Commits
refactor/t
...
refactor/o
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ae16d6bdc | ||
|
|
db73c56dfe | ||
|
|
4a85a9d010 | ||
|
|
7bead41ae9 | ||
|
|
2491d453cf | ||
|
|
1a1712eaeb |
2
.github/workflows/release.yml
vendored
2
.github/workflows/release.yml
vendored
@@ -15,6 +15,8 @@ jobs:
|
|||||||
steps:
|
steps:
|
||||||
- name: Checkout
|
- name: Checkout
|
||||||
uses: actions/checkout@v4
|
uses: actions/checkout@v4
|
||||||
|
with:
|
||||||
|
ref: nightly
|
||||||
|
|
||||||
- name: Generate metadata
|
- name: Generate metadata
|
||||||
id: metadata
|
id: metadata
|
||||||
|
|||||||
@@ -37,7 +37,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@tanstack/eslint-plugin-query": "^5.91.4",
|
"@tanstack/eslint-plugin-query": "^5.91.4",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.4.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^5.1.4",
|
"@vitejs/plugin-react": "^5.1.4",
|
||||||
@@ -417,7 +417,7 @@
|
|||||||
|
|
||||||
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
"@types/ms": ["@types/ms@2.1.0", "", {}, "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA=="],
|
||||||
|
|
||||||
"@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="],
|
"@types/node": ["@types/node@25.4.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-9wLpoeWuBlcbBpOY3XmzSTG3oscB6xjBEEtn+pYXTfhyXhIxC5FsBer2KTopBlvKEiW9l13po9fq+SJY/5lkhw=="],
|
||||||
|
|
||||||
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
"@types/react": ["@types/react@19.2.14", "", { "dependencies": { "csstype": "^3.2.2" } }, "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w=="],
|
||||||
|
|
||||||
|
|||||||
@@ -43,7 +43,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^10.0.1",
|
"@eslint/js": "^10.0.1",
|
||||||
"@tanstack/eslint-plugin-query": "^5.91.4",
|
"@tanstack/eslint-plugin-query": "^5.91.4",
|
||||||
"@types/node": "^25.5.0",
|
"@types/node": "^25.4.0",
|
||||||
"@types/react": "^19.2.14",
|
"@types/react": "^19.2.14",
|
||||||
"@types/react-dom": "^19.2.3",
|
"@types/react-dom": "^19.2.3",
|
||||||
"@vitejs/plugin-react": "^5.1.4",
|
"@vitejs/plugin-react": "^5.1.4",
|
||||||
|
|||||||
@@ -2,153 +2,152 @@ package controller_test
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
"net/http/httptest"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/steveiliop56/tinyauth/internal/config"
|
"github.com/steveiliop56/tinyauth/internal/config"
|
||||||
"github.com/steveiliop56/tinyauth/internal/controller"
|
"github.com/steveiliop56/tinyauth/internal/controller"
|
||||||
"github.com/steveiliop56/tinyauth/internal/utils"
|
"github.com/steveiliop56/tinyauth/internal/utils/tlog"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
"gotest.tools/v3/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestContextController(t *testing.T) {
|
var contextControllerCfg = controller.ContextControllerConfig{
|
||||||
controllerConfig := controller.ContextControllerConfig{
|
Providers: []controller.Provider{
|
||||||
Providers: []controller.Provider{
|
|
||||||
{
|
|
||||||
Name: "Local",
|
|
||||||
ID: "local",
|
|
||||||
OAuth: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Title: "Tinyauth",
|
|
||||||
AppURL: "https://tinyauth.example.com",
|
|
||||||
CookieDomain: "example.com",
|
|
||||||
ForgotPasswordMessage: "foo",
|
|
||||||
BackgroundImage: "/background.jpg",
|
|
||||||
OAuthAutoRedirect: "none",
|
|
||||||
WarningsEnabled: true,
|
|
||||||
}
|
|
||||||
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
middlewares []gin.HandlerFunc
|
|
||||||
expected string
|
|
||||||
path string
|
|
||||||
}{
|
|
||||||
{
|
{
|
||||||
description: "Ensure context controller returns app context",
|
Name: "Local",
|
||||||
middlewares: []gin.HandlerFunc{},
|
ID: "local",
|
||||||
path: "/api/context/app",
|
OAuth: false,
|
||||||
expected: func() string {
|
|
||||||
expectedAppContextResponse := controller.AppContextResponse{
|
|
||||||
Status: 200,
|
|
||||||
Message: "Success",
|
|
||||||
Providers: controllerConfig.Providers,
|
|
||||||
Title: controllerConfig.Title,
|
|
||||||
AppURL: controllerConfig.AppURL,
|
|
||||||
CookieDomain: controllerConfig.CookieDomain,
|
|
||||||
ForgotPasswordMessage: controllerConfig.ForgotPasswordMessage,
|
|
||||||
BackgroundImage: controllerConfig.BackgroundImage,
|
|
||||||
OAuthAutoRedirect: controllerConfig.OAuthAutoRedirect,
|
|
||||||
WarningsEnabled: controllerConfig.WarningsEnabled,
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(expectedAppContextResponse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal expected response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes)
|
|
||||||
}(),
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
description: "Ensure user context returns 401 when unauthorized",
|
Name: "Google",
|
||||||
middlewares: []gin.HandlerFunc{},
|
ID: "google",
|
||||||
path: "/api/context/user",
|
OAuth: true,
|
||||||
expected: func() string {
|
|
||||||
expectedUserContextResponse := controller.UserContextResponse{
|
|
||||||
Status: 401,
|
|
||||||
Message: "Unauthorized",
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(expectedUserContextResponse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal expected response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes)
|
|
||||||
}(),
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
description: "Ensure user context returns when authorized",
|
Title: "Test App",
|
||||||
middlewares: []gin.HandlerFunc{
|
AppURL: "http://localhost:8080",
|
||||||
func(c *gin.Context) {
|
CookieDomain: "localhost",
|
||||||
c.Set("context", &config.UserContext{
|
ForgotPasswordMessage: "Contact admin to reset your password.",
|
||||||
Username: "johndoe",
|
BackgroundImage: "/assets/bg.jpg",
|
||||||
Name: "John Doe",
|
OAuthAutoRedirect: "google",
|
||||||
Email: utils.CompileUserEmail("johndoe", controllerConfig.CookieDomain),
|
WarningsEnabled: true,
|
||||||
Provider: "local",
|
}
|
||||||
IsLoggedIn: true,
|
|
||||||
})
|
var contextCtrlTestContext = config.UserContext{
|
||||||
},
|
Username: "testuser",
|
||||||
},
|
Name: "testuser",
|
||||||
path: "/api/context/user",
|
Email: "test@example.com",
|
||||||
expected: func() string {
|
IsLoggedIn: true,
|
||||||
expectedUserContextResponse := controller.UserContextResponse{
|
IsBasicAuth: false,
|
||||||
Status: 200,
|
OAuth: false,
|
||||||
Message: "Success",
|
Provider: "local",
|
||||||
IsLoggedIn: true,
|
TotpPending: false,
|
||||||
Username: "johndoe",
|
OAuthGroups: "",
|
||||||
Name: "John Doe",
|
TotpEnabled: false,
|
||||||
Email: utils.CompileUserEmail("johndoe", controllerConfig.CookieDomain),
|
OAuthSub: "",
|
||||||
Provider: "local",
|
}
|
||||||
}
|
|
||||||
|
func setupContextController(middlewares *[]gin.HandlerFunc) (*gin.Engine, *httptest.ResponseRecorder) {
|
||||||
bytes, err := json.Marshal(expectedUserContextResponse)
|
tlog.NewSimpleLogger().Init()
|
||||||
|
|
||||||
if err != nil {
|
// Setup
|
||||||
t.Fatalf("Failed to marshal expected response: %v", err)
|
gin.SetMode(gin.TestMode)
|
||||||
}
|
router := gin.Default()
|
||||||
|
recorder := httptest.NewRecorder()
|
||||||
return string(bytes)
|
|
||||||
}(),
|
if middlewares != nil {
|
||||||
},
|
for _, m := range *middlewares {
|
||||||
}
|
router.Use(m)
|
||||||
|
}
|
||||||
for _, test := range tests {
|
}
|
||||||
t.Run(test.description, func(t *testing.T) {
|
|
||||||
router := gin.Default()
|
group := router.Group("/api")
|
||||||
|
|
||||||
for _, middleware := range test.middlewares {
|
ctrl := controller.NewContextController(contextControllerCfg, group)
|
||||||
router.Use(middleware)
|
ctrl.SetupRoutes()
|
||||||
}
|
|
||||||
|
return router, recorder
|
||||||
group := router.Group("/api")
|
}
|
||||||
gin.SetMode(gin.TestMode)
|
|
||||||
|
func TestAppContextHandler(t *testing.T) {
|
||||||
contextController := controller.NewContextController(controllerConfig, group)
|
expectedRes := controller.AppContextResponse{
|
||||||
contextController.SetupRoutes()
|
Status: 200,
|
||||||
|
Message: "Success",
|
||||||
recorder := httptest.NewRecorder()
|
Providers: contextControllerCfg.Providers,
|
||||||
|
Title: contextControllerCfg.Title,
|
||||||
request, err := http.NewRequest("GET", test.path, nil)
|
AppURL: contextControllerCfg.AppURL,
|
||||||
|
CookieDomain: contextControllerCfg.CookieDomain,
|
||||||
if err != nil {
|
ForgotPasswordMessage: contextControllerCfg.ForgotPasswordMessage,
|
||||||
t.Fatalf("Failed to create request: %v", err)
|
BackgroundImage: contextControllerCfg.BackgroundImage,
|
||||||
}
|
OAuthAutoRedirect: contextControllerCfg.OAuthAutoRedirect,
|
||||||
|
WarningsEnabled: contextControllerCfg.WarningsEnabled,
|
||||||
router.ServeHTTP(recorder, request)
|
}
|
||||||
|
|
||||||
if recorder.Code != http.StatusOK {
|
router, recorder := setupContextController(nil)
|
||||||
t.Fatalf("Expected status code 200, got %d", recorder.Code)
|
req := httptest.NewRequest("GET", "/api/context/app", nil)
|
||||||
}
|
router.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
if recorder.Body.String() != test.expected {
|
assert.Equal(t, 200, recorder.Code)
|
||||||
t.Fatalf("Expected response body %s, got %s", test.expected, recorder.Body.String())
|
|
||||||
}
|
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)
|
||||||
|
|
||||||
|
assert.NilError(t, err)
|
||||||
|
assert.DeepEqual(t, expectedRes, ctrlRes)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ func (controller *HealthController) SetupRoutes() {
|
|||||||
|
|
||||||
func (controller *HealthController) healthHandler(c *gin.Context) {
|
func (controller *HealthController) healthHandler(c *gin.Context) {
|
||||||
c.JSON(200, gin.H{
|
c.JSON(200, gin.H{
|
||||||
"status": 200,
|
"status": "ok",
|
||||||
"message": "Healthy",
|
"message": "Healthy",
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,88 +0,0 @@
|
|||||||
package controller_test
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/json"
|
|
||||||
"net/http"
|
|
||||||
"net/http/httptest"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/gin-gonic/gin"
|
|
||||||
"github.com/steveiliop56/tinyauth/internal/controller"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestHealthController(t *testing.T) {
|
|
||||||
tests := []struct {
|
|
||||||
description string
|
|
||||||
path string
|
|
||||||
method string
|
|
||||||
expected string
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
description: "Ensure health endpoint returns 200 OK",
|
|
||||||
path: "/api/healthz",
|
|
||||||
method: "GET",
|
|
||||||
expected: func() string {
|
|
||||||
expectedHealthResponse := map[string]any{
|
|
||||||
"status": 200,
|
|
||||||
"message": "Healthy",
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(expectedHealthResponse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal expected response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes)
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
description: "Ensure health endpoint returns 200 OK for HEAD request",
|
|
||||||
path: "/api/healthz",
|
|
||||||
method: "HEAD",
|
|
||||||
expected: func() string {
|
|
||||||
expectedHealthResponse := map[string]any{
|
|
||||||
"status": 200,
|
|
||||||
"message": "Healthy",
|
|
||||||
}
|
|
||||||
|
|
||||||
bytes, err := json.Marshal(expectedHealthResponse)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to marshal expected response: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return string(bytes)
|
|
||||||
}(),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, test := range tests {
|
|
||||||
t.Run(test.description, func(t *testing.T) {
|
|
||||||
router := gin.Default()
|
|
||||||
group := router.Group("/api")
|
|
||||||
gin.SetMode(gin.TestMode)
|
|
||||||
|
|
||||||
healthController := controller.NewHealthController(group)
|
|
||||||
healthController.SetupRoutes()
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
|
|
||||||
request, err := http.NewRequest(test.method, test.path, nil)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
t.Fatalf("Failed to create request: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
router.ServeHTTP(recorder, request)
|
|
||||||
|
|
||||||
if recorder.Code != http.StatusOK {
|
|
||||||
t.Fatalf("Expected status code 200, got %d", recorder.Code)
|
|
||||||
}
|
|
||||||
|
|
||||||
if recorder.Body.String() != test.expected {
|
|
||||||
t.Fatalf("Expected response body %s, got %s", test.expected, recorder.Body.String())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user