247 lines
7.5 KiB
Go
247 lines
7.5 KiB
Go
package services
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"golang.org/x/crypto/bcrypt"
|
|
|
|
"git.beifan.cn/trace-system/backend-go/database"
|
|
"git.beifan.cn/trace-system/backend-go/models"
|
|
)
|
|
|
|
func TestUsersService_Create_Success(t *testing.T) {
|
|
svc := UsersService{}
|
|
dto, err := svc.Create(models.CreateUserDTO{
|
|
Password: "password123",
|
|
Name: "新技术员",
|
|
Email: "new@example.com",
|
|
Phone: "13800000001",
|
|
EmployeeNo: "users_create_ok",
|
|
Position: "软件工程师",
|
|
Role: "software_engineer",
|
|
})
|
|
assert.NoError(t, err)
|
|
assert.NotNil(t, dto)
|
|
assert.Equal(t, "users_create_ok", dto.Username)
|
|
assert.Equal(t, "software_engineer", dto.Role)
|
|
assert.Equal(t, "13800000001", dto.Phone)
|
|
assert.Equal(t, "users_create_ok", dto.EmployeeNo)
|
|
assert.Len(t, dto.EmployeeSerials, 1)
|
|
|
|
database.DB.Unscoped().Where("username = ?", "users_create_ok").Delete(&models.User{})
|
|
database.DB.Unscoped().Where("employee_name = ?", "新技术员").Delete(&models.EmployeeSerial{})
|
|
}
|
|
|
|
func TestUsersService_Create_BlocksEmployeeRole(t *testing.T) {
|
|
svc := UsersService{}
|
|
_, err := svc.Create(models.CreateUserDTO{
|
|
Name: "普通员工",
|
|
Phone: "13800000002",
|
|
EmployeeNo: "employee_no_pwd",
|
|
Position: "生产员工",
|
|
Role: "employee",
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "权限管理只能创建")
|
|
}
|
|
|
|
func TestUsersService_Create_BackendRoleRequiresPassword(t *testing.T) {
|
|
svc := UsersService{}
|
|
_, err := svc.Create(models.CreateUserDTO{
|
|
Name: "无密码技术员",
|
|
Phone: "13800000003",
|
|
EmployeeNo: "tech_no_pwd",
|
|
Position: "软件工程师",
|
|
Role: "software_engineer",
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "必须设置")
|
|
}
|
|
|
|
func TestUsersService_Create_DuplicateUsername(t *testing.T) {
|
|
user := models.User{
|
|
Username: "users_create_dup",
|
|
Password: "x",
|
|
Name: "existing",
|
|
Role: "software_engineer",
|
|
}
|
|
database.DB.Create(&user)
|
|
defer database.DB.Unscoped().Delete(&user)
|
|
|
|
svc := UsersService{}
|
|
_, err := svc.Create(models.CreateUserDTO{
|
|
Username: "users_create_dup",
|
|
Password: "password123",
|
|
Name: "duplicate",
|
|
Phone: "13800000004",
|
|
EmployeeNo: "users_create_dup_2",
|
|
Position: "软件工程师",
|
|
Role: "software_engineer",
|
|
})
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "用户名已存在")
|
|
}
|
|
|
|
func TestUsersService_Update_BlocksSelfDemotion(t *testing.T) {
|
|
admin := models.User{
|
|
Username: "users_self_admin",
|
|
Password: "x",
|
|
Name: "self admin",
|
|
Role: "admin",
|
|
}
|
|
database.DB.Create(&admin)
|
|
defer database.DB.Unscoped().Delete(&admin)
|
|
|
|
svc := UsersService{}
|
|
_, err := svc.Update(admin.ID, models.UpdateUserDTO{
|
|
Role: "software_engineer",
|
|
}, admin.ID)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "不能通过权限管理修改管理员角色")
|
|
}
|
|
|
|
func TestUsersService_Update_AllowsRoleChangeForOthers(t *testing.T) {
|
|
currentAdmin := models.User{
|
|
Username: "users_update_admin",
|
|
Password: "x",
|
|
Name: "current",
|
|
Role: "admin",
|
|
}
|
|
target := models.User{
|
|
Username: "users_update_target",
|
|
Password: "x",
|
|
Name: "target",
|
|
Role: "software_engineer",
|
|
}
|
|
database.DB.Create(¤tAdmin)
|
|
database.DB.Create(&target)
|
|
defer database.DB.Unscoped().Delete(¤tAdmin)
|
|
defer database.DB.Unscoped().Delete(&target)
|
|
|
|
svc := UsersService{}
|
|
updated, err := svc.Update(target.ID, models.UpdateUserDTO{
|
|
Name: "新名字",
|
|
Role: "project_manager",
|
|
}, currentAdmin.ID)
|
|
assert.NoError(t, err)
|
|
assert.Equal(t, "新名字", updated.Name)
|
|
assert.Equal(t, "project_manager", updated.Role)
|
|
}
|
|
|
|
func TestUsersService_ResetPassword_ChangesHash(t *testing.T) {
|
|
hashed, _ := bcrypt.GenerateFromPassword([]byte("oldpass"), bcrypt.DefaultCost)
|
|
user := models.User{
|
|
Username: "users_reset_pwd",
|
|
Password: string(hashed),
|
|
Name: "reset",
|
|
Role: "software_engineer",
|
|
}
|
|
database.DB.Create(&user)
|
|
defer database.DB.Unscoped().Delete(&user)
|
|
|
|
svc := UsersService{}
|
|
err := svc.ResetPassword(user.ID, "newpass456")
|
|
assert.NoError(t, err)
|
|
|
|
var refreshed models.User
|
|
database.DB.First(&refreshed, user.ID)
|
|
assert.NoError(t, bcrypt.CompareHashAndPassword([]byte(refreshed.Password), []byte("newpass456")))
|
|
assert.Error(t, bcrypt.CompareHashAndPassword([]byte(refreshed.Password), []byte("oldpass")))
|
|
}
|
|
|
|
func TestUsersService_Delete_BlocksSelf(t *testing.T) {
|
|
user := models.User{
|
|
Username: "users_delete_self",
|
|
Password: "x",
|
|
Name: "self",
|
|
Role: "admin",
|
|
}
|
|
database.DB.Create(&user)
|
|
defer database.DB.Unscoped().Delete(&user)
|
|
|
|
svc := UsersService{}
|
|
err := svc.Delete(user.ID, user.ID)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "不能删除自己")
|
|
}
|
|
|
|
func TestUsersService_Delete_BlocksLastAdmin(t *testing.T) {
|
|
// 清理可能存在的其他 admin(来自 seed 或前面测试)
|
|
database.DB.Unscoped().Where("role = ?", "admin").Delete(&models.User{})
|
|
|
|
last := models.User{
|
|
Username: "users_last_admin",
|
|
Password: "x",
|
|
Name: "last",
|
|
Role: "admin",
|
|
}
|
|
other := models.User{
|
|
Username: "users_other_admin_actor",
|
|
Password: "x",
|
|
Name: "actor",
|
|
Role: "admin",
|
|
}
|
|
database.DB.Create(&last)
|
|
database.DB.Create(&other)
|
|
|
|
svc := UsersService{}
|
|
// 当前调用者是 other(不是 last),但删除会让 last 变成最后一个 admin
|
|
// 删 last 时计数会变 0,所以拒绝
|
|
database.DB.Unscoped().Delete(&other)
|
|
err := svc.Delete(last.ID, 999999)
|
|
assert.Error(t, err)
|
|
assert.Contains(t, err.Error(), "不能删除最后一个管理员")
|
|
|
|
database.DB.Unscoped().Delete(&last)
|
|
}
|
|
|
|
func TestUsersService_FindAssignable_ReturnsWorkOrderRoles(t *testing.T) {
|
|
a := models.User{Username: "assignable_admin", Password: "x", Name: "A", Role: "admin"}
|
|
tech := models.User{Username: "assignable_tech", Password: "x", Name: "T", Role: "technician"}
|
|
software := models.User{Username: "assignable_software", Password: "x", Name: "S", Role: "software_engineer"}
|
|
plain := models.User{Username: "assignable_user", Password: "x", Name: "U", Role: "employee"}
|
|
database.DB.Create(&a)
|
|
database.DB.Create(&tech)
|
|
database.DB.Create(&software)
|
|
database.DB.Create(&plain)
|
|
defer database.DB.Unscoped().Delete(&a)
|
|
defer database.DB.Unscoped().Delete(&tech)
|
|
defer database.DB.Unscoped().Delete(&software)
|
|
defer database.DB.Unscoped().Delete(&plain)
|
|
|
|
svc := UsersService{}
|
|
users, err := svc.FindAssignable()
|
|
assert.NoError(t, err)
|
|
|
|
usernames := make(map[string]string)
|
|
for _, u := range users {
|
|
usernames[u.Username] = u.Role
|
|
}
|
|
assert.Equal(t, "technician", usernames["assignable_tech"])
|
|
assert.Equal(t, "software_engineer", usernames["assignable_software"])
|
|
_, hasAdmin := usernames["assignable_admin"]
|
|
assert.False(t, hasAdmin, "admin should not be assignable")
|
|
_, hasPlain := usernames["assignable_user"]
|
|
assert.False(t, hasPlain, "plain user should not be assignable")
|
|
}
|
|
|
|
func TestUsersService_FindAll_FilterByRole(t *testing.T) {
|
|
tech1 := models.User{Username: "findall_tech1", Password: "x", Name: "T1", Role: "software_engineer"}
|
|
tech2 := models.User{Username: "findall_tech2", Password: "x", Name: "T2", Role: "software_engineer"}
|
|
user1 := models.User{Username: "findall_user1", Password: "x", Name: "U1", Role: "employee"}
|
|
database.DB.Create(&tech1)
|
|
database.DB.Create(&tech2)
|
|
database.DB.Create(&user1)
|
|
defer database.DB.Unscoped().Delete(&tech1)
|
|
defer database.DB.Unscoped().Delete(&tech2)
|
|
defer database.DB.Unscoped().Delete(&user1)
|
|
|
|
svc := UsersService{}
|
|
results, _, _, err := svc.FindAll(1, 50, "software_engineer", "")
|
|
assert.NoError(t, err)
|
|
for _, u := range results {
|
|
assert.Equal(t, "software_engineer", u.Role)
|
|
}
|
|
}
|