Files
stock-analysis/internal/service/auth_test.go
WarrenZhou e28bfec957 Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换)
- 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制)
- 增量同步:支持 -incr/-since 参数按存档日期增量入库
- 策略回测:净值曲线/10指标/基准对比,含基本面过滤
- 用户认证:用户名密码 + 邮箱验证码双登录方式
- 前端:三Tab SPA(实时行情/策略回测/数据管理)
2026-08-17 20:23:22 +08:00

153 lines
3.6 KiB
Go

package service
import (
"context"
"os"
"testing"
"stock-analysis/internal/model"
"stock-analysis/internal/storage/sqlite"
)
type testMailer struct {
lastEmail string
lastCode string
}
func (m *testMailer) SendCode(email, code string) error {
m.lastEmail = email
m.lastCode = code
return nil
}
func TestAuthService(t *testing.T) {
ctx := context.Background()
dbPath := "test_auth.db"
_ = os.Remove(dbPath)
defer os.Remove(dbPath)
store, err := sqlite.Open(dbPath)
if err != nil {
t.Fatalf("open sqlite: %v", err)
}
defer store.Close()
svc := NewAuthService(store)
mailer := &testMailer{}
svc.SetMailer(mailer)
email := "Test@Example.COM "
normalized := "test@example.com"
username := "testuser"
// 1. 发送注册验证码
if err := svc.SendVerifyCode(ctx, email, "register"); err != nil {
t.Fatalf("send register code: %v", err)
}
if mailer.lastEmail != normalized {
t.Fatalf("email not normalized: got %s want %s", mailer.lastEmail, normalized)
}
if len(mailer.lastCode) != 6 {
t.Fatalf("code length invalid: %s", mailer.lastCode)
}
// 2. 验证码注册(含用户名)
u, err := svc.Register(ctx, username, email, mailer.lastCode, "123456")
if err != nil {
t.Fatalf("register: %v", err)
}
if u.Email != normalized {
t.Fatalf("registered email: got %s want %s", u.Email, normalized)
}
if u.Username != username {
t.Fatalf("registered username: got %s want %s", u.Username, username)
}
// 3. 重复注册应失败
if _, err := svc.Register(ctx, username, email, mailer.lastCode, "123456"); err == nil {
t.Fatal("duplicate register should fail")
}
// 4. 发送登录验证码
if err := svc.SendVerifyCode(ctx, email, "login"); err != nil {
t.Fatalf("send login code: %v", err)
}
// 5. 验证码登录
logged, token, err := svc.Login(ctx, email, mailer.lastCode)
if err != nil {
t.Fatalf("login: %v", err)
}
if logged.ID != u.ID {
t.Fatal("login user id mismatch")
}
if token == "" {
t.Fatal("login token empty")
}
// 6. 用户名+密码登录
logged2, token2, err := svc.LoginByUsername(ctx, username, "123456")
if err != nil {
t.Fatalf("login by username: %v", err)
}
if logged2.ID != u.ID {
t.Fatal("username login user id mismatch")
}
if token2 == "" {
t.Fatal("username login token empty")
}
// 7. token 校验
validated, err := svc.ValidateToken(ctx, token2)
if err != nil {
t.Fatalf("validate token: %v", err)
}
if validated.ID != u.ID {
t.Fatal("validated user id mismatch")
}
// 8. 退出后 token 失效
svc.Logout(token2)
if _, err := svc.ValidateToken(ctx, token2); err == nil {
t.Fatal("token should be invalid after logout")
}
// 9. 密码错误
if _, _, err := svc.LoginByUsername(ctx, username, "wrong"); err == nil {
t.Fatal("wrong password should fail")
}
// 10. 验证码过期/错误
if _, _, err := svc.Login(ctx, email, "000000"); err == nil {
t.Fatal("used/wrong code should fail")
}
// 11. 不存在用户
if _, _, err := svc.Login(ctx, "notfound@example.com", "123456"); err == nil {
t.Fatal("nonexistent user should fail")
}
// 12. EnsureAdmin 创建 admin
if err := svc.EnsureAdmin(ctx); err != nil {
t.Fatalf("ensure admin: %v", err)
}
// 第二次调用应不报错(幂等)
if err := svc.EnsureAdmin(ctx); err != nil {
t.Fatalf("ensure admin idempotent: %v", err)
}
// admin 可以登录
admin, adminToken, err := svc.LoginByUsername(ctx, "admin", "123456")
if err != nil {
t.Fatalf("admin login: %v", err)
}
if admin.Username != "admin" {
t.Fatal("admin username mismatch")
}
if adminToken == "" {
t.Fatal("admin token empty")
}
// 验证 model 类型
_ = model.User{}
_ = model.VerifyCode{}
}