227 lines
6.7 KiB
Go
227 lines
6.7 KiB
Go
package logic
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"spark-server/internal/module/client/entity"
|
|
userentity "spark-server/internal/module/user/entity"
|
|
"spark-server/internal/pkg/errs"
|
|
"spark-server/internal/pkg/i18n"
|
|
"spark-server/internal/pkg/reqctx"
|
|
)
|
|
|
|
type stubClientRepo struct {
|
|
legal map[string]map[string]*entity.LegalDocument
|
|
feedback []entity.Feedback
|
|
plans []entity.SubscriptionPlan
|
|
passwords map[int64]string
|
|
}
|
|
|
|
func (s *stubClientRepo) GetLegal(_ context.Context, lang string) (*entity.LegalDocument, *entity.LegalDocument, error) {
|
|
if s.legal == nil {
|
|
return nil, nil, errs.ErrNotFound
|
|
}
|
|
byLang := s.legal[lang]
|
|
if byLang == nil {
|
|
return nil, nil, errs.ErrNotFound
|
|
}
|
|
return byLang[entity.DocPrivacy], byLang[entity.DocTerms], nil
|
|
}
|
|
|
|
func (s *stubClientRepo) UpsertLegal(_ context.Context, d *entity.LegalDocument) error {
|
|
if s.legal == nil {
|
|
s.legal = map[string]map[string]*entity.LegalDocument{}
|
|
}
|
|
if s.legal[d.Lang] == nil {
|
|
s.legal[d.Lang] = map[string]*entity.LegalDocument{}
|
|
}
|
|
cp := *d
|
|
s.legal[d.Lang][d.Key] = &cp
|
|
return nil
|
|
}
|
|
|
|
func (s *stubClientRepo) InsertFeedback(_ context.Context, f *entity.Feedback) error {
|
|
f.ID = int64(len(s.feedback) + 1)
|
|
s.feedback = append(s.feedback, *f)
|
|
return nil
|
|
}
|
|
|
|
func (s *stubClientRepo) ListFeedback(_ context.Context, _, _ int) ([]entity.Feedback, int64, error) {
|
|
return s.feedback, int64(len(s.feedback)), nil
|
|
}
|
|
|
|
func (s *stubClientRepo) UpdateFeedbackStatus(_ context.Context, id int64, status string) error {
|
|
for i := range s.feedback {
|
|
if s.feedback[i].ID == id {
|
|
s.feedback[i].Status = status
|
|
return nil
|
|
}
|
|
}
|
|
return errs.ErrNotFound
|
|
}
|
|
|
|
func (s *stubClientRepo) ListPlans(_ context.Context, lang string) ([]entity.SubscriptionPlan, error) {
|
|
out := make([]entity.SubscriptionPlan, 0)
|
|
for i := range s.plans {
|
|
p := &s.plans[i]
|
|
if p.Lang == lang && p.Enabled {
|
|
out = append(out, *p)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *stubClientRepo) ListPlansAdmin(_ context.Context, lang string) ([]entity.SubscriptionPlan, error) {
|
|
if lang == "" {
|
|
return s.plans, nil
|
|
}
|
|
out := make([]entity.SubscriptionPlan, 0)
|
|
for i := range s.plans {
|
|
p := &s.plans[i]
|
|
if p.Lang == lang {
|
|
out = append(out, *p)
|
|
}
|
|
}
|
|
return out, nil
|
|
}
|
|
|
|
func (s *stubClientRepo) UpsertPlan(_ context.Context, p *entity.SubscriptionPlan) error {
|
|
s.plans = append(s.plans, *p)
|
|
return nil
|
|
}
|
|
|
|
func (s *stubClientRepo) GetPasswordHash(_ context.Context, userID int64) (string, error) {
|
|
if s.passwords == nil {
|
|
return "", nil
|
|
}
|
|
return s.passwords[userID], nil
|
|
}
|
|
|
|
func (s *stubClientRepo) SetPasswordHash(_ context.Context, userID int64, hash string) error {
|
|
if s.passwords == nil {
|
|
s.passwords = map[int64]string{}
|
|
}
|
|
s.passwords[userID] = hash
|
|
return nil
|
|
}
|
|
|
|
type stubUserFinder struct {
|
|
byEmail map[string]*userentity.User
|
|
}
|
|
|
|
func (s stubUserFinder) GetByEmail(_ context.Context, email string) (*userentity.User, error) {
|
|
u := s.byEmail[userentity.NormalizeEmail(email)]
|
|
if u == nil {
|
|
return nil, errs.ErrNotFound
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
type stubOTP struct {
|
|
user *userentity.User
|
|
}
|
|
|
|
func (s stubOTP) AuthenticateEmail(context.Context, string, string, *userentity.LoginDevice) (*userentity.User, error) {
|
|
if s.user == nil {
|
|
return nil, errs.ErrUnauthorized
|
|
}
|
|
return s.user, nil
|
|
}
|
|
|
|
func ctxLang(lang string) context.Context {
|
|
return reqctx.With(context.Background(), &reqctx.RequestContext{Language: lang})
|
|
}
|
|
|
|
func TestLegalPlansFeedbackFallback(t *testing.T) {
|
|
svc := NewService(nil, nil, nil)
|
|
legal, err := svc.LegalAgreements(t.Context())
|
|
if err != nil || legal.Privacy.Title == "" || legal.Terms.Title == "" {
|
|
t.Fatalf("legal %+v err=%v", legal, err)
|
|
}
|
|
plans, err := svc.SubscriptionPlans(t.Context())
|
|
if err != nil || len(plans.Plans) != 4 {
|
|
t.Fatalf("plans %+v err=%v", plans, err)
|
|
}
|
|
fb, err := svc.SubmitFeedback(t.Context(), FeedbackInput{Content: "界面很好", Email: "a@b.com"})
|
|
if err != nil || fb.ID == 0 {
|
|
t.Fatalf("feedback %+v err=%v", fb, err)
|
|
}
|
|
}
|
|
|
|
func TestClientServiceAdminAndRepo(t *testing.T) {
|
|
repo := &stubClientRepo{
|
|
legal: map[string]map[string]*entity.LegalDocument{
|
|
string(i18n.ZhCN): {
|
|
entity.DocPrivacy: {Key: entity.DocPrivacy, Lang: string(i18n.ZhCN), Title: "P", Content: "c", Version: "1"},
|
|
entity.DocTerms: {Key: entity.DocTerms, Lang: string(i18n.ZhCN), Title: "T", Content: "c", Version: "1"},
|
|
},
|
|
},
|
|
plans: []entity.SubscriptionPlan{{PlanID: "month", Lang: string(i18n.ZhCN), Title: "月", Enabled: true}},
|
|
}
|
|
svc := NewService(repo, nil, nil)
|
|
if !svc.Ready() {
|
|
t.Fatal("ready")
|
|
}
|
|
ctx := ctxLang("en")
|
|
bundle, err := svc.LegalAgreements(ctx)
|
|
if err != nil || bundle.Privacy.Title != "P" {
|
|
t.Fatalf("legal %+v %v", bundle, err)
|
|
}
|
|
if err := svc.UpsertLegalAdmin(ctx, &entity.LegalDocument{Key: "bad", Lang: "en", Title: "x", Content: "y"}); err == nil {
|
|
t.Fatal("bad key")
|
|
}
|
|
if err := svc.UpsertLegalAdmin(ctx, &entity.LegalDocument{Key: entity.DocPrivacy, Lang: "en", Title: "Privacy", Content: "body"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
list, total, err := svc.ListFeedbackAdmin(ctx, 0, 10)
|
|
if err != nil || total != 0 || len(list) != 0 {
|
|
t.Fatalf("feedback list %+v %d %v", list, total, err)
|
|
}
|
|
_, err = svc.SubmitFeedback(ctx, FeedbackInput{Content: "ok"})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.UpdateFeedbackStatus(ctx, 1, "closed"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
adminPlans, err := svc.ListPlansAdmin(ctx, string(i18n.ZhCN))
|
|
if err != nil || len(adminPlans) != 1 {
|
|
t.Fatalf("plans %+v %v", adminPlans, err)
|
|
}
|
|
if err := svc.UpsertPlanAdmin(ctx, &entity.SubscriptionPlan{PlanID: "week", Lang: "en", Title: "W", PriceCents: 100}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := svc.UpsertPlanAdmin(ctx, &entity.SubscriptionPlan{PlanID: "bad", Lang: "en"}); err == nil {
|
|
t.Fatal("bad plan")
|
|
}
|
|
}
|
|
|
|
func TestClientPasswordFlow(t *testing.T) {
|
|
repo := &stubClientRepo{}
|
|
user := &userentity.User{ID: 42, Status: userentity.StatusActive}
|
|
email := "real@example.com"
|
|
svc := NewService(repo, stubUserFinder{byEmail: map[string]*userentity.User{
|
|
userentity.NormalizeEmail(email): user,
|
|
}}, stubOTP{user: user})
|
|
ctx := ctxLang(string(i18n.ZhCN))
|
|
if err := svc.SetPassword(ctx, SetPasswordInput{Email: email, VerifyCode: "1", Password: "secret1"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
got, err := svc.VerifyPassword(ctx, email, "secret1")
|
|
if err != nil || got.ID != user.ID {
|
|
t.Fatalf("verify %+v %v", got, err)
|
|
}
|
|
if _, err := svc.VerifyPassword(ctx, email, "wrong"); err == nil {
|
|
t.Fatal("want auth fail")
|
|
}
|
|
testUser := &userentity.User{ID: 7, Status: userentity.StatusActive}
|
|
testEmail := "test01@spark.com"
|
|
svc2 := NewService(nil, stubUserFinder{byEmail: map[string]*userentity.User{
|
|
userentity.NormalizeEmail(testEmail): testUser,
|
|
}}, nil)
|
|
if _, err := svc2.VerifyPassword(ctx, testEmail, "any"); err != nil {
|
|
t.Fatalf("test email skip password %v", err)
|
|
}
|
|
}
|