253 lines
6.2 KiB
Go
253 lines
6.2 KiB
Go
package test
|
|
|
|
import (
|
|
"RenewCMS/internal/domain/user"
|
|
"RenewCMS/internal/infrastructure/useCases"
|
|
"errors"
|
|
"sort"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
type stubHasher struct {
|
|
hashErr error
|
|
hashCalls int
|
|
compareCalls int
|
|
}
|
|
|
|
func (s *stubHasher) Hash(value string) (string, error) {
|
|
s.hashCalls++
|
|
if s.hashErr != nil {
|
|
return "", s.hashErr
|
|
}
|
|
return "hashed:" + value, nil
|
|
}
|
|
|
|
func (s *stubHasher) Compare(hashed string, value string) error {
|
|
s.compareCalls++
|
|
if hashed == "hashed:"+value {
|
|
return nil
|
|
}
|
|
return errors.New("hash mismatch")
|
|
}
|
|
|
|
type mockUserRepo struct {
|
|
users map[uint32]user.User
|
|
nextID uint32
|
|
fail failSwitch
|
|
clearVerificationCodeCalls int
|
|
clearVerificationCodeUserIds []uint32
|
|
updatePasswordResetCodeCalls int
|
|
lastUpdatePasswordResetCodeUserId uint32
|
|
lastUpdatePasswordResetCodeCode string
|
|
lastUpdatePasswordResetCodeExpiration time.Time
|
|
resetPasswordCalls int
|
|
lastResetPasswordUserId uint32
|
|
lastResetPasswordHash string
|
|
updateDisplayNameCalls int
|
|
lastUpdateDisplayName string
|
|
}
|
|
|
|
func newMockUserRepo() *mockUserRepo {
|
|
return &mockUserRepo{users: make(map[uint32]user.User)}
|
|
}
|
|
|
|
func newVerifiedUserRepo() *mockUserRepo {
|
|
repo := newMockUserRepo()
|
|
repo.users[1] = user.User{ID: 1, IsVerified: true}
|
|
return repo
|
|
}
|
|
|
|
func (m *mockUserRepo) sortedIds() []uint32 {
|
|
ids := make([]uint32, 0, len(m.users))
|
|
for id := range m.users {
|
|
ids = append(ids, id)
|
|
}
|
|
sort.Slice(ids, func(i, j int) bool { return ids[i] < ids[j] })
|
|
return ids
|
|
}
|
|
|
|
func (m *mockUserRepo) Get(id uint32) (user.User, error) {
|
|
if err := m.fail.trigger("Get"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[id]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) GetByUsername(username string) (user.User, error) {
|
|
if err := m.fail.trigger("GetByUsername"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
for _, id := range m.sortedIds() {
|
|
u := m.users[id]
|
|
if u.Username == username {
|
|
return u, nil
|
|
}
|
|
}
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
|
|
func (m *mockUserRepo) GetByEmail(email string) (user.User, error) {
|
|
if err := m.fail.trigger("GetByEmail"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
for _, id := range m.sortedIds() {
|
|
u := m.users[id]
|
|
if u.Email == email {
|
|
return u, nil
|
|
}
|
|
}
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
|
|
func (m *mockUserRepo) GetAll() []user.User {
|
|
result := make([]user.User, 0)
|
|
for _, id := range m.sortedIds() {
|
|
result = append(result, m.users[id])
|
|
}
|
|
return result
|
|
}
|
|
|
|
func (m *mockUserRepo) Create(u user.User) (user.User, error) {
|
|
if err := m.fail.trigger("Create"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
m.nextID++
|
|
u.ID = m.nextID
|
|
m.users[u.ID] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) Delete(id uint32) error {
|
|
if err := m.fail.trigger("Delete"); err != nil {
|
|
return err
|
|
}
|
|
if _, ok := m.users[id]; !ok {
|
|
return user.ErrUserNotFound
|
|
}
|
|
delete(m.users, id)
|
|
return nil
|
|
}
|
|
|
|
func (m *mockUserRepo) UpdateVerificationStatus(userId uint32, isVerified bool) (user.User, error) {
|
|
if err := m.fail.trigger("UpdateVerificationStatus"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.IsVerified = isVerified
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) UpdatePassword(userId uint32, password string) (user.User, error) {
|
|
if err := m.fail.trigger("UpdatePassword"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.Password = password
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) UpdatePasswordResetCode(userId uint32, code string, expiration time.Time) (user.User, error) {
|
|
m.updatePasswordResetCodeCalls++
|
|
m.lastUpdatePasswordResetCodeUserId = userId
|
|
m.lastUpdatePasswordResetCodeCode = code
|
|
m.lastUpdatePasswordResetCodeExpiration = expiration
|
|
if err := m.fail.trigger("UpdatePasswordResetCode"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.PasswordResetCode = code
|
|
u.PasswordResetExpiration = expiration
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) ClearVerificationCode(userId uint32) (user.User, error) {
|
|
m.clearVerificationCodeCalls++
|
|
m.clearVerificationCodeUserIds = append(m.clearVerificationCodeUserIds, userId)
|
|
if err := m.fail.trigger("ClearVerificationCode"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.IsVerified = true
|
|
u.VerificationCode = ""
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) ResetPassword(userId uint32, hashedPassword string) (user.User, error) {
|
|
m.resetPasswordCalls++
|
|
m.lastResetPasswordUserId = userId
|
|
m.lastResetPasswordHash = hashedPassword
|
|
if err := m.fail.trigger("ResetPassword"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.Password = hashedPassword
|
|
u.PasswordResetCode = ""
|
|
u.PasswordResetExpiration = time.Time{}
|
|
u.TokenVersion++
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
func (m *mockUserRepo) UpdateDisplayName(userId uint32, displayName string) (user.User, error) {
|
|
m.updateDisplayNameCalls++
|
|
m.lastUpdateDisplayName = displayName
|
|
if err := m.fail.trigger("UpdateDisplayName"); err != nil {
|
|
return user.User{}, err
|
|
}
|
|
u, ok := m.users[userId]
|
|
if !ok {
|
|
return user.User{}, user.ErrUserNotFound
|
|
}
|
|
u.DisplayName = displayName
|
|
m.users[userId] = u
|
|
return u, nil
|
|
}
|
|
|
|
var _ user.Repository = &mockUserRepo{}
|
|
|
|
func TestCreateUser(t *testing.T) {
|
|
repo := newMockUserRepo()
|
|
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{}, newMockMailRepo(), "https://cms.example.com", discardRecorder)
|
|
|
|
cmd := useCases.CreateUserCommand{
|
|
Username: "testuser",
|
|
Password: "password123",
|
|
Email: "test@example.com",
|
|
}
|
|
|
|
res, err := uc.CreateUser(testCtx, cmd)
|
|
if err != nil {
|
|
t.Fatalf("User creation failed: %v", err)
|
|
}
|
|
if res.Username != cmd.Username {
|
|
t.Errorf("Username mismatch")
|
|
}
|
|
if res.VerificationCode == "" {
|
|
t.Errorf("Verification code was not generated")
|
|
}
|
|
}
|