package auth import ( "testing" "github.com/perfect-panel/server/internal/config" "github.com/stretchr/testify/assert" ) func TestNormalizeEmail(t *testing.T) { tests := []struct { input string want string }{ // Gmail dot trick {"a.v.x.xx@gmail.com", "avxxx@gmail.com"}, {"john.doe@gmail.com", "johndoe@gmail.com"}, {"a.b.c.d.e@gmail.com", "abcde@gmail.com"}, // Gmail + alias {"user+tag@gmail.com", "user@gmail.com"}, {"a.b+tag@gmail.com", "ab@gmail.com"}, // Googlemail {"a.b@googlemail.com", "ab@googlemail.com"}, // Non-Gmail: dots preserved {"john.doe@outlook.com", "john.doe@outlook.com"}, {"john.doe@qq.com", "john.doe@qq.com"}, // + alias stripped for all providers {"user+spam@outlook.com", "user@outlook.com"}, {"user+spam@qq.com", "user@qq.com"}, // Case insensitive {"User@Gmail.COM", "user@gmail.com"}, {"A.B@Gmail.com", "ab@gmail.com"}, // No change for normal non-gmail email {"abc@163.com", "abc@163.com"}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := NormalizeEmail(tt.input) if got != tt.want { t.Errorf("NormalizeEmail(%q) = %q, want %q", tt.input, got, tt.want) } }) } } func TestNormalizeEmail_NoChangeSkipsCheck(t *testing.T) { // These emails should NOT trigger cross-user check (normalized == original) noChangeCases := []string{ "abc@163.com", "john.doe@outlook.com", "user@qq.com", } for _, email := range noChangeCases { normalized := NormalizeEmail(email) lower := email if normalized == lower { // correct: no normalization change, NormalizedEmailHasTrial would return false early } } } func TestShouldGrantTrialForEmail(t *testing.T) { // 模拟线上配置:白名单开启,gmail.com 也在名单里 rcWithGmail := config.RegisterConfig{ EnableTrial: true, EnableTrialEmailWhitelist: true, TrialEmailDomainWhitelist: "hifastapp.com,hifastvpn.com,126.com,139.com,163.com,gmail.com", } // 白名单关闭 rcNoWhitelist := config.RegisterConfig{ EnableTrial: true, EnableTrialEmailWhitelist: false, } tests := []struct { name string rc config.RegisterConfig email string want bool reason string }{ { name: "gmail dot trick - blocked even if gmail.com in whitelist", rc: rcWithGmail, email: "s.m.s.n.fsmbt.d.ndny@gmail.com", want: false, reason: "gmail 泛域名(含点号)应拒绝", }, { name: "gmail plus alias - blocked", rc: rcWithGmail, email: "user+tag@gmail.com", want: false, reason: "gmail +别名应拒绝", }, { name: "clean gmail - allowed", rc: rcWithGmail, email: "normaluser@gmail.com", want: true, reason: "干净的 gmail 应放行", }, { name: "163 with dot - allowed (non-gmail dot is ok)", rc: rcWithGmail, email: "s.m.s.n@163.com", want: true, reason: "非 gmail 域点号不拦截", }, { name: "163 plus alias - blocked", rc: rcWithGmail, email: "user+spam@163.com", want: false, reason: "所有域名的 +别名都拦截", }, { name: "gmail typo squatting domain - blocked even if accidentally whitelisted", rc: config.RegisterConfig{EnableTrial: true, EnableTrialEmailWhitelist: true, TrialEmailDomainWhitelist: "gmail.com,gmaial.com"}, email: "1.2.3.4xxx@gmaial.com", want: false, reason: "易混淆 Gmail 域名不应发放试用", }, { name: "invalid empty local - blocked", rc: rcWithGmail, email: "@gmail.com", want: false, reason: "邮箱 local 为空应拒绝", }, { name: "subdomain spoof - blocked", rc: rcWithGmail, email: "user@fake.gmail.com", want: false, reason: "白名单必须精确匹配域名,不匹配子域", }, { name: "whitelist disabled - gmail dot trick still blocked", rc: rcNoWhitelist, email: "s.m.s.n.fsmbt.d.ndny@gmail.com", want: false, reason: "白名单未启用,但泛域名仍应拒绝", }, { name: "trial disabled - always blocked", rc: config.RegisterConfig{EnableTrial: false}, email: "user@163.com", want: false, reason: "试用未开启", }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got := ShouldGrantTrialForEmail(tt.rc, tt.email) if got != tt.want { t.Errorf("ShouldGrantTrialForEmail(%q) = %v, want %v | reason: %s", tt.email, got, tt.want, tt.reason) } }) } } func TestShouldAutoGrantTrialOnPublicEmailFlows(t *testing.T) { assert.False(t, ShouldAutoGrantTrialOnPublicEmailFlows(config.RegisterConfig{})) assert.False(t, ShouldAutoGrantTrialOnPublicEmailFlows(config.RegisterConfig{ EnableTrial: true, EnableTrialEmailWhitelist: true, TrialEmailDomainWhitelist: "gmail.com,example.com", })) } func TestIsEmailDomainWhitelisted(t *testing.T) { whitelist := "gmail.com,edu.cn,outlook.com" tests := []struct { email string want bool }{ {"user@gmail.com", true}, {"user@edu.cn", true}, {"User@Gmail.COM", true}, {"user@yahoo.com", false}, {"user@fake.gmail.com", false}, // subdomain not matched {"user@", false}, {"notanemail", false}, {"@gmail.com", false}, } for _, tt := range tests { t.Run(tt.email, func(t *testing.T) { got := IsEmailDomainWhitelisted(tt.email, whitelist) if got != tt.want { t.Errorf("IsEmailDomainWhitelisted(%q) = %v, want %v", tt.email, got, tt.want) } }) } }