From dbb2ee807d711c657c134ce3e450158e1d3e0fa0 Mon Sep 17 00:00:00 2001 From: Emiya Kiritsugu Date: Tue, 5 May 2026 19:26:15 -0300 Subject: [PATCH 1/3] test(alunos): add unit tests for CRUD actions #552 --- src/lib/actions/alunos.test.ts | 125 +++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 src/lib/actions/alunos.test.ts diff --git a/src/lib/actions/alunos.test.ts b/src/lib/actions/alunos.test.ts new file mode 100644 index 0000000..e2546e0 --- /dev/null +++ b/src/lib/actions/alunos.test.ts @@ -0,0 +1,125 @@ +import { describe, it, expect, vi, beforeEach } from 'vitest'; +import { createAlunoAction, updateAlunoAction, deleteAlunoAction } from './alunos'; +import { prisma } from '@/lib/prisma'; +import { getUser } from '@/utils/supabase/server'; +import { revalidatePath } from 'next/cache'; +import type { Aluno, AlunoBase } from '@/lib/definitions'; + +// Mocking dependencies +vi.mock('@/lib/prisma', () => ({ + prisma: { + aluno: { + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + findUnique: vi.fn(), + }, + $transaction: vi.fn(), + }, +})); + +vi.mock('@/utils/supabase/server', () => ({ + getUser: vi.fn(), +})); + +vi.mock('next/cache', () => ({ + revalidatePath: vi.fn(), +})); + +vi.mock('@sentry/nextjs', () => ({ + captureException: vi.fn(), +})); + +describe('alunos actions (CRUD Unit Tests)', () => { + const mockAdmin = { user: { email: 'admin@nextgym.com' }, error: null }; + const validId = '550e8400-e29b-41d4-a716-446655440000'; + + beforeEach(() => { + vi.clearAllMocks(); + vi.mocked(getUser).mockResolvedValue( + mockAdmin as unknown as { user: { email: string }; error: null } + ); + }); + + describe('createAlunoAction', () => { + it('should create an aluno successfully when data is valid', async () => { + const inputData = { + nomeCompleto: 'José Inamar', + cpf: '12345678900', + email: 'jose.inamar@ufrn.edu.br', + telefone: '84999999999', + statusMatricula: 'ATIVA' as const, + }; + + const mockCreatedAluno = { + id: validId, + ...inputData, + dataCadastro: new Date(), + nivel: 1, + exp: 0, + streakDiasSeguidos: 0, + treinosNoMes: 0, + }; + + vi.mocked(prisma.aluno.create).mockResolvedValue(mockCreatedAluno as unknown as Aluno); + + const result = await createAlunoAction(inputData); + + expect(result.success).toBe(true); + expect(result.data?.id).toBe(validId); + expect(prisma.aluno.create).toHaveBeenCalled(); + expect(revalidatePath).toHaveBeenCalledWith('/dashboard/alunos'); + }); + + it('should return error if unauthorized', async () => { + vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as unknown as { + user: null; + error: null; + }); + const result = await createAlunoAction( + {} as unknown as AlunoBase & + Pick + ); + expect(result.success).toBe(false); + expect(result.error).toBe('Usuário não autenticado'); + }); + }); + + describe('updateAlunoAction', () => { + it('should update an aluno successfully', async () => { + const updateData = { nomeCompleto: 'José Inamar Silva' }; + const mockUpdatedAluno = { + id: validId, + nomeCompleto: 'José Inamar Silva', + cpf: '12345678900', + email: 'jose.inamar@ufrn.edu.br', + telefone: '84999999999', + statusMatricula: 'ATIVA' as const, + dataCadastro: new Date(), + }; + + vi.mocked(prisma.aluno.update).mockResolvedValue(mockUpdatedAluno as unknown as Aluno); + + const result = await updateAlunoAction(validId, updateData); + + expect(result.success).toBe(true); + expect(result.data?.nomeCompleto).toBe('José Inamar Silva'); + expect(prisma.aluno.update).toHaveBeenCalledWith({ + where: { id: validId }, + data: expect.objectContaining({ nomeCompleto: 'José Inamar Silva' }), + }); + }); + }); + + describe('deleteAlunoAction', () => { + it('should delete an aluno successfully', async () => { + const result = await deleteAlunoAction(validId); + + expect(result.success).toBe(true); + expect(prisma.aluno.delete).toHaveBeenCalledWith({ + where: { id: validId }, + }); + expect(revalidatePath).toHaveBeenCalledWith('/dashboard/alunos'); + }); + }); +}); From e8a78fb0f6a712d91782e4dc78dc6b28cc118f6c Mon Sep 17 00:00:00 2001 From: Emiya Kiritsugu Date: Tue, 5 May 2026 19:29:05 -0300 Subject: [PATCH 2/3] test(alunos): refine unit test mocks to satisfy strict typechecking and linting #552 --- src/lib/actions/alunos.test.ts | 20 ++++++-------------- 1 file changed, 6 insertions(+), 14 deletions(-) diff --git a/src/lib/actions/alunos.test.ts b/src/lib/actions/alunos.test.ts index e2546e0..f836413 100644 --- a/src/lib/actions/alunos.test.ts +++ b/src/lib/actions/alunos.test.ts @@ -1,9 +1,9 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createAlunoAction, updateAlunoAction, deleteAlunoAction } from './alunos'; import { prisma } from '@/lib/prisma'; import { getUser } from '@/utils/supabase/server'; import { revalidatePath } from 'next/cache'; -import type { Aluno, AlunoBase } from '@/lib/definitions'; // Mocking dependencies vi.mock('@/lib/prisma', () => ({ @@ -36,9 +36,7 @@ describe('alunos actions (CRUD Unit Tests)', () => { beforeEach(() => { vi.clearAllMocks(); - vi.mocked(getUser).mockResolvedValue( - mockAdmin as unknown as { user: { email: string }; error: null } - ); + vi.mocked(getUser).mockResolvedValue(mockAdmin as any); }); describe('createAlunoAction', () => { @@ -61,7 +59,7 @@ describe('alunos actions (CRUD Unit Tests)', () => { treinosNoMes: 0, }; - vi.mocked(prisma.aluno.create).mockResolvedValue(mockCreatedAluno as unknown as Aluno); + vi.mocked(prisma.aluno.create).mockResolvedValue(mockCreatedAluno as any); const result = await createAlunoAction(inputData); @@ -72,14 +70,8 @@ describe('alunos actions (CRUD Unit Tests)', () => { }); it('should return error if unauthorized', async () => { - vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as unknown as { - user: null; - error: null; - }); - const result = await createAlunoAction( - {} as unknown as AlunoBase & - Pick - ); + vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as any); + const result = await createAlunoAction({} as any); expect(result.success).toBe(false); expect(result.error).toBe('Usuário não autenticado'); }); @@ -98,7 +90,7 @@ describe('alunos actions (CRUD Unit Tests)', () => { dataCadastro: new Date(), }; - vi.mocked(prisma.aluno.update).mockResolvedValue(mockUpdatedAluno as unknown as Aluno); + vi.mocked(prisma.aluno.update).mockResolvedValue(mockUpdatedAluno as any); const result = await updateAlunoAction(validId, updateData); From 4df98502a3ae92a84efbb1d99c866357be9a70a9 Mon Sep 17 00:00:00 2001 From: Emiya Kiritsugu Date: Wed, 6 May 2026 01:53:14 -0300 Subject: [PATCH 3/3] fix: refine mock types and add unauthorized negative path tests --- src/lib/actions/alunos.test.ts | 40 +++++++++++++++++++++++++++++----- 1 file changed, 34 insertions(+), 6 deletions(-) diff --git a/src/lib/actions/alunos.test.ts b/src/lib/actions/alunos.test.ts index f836413..4ab5f05 100644 --- a/src/lib/actions/alunos.test.ts +++ b/src/lib/actions/alunos.test.ts @@ -1,4 +1,3 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { createAlunoAction, updateAlunoAction, deleteAlunoAction } from './alunos'; import { prisma } from '@/lib/prisma'; @@ -36,7 +35,9 @@ describe('alunos actions (CRUD Unit Tests)', () => { beforeEach(() => { vi.clearAllMocks(); - vi.mocked(getUser).mockResolvedValue(mockAdmin as any); + vi.mocked(getUser).mockResolvedValue( + mockAdmin as unknown as Awaited> + ); }); describe('createAlunoAction', () => { @@ -59,7 +60,9 @@ describe('alunos actions (CRUD Unit Tests)', () => { treinosNoMes: 0, }; - vi.mocked(prisma.aluno.create).mockResolvedValue(mockCreatedAluno as any); + vi.mocked(prisma.aluno.create).mockResolvedValue( + mockCreatedAluno as unknown as Awaited> + ); const result = await createAlunoAction(inputData); @@ -70,8 +73,10 @@ describe('alunos actions (CRUD Unit Tests)', () => { }); it('should return error if unauthorized', async () => { - vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as any); - const result = await createAlunoAction({} as any); + vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as unknown as Awaited< + ReturnType + >); + const result = await createAlunoAction({} as Parameters[0]); expect(result.success).toBe(false); expect(result.error).toBe('Usuário não autenticado'); }); @@ -90,7 +95,9 @@ describe('alunos actions (CRUD Unit Tests)', () => { dataCadastro: new Date(), }; - vi.mocked(prisma.aluno.update).mockResolvedValue(mockUpdatedAluno as any); + vi.mocked(prisma.aluno.update).mockResolvedValue( + mockUpdatedAluno as unknown as Awaited> + ); const result = await updateAlunoAction(validId, updateData); @@ -101,6 +108,16 @@ describe('alunos actions (CRUD Unit Tests)', () => { data: expect.objectContaining({ nomeCompleto: 'José Inamar Silva' }), }); }); + + it('should return error if unauthorized', async () => { + vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as unknown as Awaited< + ReturnType + >); + const result = await updateAlunoAction(validId, { nomeCompleto: 'Novo Nome' }); + expect(result.success).toBe(false); + expect(result.error).toBe('Usuário não autenticado'); + expect(prisma.aluno.update).not.toHaveBeenCalled(); + }); }); describe('deleteAlunoAction', () => { @@ -113,5 +130,16 @@ describe('alunos actions (CRUD Unit Tests)', () => { }); expect(revalidatePath).toHaveBeenCalledWith('/dashboard/alunos'); }); + + it('should return error if unauthorized', async () => { + vi.mocked(getUser).mockResolvedValue({ user: null, error: null } as unknown as Awaited< + ReturnType + >); + const result = await deleteAlunoAction(validId); + expect(result.success).toBe(false); + expect(result.error).toBe('Usuário não autenticado'); + expect(prisma.aluno.delete).not.toHaveBeenCalled(); + expect(revalidatePath).not.toHaveBeenCalled(); + }); }); });