Files
stock-manager/tests/unit/actions/user.actions.test.ts
T

297 lines
8.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from "vitest"
import { es } from "@/i18n/dictionaries/es"
const mocks = vi.hoisted(() => ({
revalidatePath: vi.fn(),
getI18n: vi.fn(),
requireRole: vi.fn(),
createUserUseCase: vi.fn(),
updateUserUseCase: vi.fn(),
setUserActiveUseCase: vi.fn(),
resetUserPasswordUseCase: vi.fn(),
}))
vi.mock("next/cache", () => ({
revalidatePath: mocks.revalidatePath,
}))
vi.mock("@/i18n/server", () => ({
getI18n: mocks.getI18n,
}))
vi.mock("@/services/auth.service", () => ({
requireRole: mocks.requireRole,
}))
vi.mock("@/use-cases/user.use-cases", () => ({
createUserUseCase: mocks.createUserUseCase,
updateUserUseCase: mocks.updateUserUseCase,
setUserActiveUseCase: mocks.setUserActiveUseCase,
resetUserPasswordUseCase: mocks.resetUserPasswordUseCase,
}))
import {
createUserAction,
resetUserPasswordAction,
setUserActiveAction,
updateUserAction,
} from "@/actions/user.actions"
describe("user actions localization", () => {
beforeEach(() => {
vi.clearAllMocks()
mocks.getI18n.mockResolvedValue({ dictionary: es, locale: "es" })
mocks.requireRole.mockResolvedValue({
user: { id: "admin-1" },
})
})
describe("createUserAction", () => {
it("returns localized schema validation errors for invalid create input", async () => {
const result = await createUserAction({
name: "",
email: "bad",
password: "short",
role: "ADMIN",
isActive: true,
})
expect(mocks.getI18n).toHaveBeenCalledOnce()
expect(mocks.createUserUseCase).not.toHaveBeenCalled()
expect(result).toEqual({
success: false,
errors: {
name: [es.admin.users.schema.nameRequired],
email: [es.admin.users.schema.emailInvalid],
password: [es.admin.users.schema.passwordMinLength],
},
})
})
it("localizes mapped duplicate field errors for create failures", async () => {
mocks.createUserUseCase.mockResolvedValue({
success: false,
errors: {
email: ["Email already exists"],
},
})
const result = await createUserAction({
name: "Ada",
email: "ada@example.test",
password: "password1",
role: "STAFF",
isActive: true,
})
expect(result).toEqual({
success: false,
errors: {
email: [es.admin.users.actions.duplicateEmail],
},
message: es.admin.users.actions.createFailure,
})
})
it("returns a localized create success message and revalidates", async () => {
mocks.createUserUseCase.mockResolvedValue({ success: true })
const result = await createUserAction({
name: "Ada",
email: "ada@example.test",
password: "password1",
role: "STAFF",
isActive: true,
})
expect(result).toEqual({
success: true,
message: es.admin.users.actions.createSuccess,
})
expect(mocks.revalidatePath).toHaveBeenCalledWith("/people")
})
})
describe("updateUserAction", () => {
it("returns localized schema validation errors for invalid update input", async () => {
const result = await updateUserAction({
id: "",
name: "",
email: "bad",
role: "ADMIN",
isActive: true,
})
expect(mocks.getI18n).toHaveBeenCalledOnce()
expect(mocks.updateUserUseCase).not.toHaveBeenCalled()
expect(result).toEqual({
success: false,
errors: {
id: [es.admin.users.schema.userIdRequired],
name: [es.admin.users.schema.nameRequired],
email: [es.admin.users.schema.emailInvalid],
},
})
})
it("localizes mapped use-case errors for update failures", async () => {
mocks.updateUserUseCase.mockResolvedValue({
success: false,
errors: {
id: [
"Cannot remove access from the last active administrator",
"You cannot remove your own administrator access",
],
},
})
const result = await updateUserAction({
id: "user-1",
name: "Admin",
email: "admin@example.test",
role: "MANAGER",
isActive: true,
})
expect(result).toEqual({
success: false,
errors: {
id: [
es.admin.users.actions.lastActiveAdmin,
es.admin.users.actions.selfAdminAccess,
],
},
message: es.admin.users.actions.updateFailure,
})
})
it("returns a localized update success message and revalidates", async () => {
mocks.updateUserUseCase.mockResolvedValue({ success: true })
const result = await updateUserAction({
id: "user-1",
name: "Admin",
email: "admin@example.test",
role: "ADMIN",
isActive: true,
})
expect(result).toEqual({
success: true,
message: es.admin.users.actions.updateSuccess,
})
expect(mocks.revalidatePath).toHaveBeenCalledWith("/people")
})
})
describe("setUserActiveAction", () => {
it("returns localized schema validation errors for invalid input", async () => {
const result = await setUserActiveAction({
id: "",
isActive: false,
})
expect(mocks.getI18n).toHaveBeenCalledOnce()
expect(mocks.setUserActiveUseCase).not.toHaveBeenCalled()
expect(result).toEqual({
success: false,
errors: {
id: [es.admin.users.schema.userIdRequired],
},
})
})
it("localizes mapped use-case errors for toggle failures", async () => {
mocks.setUserActiveUseCase.mockResolvedValue({
success: false,
errors: {
id: ["You cannot deactivate your own user"],
},
})
const result = await setUserActiveAction({
id: "user-1",
isActive: false,
})
expect(result).toEqual({
success: false,
errors: {
id: [es.admin.users.actions.selfDeactivate],
},
message: es.admin.users.actions.toggleStatusFailure,
})
})
it("returns a localized toggle status success message and revalidates", async () => {
mocks.setUserActiveUseCase.mockResolvedValue({ success: true })
const result = await setUserActiveAction({
id: "user-1",
isActive: false,
})
expect(result).toEqual({
success: true,
message: es.admin.users.actions.toggleStatusSuccess,
})
expect(mocks.revalidatePath).toHaveBeenCalledWith("/people")
})
})
describe("resetUserPasswordAction", () => {
it("returns localized schema validation errors for invalid input", async () => {
const result = await resetUserPasswordAction({
id: "",
password: "short",
})
expect(mocks.getI18n).toHaveBeenCalledOnce()
expect(mocks.resetUserPasswordUseCase).not.toHaveBeenCalled()
expect(result).toEqual({
success: false,
errors: {
id: [es.admin.users.schema.userIdRequired],
password: [es.admin.users.schema.passwordMinLength],
},
})
})
it("localizes mapped use-case errors for reset failures", async () => {
mocks.resetUserPasswordUseCase.mockResolvedValue({
success: false,
errors: { id: ["User not found"] },
})
const result = await resetUserPasswordAction({
id: "nonexistent",
password: "newpassword1",
})
expect(result).toEqual({
success: false,
errors: {
id: [es.admin.users.actions.notFound],
},
message: es.admin.users.actions.resetPasswordFailure,
})
})
it("returns a localized reset success message and revalidates", async () => {
mocks.resetUserPasswordUseCase.mockResolvedValue({ success: true })
const result = await resetUserPasswordAction({
id: "user-1",
password: "newpassword1",
})
expect(result).toEqual({
success: true,
message: es.admin.users.actions.resetPasswordSuccess,
})
expect(mocks.revalidatePath).toHaveBeenCalledWith("/people")
})
})
})