152 lines
5.0 KiB
Go
152 lines
5.0 KiB
Go
package logic
|
|
|
|
import (
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
userlogic "spark-server/internal/module/user/logic"
|
|
usermodel "spark-server/internal/module/user/model"
|
|
"spark-server/internal/pkg/errs"
|
|
"spark-server/internal/pkg/reqctx"
|
|
)
|
|
|
|
func TestLoginServiceValidatesAndIssues(t *testing.T) {
|
|
j, err := NewJWT(&Options{Secret: "test-secret-test-secret", Issuer: "spark-im"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
svc := NewLoginService(j, StaticUser{Email: "test01@spark.com", UserID: 11, SparkID: "test01"})
|
|
ctx := reqctx.With(t.Context(), &reqctx.RequestContext{RealIP: "203.0.113.10"})
|
|
|
|
_, err = svc.Login(ctx, &LoginInput{Email: "test01@spark.com"})
|
|
app, ok := errs.AsAppError(err)
|
|
if !ok || app.Code != errs.ErrInvalidArgument.Code || !strings.Contains(app.Msg, "请填写验证码") {
|
|
t.Fatalf("validate %+v", err)
|
|
}
|
|
|
|
enCtx := reqctx.With(t.Context(), &reqctx.RequestContext{Language: "en"})
|
|
_, err = svc.Login(enCtx, &LoginInput{Email: "test01@spark.com"})
|
|
app, ok = errs.AsAppError(err)
|
|
if !ok || !strings.Contains(app.Msg, "Please enter the verification code") {
|
|
t.Fatalf("en validate %+v", err)
|
|
}
|
|
|
|
tok, err := svc.Login(ctx, &LoginInput{Email: "test01@spark.com", VerifyCode: "any", DeviceID: 7, DeviceType: "ios"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if tok.UserID != 11 || tok.DeviceID != 7 || tok.AccessToken == "" || tok.RefreshToken == "" || tok.SparkID != "test01" {
|
|
t.Fatalf("%+v", tok)
|
|
}
|
|
}
|
|
|
|
func TestLoginServiceSendRefreshLogout(t *testing.T) {
|
|
j, err := NewJWT(&Options{Secret: "test-secret-test-secret", Issuer: "spark-im"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
users := StaticUser{Email: "test01@spark.com", UserID: 11, SparkID: "test01"}
|
|
kicker := &kickRec{}
|
|
svc := NewLoginService(j, users)
|
|
svc.Sessions = kicker
|
|
ctx := reqctx.With(t.Context(), &reqctx.RequestContext{RealIP: "203.0.113.10"})
|
|
sent, err := svc.SendEmailCode(ctx, SendCodeInput{Email: "test01@spark.com"})
|
|
if err != nil || sent == nil || sent.ExpiresIn != userlogic.EmailCodeTTLSeconds {
|
|
t.Fatalf("code %+v %v", sent, err)
|
|
}
|
|
tok, err := svc.Login(ctx, &LoginInput{Email: "test01@spark.com", VerifyCode: "any", DeviceID: 7, DeviceType: "ios"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
refreshed, err := svc.Refresh(ctx, RefreshInput{RefreshToken: tok.RefreshToken})
|
|
if err != nil || refreshed.UserID != 11 {
|
|
t.Fatalf("refresh %+v %v", refreshed, err)
|
|
}
|
|
p, err := j.Authenticate(ctx, refreshed.AccessToken)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
outCtx := reqctx.With(ctx, &reqctx.RequestContext{UserID: p.UserID, DeviceID: p.DeviceID, JTI: p.JTI, TokenExpires: p.ExpiresAt})
|
|
if err := svc.Logout(outCtx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if kicker.n != 1 {
|
|
t.Fatalf("kick %d", kicker.n)
|
|
}
|
|
if _, err := svc.SendEmailCode(ctx, SendCodeInput{}); err == nil {
|
|
t.Fatal("empty email")
|
|
}
|
|
if _, err := (*LoginService)(nil).Refresh(ctx, RefreshInput{RefreshToken: "x"}); !errors.Is(err, errs.ErrUnauthorized) {
|
|
t.Fatalf("nil refresh %v", err)
|
|
}
|
|
if err := svc.Logout(ctx); !errors.Is(err, errs.ErrUnauthorized) {
|
|
t.Fatalf("logout ctx %v", err)
|
|
}
|
|
}
|
|
|
|
func TestStaticAuthenticate(t *testing.T) {
|
|
s := Static{Token: "abc", UserID: 1, DeviceID: 2}
|
|
p, err := s.Authenticate(t.Context(), "abc")
|
|
if err != nil || p.UserID != 1 || p.DeviceID != 2 {
|
|
t.Fatalf("%+v %v", p, err)
|
|
}
|
|
if _, err := s.Authenticate(t.Context(), "no"); !errors.Is(err, errs.ErrUnauthorized) {
|
|
t.Fatalf("bad token %v", err)
|
|
}
|
|
}
|
|
|
|
type kickRec struct{ n int }
|
|
|
|
func (k *kickRec) KickDevice(int64, uint64) { k.n++ }
|
|
|
|
func TestLoginServiceUnauthorized(t *testing.T) {
|
|
j, err := NewJWT(&Options{Secret: "test-secret-test-secret"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
in := LoginInput{Email: "ada@example.com", VerifyCode: "000000", DeviceID: 1, DeviceType: "web"}
|
|
svc := NewLoginService(j, StaticUser{Email: "ada@example.com", Code: "123456", UserID: 11})
|
|
if _, err = svc.Login(t.Context(), &in); !errors.Is(err, errs.ErrUnauthorized) {
|
|
t.Fatalf("bad code: %v", err)
|
|
}
|
|
in.VerifyCode = "123456"
|
|
if _, err = NewLoginService(j, nil).Login(t.Context(), &in); !errors.Is(err, errs.ErrUnauthorized) {
|
|
t.Fatalf("nil verifier: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestLoginServiceRecordsContextDevice(t *testing.T) {
|
|
j, err := NewJWT(&Options{Secret: "test-secret-test-secret", Issuer: "spark-im"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
repo := usermodel.NewMemoryRepo()
|
|
ids := seqIDs{n: 1000}
|
|
users := userlogic.NewService(repo, usermodel.NewMemoryCodes(), &ids, nil)
|
|
svc := NewLoginService(j, users)
|
|
ctx := reqctx.With(t.Context(), &reqctx.RequestContext{
|
|
RealIP: "198.51.100.9", Language: "en", AppVersion: "2.1.0",
|
|
})
|
|
tok, err := svc.Login(ctx, &LoginInput{
|
|
Email: "test07@spark.com", VerifyCode: "x", DeviceID: 9, Platform: "android", DeviceName: "Pixel",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
dev, err := repo.GetDevice(ctx, tok.UserID, 9)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if dev.DeviceType != "android" || dev.DeviceName != "Pixel" || dev.LastIP != "198.51.100.9" || dev.AppVersion != "2.1.0" || dev.Language != "en" {
|
|
t.Fatalf("%+v", dev)
|
|
}
|
|
}
|
|
|
|
type seqIDs struct{ n int64 }
|
|
|
|
func (s *seqIDs) NextID() (int64, error) {
|
|
s.n++
|
|
return s.n, nil
|
|
}
|