Files
quyun-v2/backend/app/services/user_test.go
Rogee b78f1e1c84 feat(user): implement OTP login, user creation, and profile management
- Added SendOTP method for simulating OTP sending.
- Implemented LoginWithOTP method for user login/registration via OTP, including user creation if not found.
- Added Me method to retrieve current user information.
- Implemented Update method for updating user profile details.
- Added RealName method for real-name verification.
- Implemented GetNotifications method to fetch user notifications.
- Created user_test.go for comprehensive unit tests covering login, profile retrieval, updates, real-name verification, and notifications.
- Updated database models to use appropriate consts for fields like gender, status, and roles.
2025-12-29 10:55:13 +08:00

186 lines
4.6 KiB
Go

package services
import (
"context"
"database/sql"
"testing"
"quyun/v2/app/commands/testx"
user_dto "quyun/v2/app/http/v1/dto"
"quyun/v2/database"
"quyun/v2/database/models"
"quyun/v2/pkg/consts"
. "github.com/smartystreets/goconvey/convey"
"github.com/spf13/cast"
"github.com/stretchr/testify/suite"
"go.ipao.vip/atom/contracts"
"go.uber.org/dig"
)
type UserTestSuiteInjectParams struct {
dig.In
DB *sql.DB
Initials []contracts.Initial `group:"initials"`
}
type UserTestSuite struct {
suite.Suite
UserTestSuiteInjectParams
}
func Test_User(t *testing.T) {
providers := testx.Default().With(Provide)
testx.Serve(providers, t, func(p UserTestSuiteInjectParams) {
suite.Run(t, &UserTestSuite{UserTestSuiteInjectParams: p})
})
}
func (s *UserTestSuite) Test_LoginWithOTP() {
Convey("LoginWithOTP", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
Convey("should create user and login success with correct OTP", func() {
phone := "13800138000"
resp, err := User.LoginWithOTP(ctx, phone, "123456")
So(err, ShouldBeNil)
So(resp, ShouldNotBeNil)
So(resp.Token, ShouldNotBeEmpty)
So(resp.User.Phone, ShouldEqual, phone)
So(resp.User.Nickname, ShouldStartWith, "User_")
})
Convey("should login existing user", func() {
phone := "13800138001"
// Pre-create user
_, err := User.LoginWithOTP(ctx, phone, "123456")
So(err, ShouldBeNil)
// Login again
resp, err := User.LoginWithOTP(ctx, phone, "123456")
So(err, ShouldBeNil)
So(resp.User.Phone, ShouldEqual, phone)
})
Convey("should fail with incorrect OTP", func() {
resp, err := User.LoginWithOTP(ctx, "13800138002", "000000")
So(err, ShouldNotBeNil)
So(resp, ShouldBeNil)
})
})
}
func (s *UserTestSuite) Test_Me() {
Convey("Me", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
// Create user
phone := "13800138003"
resp, _ := User.LoginWithOTP(ctx, phone, "123456")
userID := cast.ToInt64(resp.User.ID)
Convey("should return user profile", func() {
// Mock context with user ID
ctx = context.WithValue(ctx, consts.CtxKeyUser, userID)
user, err := User.Me(ctx)
So(err, ShouldBeNil)
So(user.ID, ShouldEqual, resp.User.ID)
So(user.Phone, ShouldEqual, phone)
})
Convey("should fail without auth", func() {
// No user ID in context
user, err := User.Me(ctx)
So(err, ShouldNotBeNil)
So(user, ShouldBeNil)
})
})
}
func (s *UserTestSuite) Test_Update() {
Convey("Update", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
phone := "13800138004"
resp, _ := User.LoginWithOTP(ctx, phone, "123456")
userID := cast.ToInt64(resp.User.ID)
ctx = context.WithValue(ctx, consts.CtxKeyUser, userID)
Convey("should update nickname and bio", func() {
form := &user_dto.UserUpdate{
Nickname: "NewName",
Bio: "New Bio",
Gender: consts.GenderMale,
}
err := User.Update(ctx, form)
So(err, ShouldBeNil)
// Verify
u, _ := User.Me(ctx)
So(u.Nickname, ShouldEqual, "NewName")
So(u.Bio, ShouldEqual, "New Bio")
So(u.Gender, ShouldEqual, consts.GenderMale)
})
})
}
func (s *UserTestSuite) Test_RealName() {
Convey("RealName", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser)
phone := "13800138005"
resp, _ := User.LoginWithOTP(ctx, phone, "123456")
userID := cast.ToInt64(resp.User.ID)
ctx = context.WithValue(ctx, consts.CtxKeyUser, userID)
Convey("should update realname status", func() {
form := &user_dto.RealNameForm{
Realname: "张三",
IDCard: "123456789012345678",
}
err := User.RealName(ctx, form)
So(err, ShouldBeNil)
// Verify
u, _ := User.Me(ctx)
So(u.IsRealNameVerified, ShouldBeTrue)
})
})
}
func (s *UserTestSuite) Test_GetNotifications() {
Convey("GetNotifications", s.T(), func() {
ctx := s.T().Context()
database.Truncate(ctx, s.DB, models.TableNameUser, models.TableNameNotification)
phone := "13800138006"
resp, _ := User.LoginWithOTP(ctx, phone, "123456")
userID := cast.ToInt64(resp.User.ID)
ctx = context.WithValue(ctx, consts.CtxKeyUser, userID)
// Mock notifications
_ = models.Q.Notification.WithContext(ctx).Create(&models.Notification{
UserID: userID,
Type: "system",
Title: "Welcome",
Content: "Hello World",
IsRead: false,
})
Convey("should return notifications", func() {
list, err := User.GetNotifications(ctx, "all")
So(err, ShouldBeNil)
So(len(list), ShouldEqual, 1)
So(list[0].Title, ShouldEqual, "Welcome")
So(list[0].Type, ShouldEqual, "system")
})
})
}