Initial commit: 可转债轮动分析系统
- 实时行情:东财push2主源 + 腾讯批量报价备用源(自动切换) - 历史K线:东财push2his主源 + 腾讯web子域备用源(含熔断机制) - 增量同步:支持 -incr/-since 参数按存档日期增量入库 - 策略回测:净值曲线/10指标/基准对比,含基本面过滤 - 用户认证:用户名密码 + 邮箱验证码双登录方式 - 前端:三Tab SPA(实时行情/策略回测/数据管理)
This commit is contained in:
153
internal/service/auth_test.go
Normal file
153
internal/service/auth_test.go
Normal file
@@ -0,0 +1,153 @@
|
||||
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{}
|
||||
}
|
||||
Reference in New Issue
Block a user