feat: add unified Person+User creation backend
This commit is contained in:
@@ -8,6 +8,7 @@ describe("user copy helpers", () => {
|
||||
MANAGER: "Gerente",
|
||||
STAFF: "Personal",
|
||||
VIEWER: "Visor",
|
||||
NO_USER: "Sin cuenta de usuario",
|
||||
}
|
||||
|
||||
const fallbackCopy = {
|
||||
|
||||
@@ -50,6 +50,7 @@ describe("admin users dictionary", () => {
|
||||
MANAGER: "Manager",
|
||||
STAFF: "Staff",
|
||||
VIEWER: "Viewer",
|
||||
NO_USER: "No user account",
|
||||
})
|
||||
|
||||
expect(users.status).toEqual({
|
||||
@@ -133,6 +134,7 @@ describe("admin users dictionary", () => {
|
||||
MANAGER: "Gerente",
|
||||
STAFF: "Personal",
|
||||
VIEWER: "Visor",
|
||||
NO_USER: "Sin cuenta de usuario",
|
||||
})
|
||||
|
||||
expect(users.status).toEqual({
|
||||
|
||||
@@ -0,0 +1,263 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
|
||||
import {
|
||||
buildUnifiedCreateSchema,
|
||||
unifiedFormRoleSchema,
|
||||
type UnifiedSchemaCopy,
|
||||
} from "@/schemas/user.schema"
|
||||
|
||||
const enCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "First name is required",
|
||||
lastNameRequired: "Last name is required",
|
||||
departmentRequired: "Department is required",
|
||||
emailInvalid: "Invalid email",
|
||||
passwordMinLength: "Password must be at least 8 characters",
|
||||
nameRequired: "Name is required",
|
||||
userIdRequired: "User id is required",
|
||||
idRequired: "ID is required",
|
||||
userIdInvalid: "User ID must be a valid UUID",
|
||||
}
|
||||
|
||||
const esCopy: UnifiedSchemaCopy = {
|
||||
firstNameRequired: "El nombre es obligatorio",
|
||||
lastNameRequired: "El apellido es obligatorio",
|
||||
departmentRequired: "El departamento es obligatorio",
|
||||
emailInvalid: "Correo electrónico no válido",
|
||||
passwordMinLength: "La contraseña debe tener al menos 8 caracteres",
|
||||
nameRequired: "El nombre es obligatorio",
|
||||
userIdRequired: "El ID de usuario es obligatorio",
|
||||
idRequired: "El ID es obligatorio",
|
||||
userIdInvalid: "El ID de usuario debe ser un UUID válido",
|
||||
}
|
||||
|
||||
const validPersonOnlyData = {
|
||||
firstName: "John",
|
||||
lastName: "Doe",
|
||||
department: "IT",
|
||||
email: "john@example.test",
|
||||
phone: null,
|
||||
role: "NO_USER" as const,
|
||||
password: undefined,
|
||||
isActive: true,
|
||||
}
|
||||
|
||||
const validPersonWithUserData = {
|
||||
firstName: "Jane",
|
||||
lastName: "Smith",
|
||||
department: "ENGINEERING",
|
||||
email: "jane@example.test",
|
||||
phone: "1234567890",
|
||||
role: "ADMIN" as const,
|
||||
password: "securepassword",
|
||||
isActive: true,
|
||||
}
|
||||
|
||||
describe("unifiedFormRoleSchema", () => {
|
||||
it("accepts all standard roles plus NO_USER", () => {
|
||||
const roles = ["ADMIN", "MANAGER", "STAFF", "VIEWER", "NO_USER"]
|
||||
for (const role of roles) {
|
||||
expect(unifiedFormRoleSchema.safeParse(role).success).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects invalid roles", () => {
|
||||
const result = unifiedFormRoleSchema.safeParse("SUPER_ADMIN")
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("buildUnifiedCreateSchema", () => {
|
||||
describe("with NO_USER role (person-only creation)", () => {
|
||||
it("accepts valid data without password when role is NO_USER", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse(validPersonOnlyData)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.firstName).toBe("John")
|
||||
expect(result.data.lastName).toBe("Doe")
|
||||
expect(result.data.role).toBe("NO_USER")
|
||||
expect(result.data.password).toBeUndefined()
|
||||
}
|
||||
})
|
||||
|
||||
it("accepts valid data with empty string password when role is NO_USER", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
password: "",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
|
||||
it("uses localized error messages for required fields", () => {
|
||||
const schema = buildUnifiedCreateSchema(esCopy)
|
||||
const result = schema.safeParse({
|
||||
firstName: "",
|
||||
lastName: "",
|
||||
department: "",
|
||||
email: "not-an-email",
|
||||
role: "NO_USER",
|
||||
phone: null,
|
||||
isActive: true,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.firstName).toContain(esCopy.firstNameRequired)
|
||||
expect(errors.lastName).toContain(esCopy.lastNameRequired)
|
||||
expect(errors.department).toContain(esCopy.departmentRequired)
|
||||
expect(errors.email).toContain(esCopy.emailInvalid)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("with a real User role (person + user creation)", () => {
|
||||
it("accepts valid data with password when role is ADMIN", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse(validPersonWithUserData)
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
if (result.success) {
|
||||
expect(result.data.firstName).toBe("Jane")
|
||||
expect(result.data.role).toBe("ADMIN")
|
||||
expect(result.data.password).toBe("securepassword")
|
||||
}
|
||||
})
|
||||
|
||||
it("accepts valid data with password for all real roles", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
for (const role of ["ADMIN", "MANAGER", "STAFF", "VIEWER"] as const) {
|
||||
const result = schema.safeParse({
|
||||
...validPersonWithUserData,
|
||||
role,
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects short password when role is not NO_USER", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonWithUserData,
|
||||
password: "short",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.password).toContain(enCopy.passwordMinLength)
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects missing password when role is not NO_USER", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonWithUserData,
|
||||
password: undefined,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.password).toContain(enCopy.passwordMinLength)
|
||||
}
|
||||
})
|
||||
|
||||
it("rejects empty string password when role is not NO_USER", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonWithUserData,
|
||||
password: "",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.password).toContain(enCopy.passwordMinLength)
|
||||
}
|
||||
})
|
||||
|
||||
it("uses localized password error message", () => {
|
||||
const schema = buildUnifiedCreateSchema(esCopy)
|
||||
const result = schema.safeParse({
|
||||
firstName: "Jane",
|
||||
lastName: "Smith",
|
||||
department: "ENGINEERING",
|
||||
email: "jane@example.test",
|
||||
role: "ADMIN",
|
||||
password: "corta",
|
||||
phone: null,
|
||||
isActive: true,
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
const errors = result.error.flatten().fieldErrors
|
||||
expect(errors.password).toContain(esCopy.passwordMinLength)
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
describe("email validation", () => {
|
||||
it("rejects invalid email format", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
email: "not-an-email",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
if (!result.success) {
|
||||
expect(result.error.flatten().fieldErrors.email).toContain(
|
||||
enCopy.emailInvalid,
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
it("accepts valid email", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
email: "valid@example.com",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe("department validation", () => {
|
||||
it("rejects invalid department", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
department: "INVALID_DEPT",
|
||||
})
|
||||
|
||||
expect(result.success).toBe(false)
|
||||
})
|
||||
|
||||
it("accepts valid departments", () => {
|
||||
const schema = buildUnifiedCreateSchema(enCopy)
|
||||
const validDepartments = [
|
||||
"IT",
|
||||
"ENGINEERING",
|
||||
"TRAFFIC",
|
||||
"DRIVER",
|
||||
"LOGISTICS",
|
||||
"ADMINISTRATION",
|
||||
"SALES",
|
||||
"OTHER",
|
||||
]
|
||||
for (const dept of validDepartments) {
|
||||
const result = schema.safeParse({
|
||||
...validPersonOnlyData,
|
||||
department: dept,
|
||||
})
|
||||
expect(result.success).toBe(true)
|
||||
}
|
||||
})
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user