forked from baron/baron-sso
- Added support for fixed UUIDs during bulk registration (Search-first + ExternalID mapping) - Implemented idempotency and visibility restoration for soft-deleted users - Enhanced bulk upload UI to show 'New/Updated/Unchanged' status and modified fields - Added logic to reclaim identifiers (login_id) from colliding records - Added frontend E2E and backend unit tests for UUID integrity and conflict handling - Fixed i18n, formatting, and mock tests to satisfy code-check - Applied 'go fix' for 'omitzero' tags and general Go standards
134 lines
3.8 KiB
Go
134 lines
3.8 KiB
Go
package handler
|
|
|
|
import (
|
|
"baron-sso-backend/internal/domain"
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gofiber/fiber/v2"
|
|
"github.com/stretchr/testify/assert"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// Mock DB for ApiKey tests using a real GORM instance but with a hijacked connection
|
|
// or just a simple mock if we only check nil.
|
|
// For ApiKeyHandler, it uses DB for Create/List/Delete.
|
|
|
|
func TestApiKeyHandler_CreateApiKey(t *testing.T) {
|
|
app := fiber.New()
|
|
// ApiKeyHandler requires a valid DB connection to perform h.DB.Create
|
|
// Since we don't have a real DB here, we'll check if it fails gracefully
|
|
// or we can use sqlite in-memory for a more realistic test.
|
|
h := &ApiKeyHandler{DB: nil} // Testing ServiceUnavailable
|
|
|
|
app.Post("/api-keys", h.CreateApiKey)
|
|
|
|
input := map[string]any{
|
|
"name": "M2M Test",
|
|
"scopes": []string{"read", "write"},
|
|
}
|
|
body, _ := json.Marshal(input)
|
|
|
|
req := httptest.NewRequest("POST", "/api-keys", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, _ := app.Test(req)
|
|
|
|
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
|
|
}
|
|
|
|
func TestApiKeyHandler_Validation(t *testing.T) {
|
|
app := fiber.New()
|
|
// Using a dummy DB pointer to pass the nil check
|
|
h := &ApiKeyHandler{DB: &gorm.DB{}}
|
|
|
|
app.Post("/api-keys", h.CreateApiKey)
|
|
|
|
// Missing name
|
|
input := map[string]any{
|
|
"scopes": []string{"read"},
|
|
}
|
|
body, _ := json.Marshal(input)
|
|
|
|
req := httptest.NewRequest("POST", "/api-keys", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, _ := app.Test(req)
|
|
|
|
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
|
|
}
|
|
|
|
func TestApiKeyHandler_UpdateApiKeyScopesRequiresDatabase(t *testing.T) {
|
|
app := fiber.New()
|
|
h := &ApiKeyHandler{DB: nil}
|
|
|
|
app.Patch("/api-keys/:id", h.UpdateApiKey)
|
|
|
|
body, _ := json.Marshal(map[string]any{
|
|
"scopes": []string{"org-context:read"},
|
|
})
|
|
req := httptest.NewRequest("PATCH", "/api-keys/api-key-id", bytes.NewReader(body))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
resp, _ := app.Test(req)
|
|
|
|
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
|
|
}
|
|
|
|
func TestApiKeyHandler_RotateApiKeySecretRequiresDatabase(t *testing.T) {
|
|
app := fiber.New()
|
|
h := &ApiKeyHandler{DB: nil}
|
|
|
|
app.Post("/api-keys/:id/secret/rotate", h.RotateApiKeySecret)
|
|
|
|
req := httptest.NewRequest("POST", "/api-keys/api-key-id/secret/rotate", nil)
|
|
resp, _ := app.Test(req)
|
|
|
|
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
|
|
}
|
|
|
|
func TestApiKeyWithUpdatedScopesPreservesClientID(t *testing.T) {
|
|
key := domain.ApiKey{
|
|
ID: "api-key-id",
|
|
Name: "M2M Test",
|
|
ClientID: "client-id-stable",
|
|
ClientSecretHash: "old-secret-hash",
|
|
Scopes: "audit:read",
|
|
Status: "active",
|
|
}
|
|
|
|
updated := apiKeyWithUpdatedScopes(key, []string{"audit:read", "org-context:read"})
|
|
|
|
assert.Equal(t, "client-id-stable", updated.ClientID)
|
|
assert.Equal(t, "old-secret-hash", updated.ClientSecretHash)
|
|
assert.Equal(t, "audit:read org-context:read", updated.Scopes)
|
|
}
|
|
|
|
func TestApiKeyWithRotatedSecretHashPreservesClientIDAndScopes(t *testing.T) {
|
|
key := domain.ApiKey{
|
|
ID: "api-key-id",
|
|
Name: "M2M Test",
|
|
ClientID: "client-id-stable",
|
|
ClientSecretHash: "old-secret-hash",
|
|
Scopes: "audit:read org-context:read",
|
|
Status: "active",
|
|
}
|
|
|
|
updated := apiKeyWithRotatedSecretHash(key, "new-secret-hash")
|
|
|
|
assert.Equal(t, "client-id-stable", updated.ClientID)
|
|
assert.Equal(t, "audit:read org-context:read", updated.Scopes)
|
|
assert.Equal(t, "new-secret-hash", updated.ClientSecretHash)
|
|
}
|
|
|
|
func TestNormalizeApiKeyScopesTrimsAndDeduplicates(t *testing.T) {
|
|
scopes := normalizeApiKeyScopes([]string{
|
|
" audit:read ",
|
|
"",
|
|
"org-context:read",
|
|
"audit:read",
|
|
})
|
|
|
|
assert.Equal(t, []string{"audit:read", "org-context:read"}, scopes)
|
|
}
|