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 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 }