feat: Users API, ownership transfer and removal of the legacy role string

This commit is contained in:
Florian Sylvain committed 2026-10-27 03:08:15 +01:00
1 parent 094df63a7f
commit 80f7037d3a
124 files changed
+4644 -824

No files matched your search

+80 -1
View File
@@ -1,6 +1,7 @@
package test
import (
"RenewCMS/internal/domain/role"
"RenewCMS/internal/domain/user"
"RenewCMS/internal/infrastructure/useCases"
"errors"
@@ -47,6 +48,13 @@ type mockUserRepo struct {
lastResetPasswordHash string
updateDisplayNameCalls int
lastUpdateDisplayName string
getAllCalls int
updateRoleCalls int
transferCalls int
beforeUpdateRole func()
beforeTransfer func()
beforeClearVerification func()
knownRoles *mockRoleRepo
}
func newMockUserRepo() *mockUserRepo {
@@ -106,6 +114,7 @@ func (m *mockUserRepo) GetByEmail(email string) (user.User, error) {
}
func (m *mockUserRepo) GetAll() []user.User {
m.getAllCalls++
result := make([]user.User, 0)
for _, id := range m.sortedIds() {
result = append(result, m.users[id])
@@ -198,10 +207,22 @@ func (m *mockUserRepo) ClearVerificationCode(userId uint32) (user.User, error) {
if err := m.fail.trigger("ClearVerificationCode"); err != nil {
return user.User{}, err
}
if m.beforeClearVerification != nil {
hook := m.beforeClearVerification
m.beforeClearVerification = nil
hook()
}
u, ok := m.users[userId]
if !ok {
return user.User{}, user.ErrUserNotFound
}
if u.RoleID == ownerRoleID {
for id, other := range m.users {
if id != userId && other.RoleID == ownerRoleID && other.IsVerified {
return user.User{}, user.ErrInvalidVerificationCode
}
}
}
u.IsVerified = true
u.VerificationCode = ""
m.users[userId] = u
@@ -242,11 +263,69 @@ func (m *mockUserRepo) UpdateDisplayName(userId uint32, displayName string) (use
return u, nil
}
func (m *mockUserRepo) roleExists(id uint32) bool {
if m.knownRoles == nil {
return true
}
_, present := m.knownRoles.roles[id]
return present
}
func (m *mockUserRepo) UpdateRole(id uint32, from uint32, to uint32) (user.User, error) {
m.updateRoleCalls++
if err := m.fail.trigger("UpdateRole"); err != nil {
return user.User{}, err
}
if m.beforeUpdateRole != nil {
hook := m.beforeUpdateRole
m.beforeUpdateRole = nil
hook()
}
stored, ok := m.users[id]
if !ok {
return user.User{}, user.ErrUserNotFound
}
if !m.roleExists(to) {
return user.User{}, role.ErrRoleNotFound
}
if stored.RoleID != from {
return user.User{}, user.ErrRoleChanged
}
stored.RoleID = to
m.users[id] = stored
return stored, nil
}
func (m *mockUserRepo) TransferOwnership(from uint32, to uint32, ownerRoleID uint32, formerRoleID uint32) error {
m.transferCalls++
if err := m.fail.trigger("TransferOwnership"); err != nil {
return err
}
if m.beforeTransfer != nil {
hook := m.beforeTransfer
m.beforeTransfer = nil
hook()
}
former, formerFound := m.users[from]
if !formerFound || former.RoleID != ownerRoleID {
return user.ErrRoleChanged
}
successor, successorFound := m.users[to]
if !successorFound || !successor.IsVerified {
return user.ErrInvalidTransfer
}
former.RoleID = formerRoleID
successor.RoleID = ownerRoleID
m.users[from] = former
m.users[to] = successor
return nil
}
var _ user.Repository = &mockUserRepo{}
func TestCreateUser(t *testing.T) {
repo := newMockUserRepo()
uc := useCases.NewCreateUserUseCase(repo, &stubHasher{}, newMockMailRepo(), "https://cms.example.com", discardRecorder)
uc := useCases.NewCreateUserUseCase(repo, newSeededMockRoleRepo(), &stubHasher{}, newMockMailRepo(), "https://cms.example.com", discardRecorder)
cmd := useCases.CreateUserCommand{
Username: "testuser",