A previous `git pull --rebase origin main` dropped 177 local commits,
losing 3400+ files across admin-v2, backend, studio-v2, website,
klausur-service, and many other services. The partial restore attempt
(660295e2) only recovered some files.
This commit restores all missing files from pre-rebase ref 98933f5e
while preserving post-rebase additions (night-scheduler, night-mode UI,
NightModeWidget dashboard integration).
Restored features include:
- AI Module Sidebar (FAB), OCR Labeling, OCR Compare
- GPU Dashboard, RAG Pipeline, Magic Help
- Klausur-Korrektur (8 files), Abitur-Archiv (5+ files)
- Companion, Zeugnisse-Crawler, Screen Flow
- Full backend, studio-v2, website, klausur-service
- All compliance SDKs, agent-core, voice-service
- CI/CD configs, documentation, scripts
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
323 lines
8.4 KiB
Go
323 lines
8.4 KiB
Go
package config
|
|
|
|
import (
|
|
"os"
|
|
"testing"
|
|
)
|
|
|
|
// TestGetEnv tests the getEnv helper function
|
|
func TestGetEnv(t *testing.T) {
|
|
// Test with default value when env var not set
|
|
result := getEnv("TEST_NONEXISTENT_VAR_12345", "default")
|
|
if result != "default" {
|
|
t.Errorf("Expected 'default', got '%s'", result)
|
|
}
|
|
|
|
// Test with set env var
|
|
os.Setenv("TEST_ENV_VAR", "custom_value")
|
|
defer os.Unsetenv("TEST_ENV_VAR")
|
|
|
|
result = getEnv("TEST_ENV_VAR", "default")
|
|
if result != "custom_value" {
|
|
t.Errorf("Expected 'custom_value', got '%s'", result)
|
|
}
|
|
}
|
|
|
|
// TestGetEnvInt tests the getEnvInt helper function
|
|
func TestGetEnvInt(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
envValue string
|
|
defaultValue int
|
|
expected int
|
|
}{
|
|
{"default when not set", "", 100, 100},
|
|
{"parse valid int", "42", 0, 42},
|
|
{"parse zero", "0", 100, 0},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.envValue != "" {
|
|
os.Setenv("TEST_INT_VAR", tt.envValue)
|
|
defer os.Unsetenv("TEST_INT_VAR")
|
|
} else {
|
|
os.Unsetenv("TEST_INT_VAR")
|
|
}
|
|
|
|
result := getEnvInt("TEST_INT_VAR", tt.defaultValue)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %d, got %d", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestGetEnvBool tests the getEnvBool helper function
|
|
func TestGetEnvBool(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
envValue string
|
|
defaultValue bool
|
|
expected bool
|
|
}{
|
|
{"default when not set", "", true, true},
|
|
{"default false when not set", "", false, false},
|
|
{"parse true", "true", false, true},
|
|
{"parse 1", "1", false, true},
|
|
{"parse yes", "yes", false, true},
|
|
{"parse false", "false", true, false},
|
|
{"parse 0", "0", true, false},
|
|
{"parse no", "no", true, false},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if tt.envValue != "" {
|
|
os.Setenv("TEST_BOOL_VAR", tt.envValue)
|
|
defer os.Unsetenv("TEST_BOOL_VAR")
|
|
} else {
|
|
os.Unsetenv("TEST_BOOL_VAR")
|
|
}
|
|
|
|
result := getEnvBool("TEST_BOOL_VAR", tt.defaultValue)
|
|
if result != tt.expected {
|
|
t.Errorf("Expected %v, got %v", tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestParseCommaSeparated tests the parseCommaSeparated helper function
|
|
func TestParseCommaSeparated(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
input string
|
|
expected []string
|
|
}{
|
|
{"empty string", "", []string{}},
|
|
{"single value", "value1", []string{"value1"}},
|
|
{"multiple values", "value1,value2,value3", []string{"value1", "value2", "value3"}},
|
|
{"with spaces", "value1, value2, value3", []string{"value1", "value2", "value3"}},
|
|
{"with trailing comma", "value1,value2,", []string{"value1", "value2"}},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := parseCommaSeparated(tt.input)
|
|
if len(result) != len(tt.expected) {
|
|
t.Errorf("Expected length %d, got %d", len(tt.expected), len(result))
|
|
return
|
|
}
|
|
for i := range result {
|
|
if result[i] != tt.expected[i] {
|
|
t.Errorf("At index %d: expected '%s', got '%s'", i, tt.expected[i], result[i])
|
|
}
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestConfigEnvironmentDefaults tests default environment values
|
|
func TestConfigEnvironmentDefaults(t *testing.T) {
|
|
// Clear any existing env vars that might interfere
|
|
varsToUnset := []string{
|
|
"PORT", "ENVIRONMENT", "DATABASE_URL", "JWT_SECRET", "JWT_REFRESH_SECRET",
|
|
}
|
|
for _, v := range varsToUnset {
|
|
os.Unsetenv(v)
|
|
}
|
|
|
|
// Set required vars
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
os.Setenv("JWT_SECRET", "test-secret-32-chars-minimum-here")
|
|
defer func() {
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
// Test defaults
|
|
if cfg.Port != "8080" {
|
|
t.Errorf("Expected default port '8080', got '%s'", cfg.Port)
|
|
}
|
|
|
|
if cfg.Environment != "development" {
|
|
t.Errorf("Expected default environment 'development', got '%s'", cfg.Environment)
|
|
}
|
|
}
|
|
|
|
// TestConfigLoadWithEnvironment tests loading config with different environments
|
|
func TestConfigLoadWithEnvironment(t *testing.T) {
|
|
// Set required vars
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
os.Setenv("JWT_SECRET", "test-secret-32-chars-minimum-here")
|
|
defer func() {
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
}()
|
|
|
|
tests := []struct {
|
|
name string
|
|
environment string
|
|
}{
|
|
{"development", "development"},
|
|
{"staging", "staging"},
|
|
{"production", "production"},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
os.Setenv("ENVIRONMENT", tt.environment)
|
|
defer os.Unsetenv("ENVIRONMENT")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.Environment != tt.environment {
|
|
t.Errorf("Expected environment '%s', got '%s'", tt.environment, cfg.Environment)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestConfigMissingRequiredVars tests that missing required vars return errors
|
|
func TestConfigMissingRequiredVars(t *testing.T) {
|
|
// Clear all env vars
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
|
|
_, err := Load()
|
|
if err == nil {
|
|
t.Error("Expected error when DATABASE_URL is missing")
|
|
}
|
|
|
|
// Set DATABASE_URL but not JWT_SECRET
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
defer os.Unsetenv("DATABASE_URL")
|
|
|
|
_, err = Load()
|
|
if err == nil {
|
|
t.Error("Expected error when JWT_SECRET is missing")
|
|
}
|
|
}
|
|
|
|
// TestConfigAllowedOrigins tests that allowed origins are parsed correctly
|
|
func TestConfigAllowedOrigins(t *testing.T) {
|
|
// Set required vars
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
os.Setenv("JWT_SECRET", "test-secret-32-chars-minimum-here")
|
|
os.Setenv("ALLOWED_ORIGINS", "http://localhost:3000,http://localhost:8000,http://localhost:8001")
|
|
defer func() {
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
os.Unsetenv("ALLOWED_ORIGINS")
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
expected := []string{"http://localhost:3000", "http://localhost:8000", "http://localhost:8001"}
|
|
if len(cfg.AllowedOrigins) != len(expected) {
|
|
t.Errorf("Expected %d origins, got %d", len(expected), len(cfg.AllowedOrigins))
|
|
}
|
|
|
|
for i, origin := range cfg.AllowedOrigins {
|
|
if origin != expected[i] {
|
|
t.Errorf("At index %d: expected '%s', got '%s'", i, expected[i], origin)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestConfigDebugSettings tests debug-related settings for different environments
|
|
func TestConfigDebugSettings(t *testing.T) {
|
|
// Set required vars
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5432/test")
|
|
os.Setenv("JWT_SECRET", "test-secret-32-chars-minimum-here")
|
|
defer func() {
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
}()
|
|
|
|
// Test development environment
|
|
t.Run("development", func(t *testing.T) {
|
|
os.Setenv("ENVIRONMENT", "development")
|
|
defer os.Unsetenv("ENVIRONMENT")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.Environment != "development" {
|
|
t.Errorf("Expected 'development', got '%s'", cfg.Environment)
|
|
}
|
|
})
|
|
|
|
// Test staging environment
|
|
t.Run("staging", func(t *testing.T) {
|
|
os.Setenv("ENVIRONMENT", "staging")
|
|
defer os.Unsetenv("ENVIRONMENT")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.Environment != "staging" {
|
|
t.Errorf("Expected 'staging', got '%s'", cfg.Environment)
|
|
}
|
|
})
|
|
|
|
// Test production environment
|
|
t.Run("production", func(t *testing.T) {
|
|
os.Setenv("ENVIRONMENT", "production")
|
|
defer os.Unsetenv("ENVIRONMENT")
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.Environment != "production" {
|
|
t.Errorf("Expected 'production', got '%s'", cfg.Environment)
|
|
}
|
|
})
|
|
}
|
|
|
|
// TestConfigStagingPorts tests that staging uses different ports
|
|
func TestConfigStagingPorts(t *testing.T) {
|
|
// Set required vars
|
|
os.Setenv("DATABASE_URL", "postgres://test:test@localhost:5433/breakpilot_staging")
|
|
os.Setenv("JWT_SECRET", "test-secret-32-chars-minimum-here")
|
|
os.Setenv("ENVIRONMENT", "staging")
|
|
os.Setenv("PORT", "8081")
|
|
defer func() {
|
|
os.Unsetenv("DATABASE_URL")
|
|
os.Unsetenv("JWT_SECRET")
|
|
os.Unsetenv("ENVIRONMENT")
|
|
os.Unsetenv("PORT")
|
|
}()
|
|
|
|
cfg, err := Load()
|
|
if err != nil {
|
|
t.Fatalf("Failed to load config: %v", err)
|
|
}
|
|
|
|
if cfg.Port != "8081" {
|
|
t.Errorf("Expected staging port '8081', got '%s'", cfg.Port)
|
|
}
|
|
|
|
if cfg.Environment != "staging" {
|
|
t.Errorf("Expected 'staging', got '%s'", cfg.Environment)
|
|
}
|
|
}
|