Files
traefikoidc/test_utils_test.go
T
lukaszraczylo 6efb78b7a8 Smarter approach to the cookies (#103)
* Smarter approach to the cookies

  - Single maxCookieSize = 1400 constant with clear documentation
  - Combined cookie storage for ~40-45% size reduction
  - Backward compatible migration from legacy cookies

* Tuneup the code.
2025-12-12 18:35:06 +00:00

345 lines
7.8 KiB
Go

package traefikoidc
import (
"crypto/rand"
"encoding/hex"
"testing"
)
// generateRandomString generates a random string of the specified length
// This is used in tests to create unique identifiers
func generateRandomString(length int) string {
bytes := make([]byte, length/2)
if _, err := rand.Read(bytes); err != nil {
// In tests, fallback to a predictable string if random fails
return "random-string-fallback"
}
return hex.EncodeToString(bytes)
}
// Test createCaseInsensitiveStringMap function
func TestCreateCaseInsensitiveStringMap(t *testing.T) {
tests := []struct {
expected map[string]struct{}
name string
items []string
}{
{
name: "Mixed case items",
items: []string{"Admin", "USER", "manager"},
expected: map[string]struct{}{
"admin": {},
"user": {},
"manager": {},
},
},
{
name: "Empty slice",
items: []string{},
expected: map[string]struct{}{},
},
{
name: "Duplicates with different cases",
items: []string{"Admin", "admin", "ADMIN"},
expected: map[string]struct{}{
"admin": {},
},
},
{
name: "Nil slice",
items: nil,
expected: map[string]struct{}{},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := createCaseInsensitiveStringMap(tt.items)
if len(result) != len(tt.expected) {
t.Errorf("createCaseInsensitiveStringMap() length = %v, want %v", len(result), len(tt.expected))
return
}
for key := range tt.expected {
if _, exists := result[key]; !exists {
t.Errorf("createCaseInsensitiveStringMap() missing key %v", key)
}
}
})
}
}
// Test keysFromMap function
func TestKeysFromMap(t *testing.T) {
tests := []struct {
name string
input map[string]struct{}
expected []string
}{
{
name: "Multiple keys",
input: map[string]struct{}{
"key1": {},
"key2": {},
"key3": {},
},
expected: []string{"key1", "key2", "key3"},
},
{
name: "Empty map",
input: map[string]struct{}{},
expected: []string{},
},
{
name: "Single key",
input: map[string]struct{}{
"onlykey": {},
},
expected: []string{"onlykey"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := keysFromMap(tt.input)
if len(result) != len(tt.expected) {
t.Errorf("keysFromMap() length = %v, want %v", len(result), len(tt.expected))
return
}
// Convert to map for comparison since order doesn't matter
resultMap := make(map[string]bool)
for _, key := range result {
resultMap[key] = true
}
for _, key := range tt.expected {
if !resultMap[key] {
t.Errorf("keysFromMap() missing key %v", key)
}
}
})
}
}
// Test TraefikOidc provider detection methods
func TestTraefikOidcProviderDetection(t *testing.T) {
tests := []struct {
name string
providerURL string
expectGoogle bool
expectAzure bool
}{
{
name: "Google provider",
providerURL: "https://accounts.google.com",
expectGoogle: true,
expectAzure: false,
},
{
name: "Azure provider",
providerURL: "https://login.microsoftonline.com/tenant-id/v2.0",
expectGoogle: false,
expectAzure: true,
},
{
name: "Generic provider",
providerURL: "https://auth.example.com",
expectGoogle: false,
expectAzure: false,
},
{
name: "Empty provider URL",
providerURL: "",
expectGoogle: false,
expectAzure: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
traefik := &TraefikOidc{
issuerURL: tt.providerURL,
}
isGoogle := traefik.isGoogleProvider()
isAzure := traefik.isAzureProvider()
if isGoogle != tt.expectGoogle {
t.Errorf("isGoogleProvider() = %v, want %v", isGoogle, tt.expectGoogle)
}
if isAzure != tt.expectAzure {
t.Errorf("isAzureProvider() = %v, want %v", isAzure, tt.expectAzure)
}
})
}
}
// Test buildFullURL function
func TestBuildFullURL(t *testing.T) {
tests := []struct {
name string
scheme string
host string
path string
expected string
}{
{
name: "Standard HTTPS URL",
scheme: "https",
host: "example.com",
path: "/auth/callback",
expected: "https://example.com/auth/callback",
},
{
name: "HTTP URL",
scheme: "http",
host: "localhost:8080",
path: "/test",
expected: "http://localhost:8080/test",
},
{
name: "Root path",
scheme: "https",
host: "api.example.com",
path: "/",
expected: "https://api.example.com/",
},
{
name: "Empty path",
scheme: "https",
host: "example.com",
path: "",
expected: "https://example.com/",
},
{
name: "Path without leading slash",
scheme: "https",
host: "example.com",
path: "noSlash",
expected: "https://example.com/noSlash",
},
{
name: "Complex path with query params",
scheme: "https",
host: "api.example.com",
path: "/v2/search?q=test&limit=10",
expected: "https://api.example.com/v2/search?q=test&limit=10",
},
{
name: "IPv4 address",
scheme: "http",
host: "192.168.1.100",
path: "/api",
expected: "http://192.168.1.100/api",
},
{
name: "IPv6 address with brackets",
scheme: "http",
host: "[::1]:8080",
path: "/test",
expected: "http://[::1]:8080/test",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := buildFullURL(tt.scheme, tt.host, tt.path)
if result != tt.expected {
t.Errorf("buildFullURL() = %v, want %v", result, tt.expected)
}
})
}
}
// Test validateURL function
func TestValidateURL(t *testing.T) {
traefik := &TraefikOidc{
logger: NewLogger("debug"),
}
tests := []struct {
name string
url string
expectError bool
}{
{
name: "Valid HTTPS URL",
url: "https://example.com/path",
expectError: false,
},
{
name: "Valid HTTP URL",
url: "http://example.com",
expectError: false,
},
{
name: "Empty URL",
url: "",
expectError: true,
},
{
name: "Invalid URL format",
url: "not-a-url",
expectError: true,
},
{
name: "URL with space",
url: "https://example .com",
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := traefik.validateURL(tt.url)
if tt.expectError && err == nil {
t.Errorf("validateURL(%q) expected error but got none", tt.url)
} else if !tt.expectError && err != nil {
t.Errorf("validateURL(%q) unexpected error: %v", tt.url, err)
}
})
}
}
// Test TraefikOidc helper logging methods
func TestTraefikOidcHelperMethods(t *testing.T) {
traefik := &TraefikOidc{
logger: NewLogger("debug"),
}
// Test safe logging methods (they just delegate to logger, but increase coverage)
traefik.safeLogDebug("test debug message")
traefik.safeLogDebugf("test debug with %s", "param")
traefik.safeLogError("test error message")
traefik.safeLogErrorf("test error with %s", "param")
traefik.safeLogInfo("test info message")
// These methods should not panic with nil logger either
traefikNilLogger := &TraefikOidc{}
traefikNilLogger.safeLogDebug("test with nil logger")
traefikNilLogger.safeLogInfo("test info with nil logger")
}
// Test CreateDefaultHTTPClient function
func TestCreateDefaultHTTPClient(t *testing.T) {
client := CreateDefaultHTTPClient()
if client == nil {
t.Fatal("createDefaultHTTPClient() returned nil")
}
if client.Timeout == 0 {
t.Error("Expected non-zero timeout")
}
// Verify it has some reasonable timeout
expectedTimeout := 30000000000 // 30 seconds in nanoseconds
if client.Timeout.Nanoseconds() != int64(expectedTimeout) {
t.Logf("Client timeout: %v (expected 30s, but this may vary)", client.Timeout)
}
}