Files
user-system/internal/auth/oauth_config_test.go
long-agent 582ad7a069 test: add comprehensive test coverage and improve code quality
- Add new test files for auth, service, and handler modules
- Improve test organization and coverage
- Refactor code for better maintainability
- Add captcha, settings, stats, and theme handler tests
- Add auth module tests (CAS, OAuth, password, SSO, state)
- Add service layer tests for auth, export, permissions, roles
- All Go tests pass (exit code 0)
- All frontend tests pass (325 tests in 59 files)
2026-04-17 20:43:50 +08:00

335 lines
9.4 KiB
Go

package auth
import (
"os"
"path/filepath"
"sync"
"testing"
)
func TestGetEnv(t *testing.T) {
// Test with default value when env not set
result := getEnv("NON_EXISTENT_ENV_VAR", "default")
if result != "default" {
t.Errorf("getEnv() = %s, want default", result)
}
// Test with env set
os.Setenv("TEST_ENV_VAR", "test_value")
defer os.Unsetenv("TEST_ENV_VAR")
result = getEnv("TEST_ENV_VAR", "default")
if result != "test_value" {
t.Errorf("getEnv() = %s, want test_value", result)
}
}
func TestGetEnvBool(t *testing.T) {
tests := []struct {
name string
envValue string
defaultValue bool
want bool
}{
{"default true, no env", "", true, true},
{"default false, no env", "", false, false},
{"env true", "true", false, true},
{"env TRUE", "TRUE", false, true},
{"env True", "True", false, true},
{"env 1", "1", false, true},
{"env false", "false", true, false},
{"env 0", "0", true, false},
{"env other", "random", true, false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
if tt.envValue != "" {
os.Setenv("TEST_BOOL_ENV", tt.envValue)
defer os.Unsetenv("TEST_BOOL_ENV")
} else {
os.Unsetenv("TEST_BOOL_ENV")
}
result := getEnvBool("TEST_BOOL_ENV", tt.defaultValue)
if result != tt.want {
t.Errorf("getEnvBool() = %v, want %v", result, tt.want)
}
})
}
}
func TestLoadFromEnv(t *testing.T) {
// Set some env vars
os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://example.com")
os.Setenv("OAUTH_CALLBACK_PATH", "/auth/callback")
os.Setenv("WECHAT_OAUTH_ENABLED", "true")
os.Setenv("WECHAT_APP_ID", "wechat-app-id")
os.Setenv("GOOGLE_OAUTH_ENABLED", "true")
os.Setenv("GOOGLE_CLIENT_ID", "google-client-id")
defer func() {
os.Unsetenv("OAUTH_REDIRECT_BASE_URL")
os.Unsetenv("OAUTH_CALLBACK_PATH")
os.Unsetenv("WECHAT_OAUTH_ENABLED")
os.Unsetenv("WECHAT_APP_ID")
os.Unsetenv("GOOGLE_OAUTH_ENABLED")
os.Unsetenv("GOOGLE_CLIENT_ID")
}()
config := loadFromEnv()
if config.Common.RedirectBaseURL != "https://example.com" {
t.Errorf("RedirectBaseURL = %s, want https://example.com", config.Common.RedirectBaseURL)
}
if config.Common.CallbackPath != "/auth/callback" {
t.Errorf("CallbackPath = %s, want /auth/callback", config.Common.CallbackPath)
}
if !config.WeChat.Enabled {
t.Error("WeChat.Enabled should be true")
}
if config.WeChat.AppID != "wechat-app-id" {
t.Errorf("WeChat.AppID = %s, want wechat-app-id", config.WeChat.AppID)
}
if !config.Google.Enabled {
t.Error("Google.Enabled should be true")
}
if config.Google.ClientID != "google-client-id" {
t.Errorf("Google.ClientID = %s, want google-client-id", config.Google.ClientID)
}
// Check default URLs
if config.WeChat.AuthURL != "https://open.weixin.qq.com/connect/qrconnect" {
t.Errorf("WeChat.AuthURL = %s", config.WeChat.AuthURL)
}
if config.Google.UserInfoURL != "https://www.googleapis.com/oauth2/v2/userinfo" {
t.Errorf("Google.UserInfoURL = %s", config.Google.UserInfoURL)
}
}
// resetOAuthConfig resets the oauth config singleton for testing
func resetOAuthConfig() {
oauthConfig = nil
oauthConfigOnce = sync.Once{}
}
func TestLoadOAuthConfig_FileNotExists(t *testing.T) {
// Reset the singleton for testing
resetOAuthConfig()
// Load from non-existent file - should fall back to env
config, _ := LoadOAuthConfig("/non/existent/path/config.yaml")
if config == nil {
t.Error("LoadOAuthConfig() should return config even when file doesn't exist")
}
}
func TestLoadOAuthConfig_InvalidYAML(t *testing.T) {
// Create temp file with invalid YAML
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "invalid_config.yaml")
if err := os.WriteFile(configPath, []byte("invalid: yaml: content: ["), 0644); err != nil {
t.Fatalf("Failed to write temp file: %v", err)
}
// Reset the singleton for testing
resetOAuthConfig()
config, err := LoadOAuthConfig(configPath)
if err == nil {
t.Error("LoadOAuthConfig() should return error for invalid YAML")
}
if config == nil {
t.Error("LoadOAuthConfig() should still return fallback config on error")
}
}
func TestLoadOAuthConfig_ValidYAML(t *testing.T) {
yamlContent := `
common:
redirect_base_url: "https://myapp.com"
callback_path: "/oauth/callback"
wechat:
enabled: true
app_id: "test-wechat-id"
app_secret: "test-secret"
scopes:
- snsapi_login
google:
enabled: true
client_id: "test-google-id"
client_secret: "test-secret"
scopes:
- openid
- email
facebook:
enabled: false
app_id: ""
app_secret: ""
qq:
enabled: true
app_id: "test-qq-id"
app_key: "test-qq-key"
weibo:
enabled: false
twitter:
enabled: false
`
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "oauth_config.yaml")
if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
t.Fatalf("Failed to write temp file: %v", err)
}
// Reset the singleton for testing
resetOAuthConfig()
config, err := LoadOAuthConfig(configPath)
if err != nil {
t.Fatalf("LoadOAuthConfig() error = %v", err)
}
if config.Common.RedirectBaseURL != "https://myapp.com" {
t.Errorf("RedirectBaseURL = %s, want https://myapp.com", config.Common.RedirectBaseURL)
}
if !config.WeChat.Enabled {
t.Error("WeChat.Enabled should be true")
}
if config.WeChat.AppID != "test-wechat-id" {
t.Errorf("WeChat.AppID = %s, want test-wechat-id", config.WeChat.AppID)
}
if len(config.WeChat.Scopes) != 1 || config.WeChat.Scopes[0] != "snsapi_login" {
t.Errorf("WeChat.Scopes = %v, want [snsapi_login]", config.WeChat.Scopes)
}
if !config.Google.Enabled {
t.Error("Google.Enabled should be true")
}
if len(config.Google.Scopes) != 2 {
t.Errorf("Google.Scopes length = %d, want 2", len(config.Google.Scopes))
}
if config.Facebook.Enabled {
t.Error("Facebook.Enabled should be false")
}
if !config.QQ.Enabled {
t.Error("QQ.Enabled should be true")
}
}
func TestGetOAuthConfig(t *testing.T) {
// Reset the singleton
resetOAuthConfig()
// Set an env var to verify it's loaded
os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://test-get-config.com")
defer os.Unsetenv("OAUTH_REDIRECT_BASE_URL")
config := GetOAuthConfig()
if config == nil {
t.Fatal("GetOAuthConfig() returned nil")
}
if config.Common.RedirectBaseURL != "https://test-get-config.com" {
t.Errorf("RedirectBaseURL = %s, want https://test-get-config.com", config.Common.RedirectBaseURL)
}
// Call again to test singleton behavior
config2 := GetOAuthConfig()
if config != config2 {
t.Error("GetOAuthConfig() should return same instance")
}
}
func TestLoadOAuthConfig_DefaultPath(t *testing.T) {
// Reset the singleton
resetOAuthConfig()
// Set env to verify fallback to env
os.Setenv("OAUTH_REDIRECT_BASE_URL", "https://default-path-test.com")
defer os.Unsetenv("OAUTH_REDIRECT_BASE_URL")
// Load with empty path - should use default path and fall back to env
config, _ := LoadOAuthConfig("")
if config.Common.RedirectBaseURL != "https://default-path-test.com" {
t.Errorf("RedirectBaseURL = %s, want https://default-path-test.com", config.Common.RedirectBaseURL)
}
}
func TestMiniProgramConfig(t *testing.T) {
yamlContent := `
wechat:
enabled: true
app_id: "test-app-id"
mini_program:
enabled: true
app_id: "mini-app-id"
app_secret: "mini-secret"
`
tmpDir := t.TempDir()
configPath := filepath.Join(tmpDir, "oauth_config.yaml")
if err := os.WriteFile(configPath, []byte(yamlContent), 0644); err != nil {
t.Fatalf("Failed to write temp file: %v", err)
}
// Reset the singleton for testing
resetOAuthConfig()
config, err := LoadOAuthConfig(configPath)
if err != nil {
t.Fatalf("LoadOAuthConfig() error = %v", err)
}
if !config.WeChat.MiniProgram.Enabled {
t.Error("MiniProgram.Enabled should be true")
}
if config.WeChat.MiniProgram.AppID != "mini-app-id" {
t.Errorf("MiniProgram.AppID = %s, want mini-app-id", config.WeChat.MiniProgram.AppID)
}
}
func TestAllOAuthConfigs_HaveDefaultURLs(t *testing.T) {
// Clear all relevant env vars
envVars := []string{
"WECHAT_AUTH_URL", "WECHAT_TOKEN_URL", "WECHAT_USER_INFO_URL",
"GOOGLE_AUTH_URL", "GOOGLE_TOKEN_URL", "GOOGLE_USER_INFO_URL",
"FACEBOOK_AUTH_URL", "FACEBOOK_TOKEN_URL", "FACEBOOK_USER_INFO_URL",
"QQ_AUTH_URL", "QQ_TOKEN_URL", "QQ_OPENID_URL", "QQ_USER_INFO_URL",
"WEIBO_AUTH_URL", "WEIBO_TOKEN_URL", "WEIBO_USER_INFO_URL",
"TWITTER_AUTH_URL", "TWITTER_TOKEN_URL", "TWITTER_USER_INFO_URL",
}
for _, v := range envVars {
os.Unsetenv(v)
}
config := loadFromEnv()
// Verify WeChat defaults
if config.WeChat.AuthURL != "https://open.weixin.qq.com/connect/qrconnect" {
t.Errorf("WeChat.AuthURL default incorrect: %s", config.WeChat.AuthURL)
}
// Verify Google defaults
if config.Google.AuthURL != "https://accounts.google.com/o/oauth2/v2/auth" {
t.Errorf("Google.AuthURL default incorrect: %s", config.Google.AuthURL)
}
// Verify Facebook defaults
if config.Facebook.AuthURL != "https://www.facebook.com/v18.0/dialog/oauth" {
t.Errorf("Facebook.AuthURL default incorrect: %s", config.Facebook.AuthURL)
}
// Verify QQ defaults
if config.QQ.AuthURL != "https://graph.qq.com/oauth2.0/authorize" {
t.Errorf("QQ.AuthURL default incorrect: %s", config.QQ.AuthURL)
}
// Verify Weibo defaults
if config.Weibo.AuthURL != "https://api.weibo.com/oauth2/authorize" {
t.Errorf("Weibo.AuthURL default incorrect: %s", config.Weibo.AuthURL)
}
// Verify Twitter defaults
if config.Twitter.AuthURL != "https://twitter.com/i/oauth2/authorize" {
t.Errorf("Twitter.AuthURL default incorrect: %s", config.Twitter.AuthURL)
}
}