From 3e3a33e00b5fd10457375246b6afa3cce8bb8188 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 13:19:50 -0300 Subject: [PATCH 1/7] feat: implementa base model --- src/models/base.ts | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 src/models/base.ts diff --git a/src/models/base.ts b/src/models/base.ts new file mode 100644 index 0000000..66b157c --- /dev/null +++ b/src/models/base.ts @@ -0,0 +1,43 @@ +type PrismaBaseDelegate = { + findMany(args: any): Promise; + findFirst(args: any): Promise; + findUnique(args: any): Promise; + create(args: { data: any }): Promise; + update(args: { where: any; data: any }): Promise; + delete(args: { where: any }): Promise; +}; + +export class BaseModel { + protected readonly model: M; + + constructor(model: M) { + this.model = model; + } + + protected async findMany(args: Parameters[0]) { + return this.model.findMany(args); + } + + protected async findFirst(args: Parameters[0]) { + return this.model.findFirst(args); + } + + protected async createOne(data: Parameters[0]["data"]) { + return this.model.create({ data }); + } + + protected async updateOne( + where: Parameters[0]["where"], + data: Parameters[0]["data"], + ) { + return this.model.update({ where, data }); + } + + protected async deleteOneById(where: Parameters[0]["where"]) { + return this.model.delete({ where }); + } + + protected async findUnique(args: Parameters[0]) { + return this.model.findUnique(args); + } +} From 286c95e24d642da53934270c111d9a0074caa9b4 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:33:12 -0300 Subject: [PATCH 2/7] refactor: modifica models para extender o base --- src/models/session.ts | 110 ++++++++++++++++++++++++------------------ src/models/task.ts | 43 +++++++---------- src/models/user.ts | 52 +++++++++++--------- 3 files changed, 110 insertions(+), 95 deletions(-) diff --git a/src/models/session.ts b/src/models/session.ts index 9bd3d69..8b27bd8 100644 --- a/src/models/session.ts +++ b/src/models/session.ts @@ -1,52 +1,9 @@ import { randomUUID } from "node:crypto"; import { env } from "~/config/env.js"; import { prisma } from "~/infra/database.js"; - -export const createSession = async (userId: string) => { - const sessionId = randomUUID(); - const now = new Date(); - - const session = await prisma.userSession.create({ - data: { - id: sessionId, - user_id: userId, - expires_at: new Date(now.getTime() + env.INACTIVITY_TIMEOUT), - last_active_at: now, - }, - }); - return session; -}; - -export const validateSession = async (sessionId: string) => { - const session = await prisma.userSession.findUnique({ - where: { - id: sessionId, - }, - }); - if (!session) return false; - - const userDb = await prisma.user.findUnique({ - where: { - id: session.user_id, - }, - }); - if (!userDb) return false; - - const now = new Date(); - const lastActive = new Date(String(session.last_active_at)); - - if (now.getTime() - lastActive.getTime() > env.INACTIVITY_TIMEOUT) - return false; - - await prisma.userSession.update({ - where: { id: sessionId }, - data: { last_active_at: now }, - }); - - const { email, id, name, created_at, updated_at } = userDb; - const secureObjectValue = { id, email, name, created_at, updated_at }; - return secureObjectValue; -}; +import { BaseModel } from "./base.js"; +import { UserDelegate, UserSessionDelegate } from "@/generated/models.js"; +import { userModel, UserModelClass } from "./user.js"; export type AuthenticatedUser = { id: string; @@ -56,4 +13,63 @@ export type AuthenticatedUser = { updated_at: Date; }; -export type UserSession = Awaited>; +export type UserSession = { + id: string; + user_id: string; + created_at: Date; + expires_at: Date; + last_active_at: Date; +}; + +class SessionModel extends BaseModel { + constructor( + model: UserSessionDelegate, + private userModel: UserModelClass, + ) { + super(model); + this.userModel = userModel; + } + + async create(user_id: string): Promise { + const id = randomUUID(); + const now = new Date(); + const expires_at = new Date(now.getTime() + env.INACTIVITY_TIMEOUT); + + return await this.createOne({ + id, + user_id, + expires_at, + last_active_at: now, + }); + } + + async validate(sessionId: string): Promise { + const sessionObject: UserSession = await this.findUnique({ + where: { id: sessionId }, + }); + if (!sessionObject) return false; + + const user = await this.userModel.findOne(sessionObject.user_id); + if (!user) return false; + + const now = new Date(); + const lastActive = new Date(String(sessionObject.last_active_at)); + + if (now.getTime() - lastActive.getTime() > env.INACTIVITY_TIMEOUT) + return false; + + await this.updateOne({ id: sessionId }, { last_active_at: now }); + + const { email, id, name, created_at, updated_at } = user; + const secureObjectValue: AuthenticatedUser = { + id, + email, + name, + created_at, + updated_at, + }; + return secureObjectValue; + } +} + +export const sessionModel = new SessionModel(prisma.userSession, userModel); diff --git a/src/models/task.ts b/src/models/task.ts index 8008149..2b757f7 100644 --- a/src/models/task.ts +++ b/src/models/task.ts @@ -1,15 +1,16 @@ -import { TaskDelegate } from "@/generated/models.js"; import { prisma } from "~/infra/database.js"; -import { Task as TaskEnt, UpdateTask } from "~/zod/taskSchema.js"; +import { BaseModel } from "~/models/base.js"; +import { TaskDelegate } from "@/generated/models.js"; +import { TaskInput, UpdateTask } from "~/zod/taskSchema.js"; +import { Task } from "@/generated/client.js"; -class TaskModel { - private model: TaskDelegate; - constructor() { - this.model = prisma.task; +class TaskModel extends BaseModel { + constructor(model: TaskDelegate) { + super(model); } - async findAll(userId: string) { - const tasks = await this.model.findMany({ + async findAll(userId: string): Promise { + const tasks: Task[] = await this.findMany({ where: { user_id: userId, }, @@ -24,21 +25,17 @@ class TaskModel { return tasks; } - async create(providedData: TaskEnt, user_id: string) { + async create(providedData: TaskInput, user_id: string): Promise { const data = { ...providedData, user_id, }; - const task = await this.model.create({ - data, - }); - - return task; + return await this.createOne(data); } - async findOne(id: string) { - const task = await this.model.findFirst({ + async findOne(id: string): Promise { + const task: Task = await this.findFirst({ where: { id }, }); @@ -47,24 +44,20 @@ class TaskModel { return task; } - async update(id: string, providedData: UpdateTask) { + async update(id: string, providedData: UpdateTask): Promise { const now = new Date(); const data = { ...providedData, updated_at: now, }; - const updatedTask = await this.model.update({ - where: { id }, - data, - }); + const updatedTask = await this.updateOne({ id }, data); return updatedTask; } + async deleteOne(id: string) { - await this.model.delete({ - where: { id }, - }); + await this.deleteOneById({ id }); } } -export const taskModel = new TaskModel(); +export const taskModel = new TaskModel(prisma.task); diff --git a/src/models/user.ts b/src/models/user.ts index 03eec83..d7748be 100644 --- a/src/models/user.ts +++ b/src/models/user.ts @@ -1,33 +1,39 @@ import { LoginInput, UserInput } from "~/zod/userSchema.js"; -import { createSession } from "~/models/session.js"; import { comparePassword, hashPassword } from "./password.js"; import { prisma } from "~/infra/database.js"; +import { BaseModel } from "./base.js"; +import { UserDelegate } from "@/generated/models.js"; +import { AuthenticatedUser } from "./session.js"; -export const registerUser = async (userInputValues: UserInput) => { - const password = await hashPassword(userInputValues.password); - const newUser = { - ...userInputValues, - password, - }; - return await prisma.user.create({ data: newUser }); -}; +class UserModel extends BaseModel { + constructor(model: UserDelegate) { + super(model); + } -export const login = async (userInputValues: LoginInput) => { - const user = await prisma.user.findUnique({ - where: { email: userInputValues.email }, - }); + async create(userInputValues: UserInput): Promise { + userInputValues.password = await hashPassword(userInputValues.password); + return await this.createOne(userInputValues); + } - if (!user) return false; + async authenticateUser(userInputValues: LoginInput) { + const user = await this.findUnique({ + where: { email: userInputValues.email }, + }); + if (!user) return false; - const isValid = await comparePassword( - userInputValues.password, - user.password, - ); + const isValid = await comparePassword( + userInputValues.password, + user.password, + ); + if (!isValid) return false; - if (!isValid) return false; + return user; + } - const session = await createSession(user.id); - if (!session) return false; + async findOne(id: string) { + return await this.findFirst({ where: { id } }); + } +} - return session; -}; +export const userModel = new UserModel(prisma.user); +export type UserModelClass = UserModel; From 948fafb6819bebedf8e4c9585f0060194a7b0fa9 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:34:09 -0300 Subject: [PATCH 3/7] =?UTF-8?q?refactor:=20reflete=20as=20mudan=C3=A7as=20?= =?UTF-8?q?dos=20novos=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/controller/user.ts | 18 +++++++++--------- src/middleware/authenticate.ts | 13 ++++++------- src/router/router.ts | 2 +- 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/controller/user.ts b/src/controller/user.ts index 1476d9c..cab144e 100644 --- a/src/controller/user.ts +++ b/src/controller/user.ts @@ -1,8 +1,8 @@ import { Request, Response } from "express"; import { loginSchema } from "~/zod/userSchema.js"; import { registerSchema } from "~/zod/userSchema.js"; -import { AuthenticatedUser, validateSession } from "~/models/session.js"; -import { registerUser, login } from "~/models/user.js"; +import { AuthenticatedUser, sessionModel } from "~/models/session.js"; +import { userModel } from "~/models/user.js"; import { CookieResponse } from "~/models/cookieResponse.js"; export const register = async (req: Request, res: Response) => { @@ -15,7 +15,7 @@ export const register = async (req: Request, res: Response) => { } try { - const registeredUser = await registerUser(result.data); + const registeredUser = await userModel.create(result.data); response.created({ message: `Usuário '${registeredUser.name}' criado com sucesso!`, }); @@ -25,7 +25,7 @@ export const register = async (req: Request, res: Response) => { } }; -export const authentication = async (req: Request, res: Response) => { +export const login = async (req: Request, res: Response) => { const result = loginSchema.safeParse(req.body); const response = new CookieResponse(res); @@ -34,16 +34,16 @@ export const authentication = async (req: Request, res: Response) => { return; } try { - const sessionObject = await login(result.data); - if (!sessionObject) { + const user = await userModel.authenticateUser(result.data); + if (!user) { response.unauthorized( "Verifique os dados enviados e tente novamente", "Email ou senha inválido", ); return; } - - new CookieResponse(res).setCookie("session", sessionObject.id); + const session = await sessionModel.create(user.id); + new CookieResponse(res).setCookie("session", session.id); response.created({ message: "Usuário autenticado!" }); } catch (error) { console.error("Erro insperado ao efetuar login: ", error); @@ -63,7 +63,7 @@ export const getUser = async ( } try { - const user = await validateSession(sessionId); + const user = await sessionModel.validate(sessionId); if (!user) { response.clearCookie("session"); diff --git a/src/middleware/authenticate.ts b/src/middleware/authenticate.ts index b8c863e..0dae42f 100644 --- a/src/middleware/authenticate.ts +++ b/src/middleware/authenticate.ts @@ -1,6 +1,6 @@ import { Request, Response, NextFunction } from "express"; import { CookieResponse } from "~/models/cookieResponse.js"; -import { AuthenticatedUser, validateSession } from "~/models/session.js"; +import { AuthenticatedUser, sessionModel } from "~/models/session.js"; export interface AuthenticatedRequest extends Request { context?: { @@ -20,9 +20,9 @@ export async function authenticate( return; } - const userOrError = await validateSession(sessionId); + const user = await sessionModel.validate(sessionId); - if (!userOrError) { + if (!user) { response.clearCookie("session"); response.unauthorized( "Por favor, faça o login novamente.", @@ -30,10 +30,9 @@ export async function authenticate( ); return; } - if (!req.context) { - req.context = { user: userOrError }; - } - req.context.user = userOrError; + if (!req.context) req.context = { user }; + + req.context.user = user; next(); } diff --git a/src/router/router.ts b/src/router/router.ts index 58e5234..d0923fe 100644 --- a/src/router/router.ts +++ b/src/router/router.ts @@ -9,7 +9,7 @@ const router = Router(); router.get("/", index); -router.post("/login", userCtrl.authentication); +router.post("/login", userCtrl.login); router.post("/user", userCtrl.register); router.get("/user", userCtrl.getUser); From 718c93bee8e911f98646f977476e02c3b75840c2 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:34:54 -0300 Subject: [PATCH 4/7] refactor: modifica nome do tipo --- src/zod/taskSchema.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/zod/taskSchema.ts b/src/zod/taskSchema.ts index 178aa8b..687c688 100644 --- a/src/zod/taskSchema.ts +++ b/src/zod/taskSchema.ts @@ -14,7 +14,7 @@ export const taskSchema = z.object({ status: z.enum(["pending", "in_progress", "done"]).optional(), }); -export type Task = z.infer; +export type TaskInput = z.infer; export const updateTaskSchema = taskSchema .partial() From d941406dcab57283e2ab19a715a7f47187ee64a6 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:35:55 -0300 Subject: [PATCH 5/7] =?UTF-8?q?refactor:=20mudan=C3=A7as=20nos=20models?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- tests/orchestrator.ts | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/tests/orchestrator.ts b/tests/orchestrator.ts index 77f3b9e..40f1d74 100644 --- a/tests/orchestrator.ts +++ b/tests/orchestrator.ts @@ -1,10 +1,10 @@ import retry from "async-retry"; import { faker } from "@faker-js/faker"; -import { registerUser } from "~/models/user.js"; +import { userModel } from "~/models/user.js"; import { UserInput } from "~/zod/userSchema.js"; -import * as session from "~/models/session.js"; +import { AuthenticatedUser, sessionModel } from "~/models/session.js"; import { taskModel } from "~/models/task.js"; -import { Task } from "~/zod/taskSchema.js"; +import { TaskInput } from "~/zod/taskSchema.js"; const apiBaseUrl = "http://localhost:5001"; @@ -30,18 +30,18 @@ export async function waitForAllServices() { } export async function createUser(userValues?: UserInput) { - return await registerUser({ + return await userModel.create({ name: userValues?.name ?? faker.person.fullName().slice(0, 30), email: userValues?.email ?? faker.internet.email(), password: userValues?.password ?? "ValidPassword", }); } -export async function createSession(user: session.AuthenticatedUser) { - return await session.createSession(user.id); +export async function createSession(user: AuthenticatedUser) { + return await sessionModel.create(user.id); } -export async function createTask(task: Task, user: session.AuthenticatedUser) { +export async function createTask(task: TaskInput, user: AuthenticatedUser) { return await taskModel.create(task, user.id); } From 61a2b6ab6185745c71e497ce7ed7fb32bd50a0c0 Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:36:25 -0300 Subject: [PATCH 6/7] feat: implementa testes para rota de login --- tests/controller/login/post.test.ts | 157 ++++++++++++++++++++++++++++ 1 file changed, 157 insertions(+) create mode 100644 tests/controller/login/post.test.ts diff --git a/tests/controller/login/post.test.ts b/tests/controller/login/post.test.ts new file mode 100644 index 0000000..3e63ab4 --- /dev/null +++ b/tests/controller/login/post.test.ts @@ -0,0 +1,157 @@ +import { + API, + createSession, + createUser, + waitForAllServices, +} from "$/orchestrator.js"; +import { AuthenticatedUser, UserSession } from "~/models/session.js"; + +beforeAll(async () => { + await waitForAllServices(); +}); + +describe("POST `/login`", () => { + let defaultUser: AuthenticatedUser; + test("Com todos os dados corretos", async () => { + defaultUser = await createUser(); + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + email: defaultUser.email, + password: "ValidPassword", + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(true); + expect(response.status).toBe(201); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ message: "Usuário autenticado!" }); + }); + + test("Com `password` inválido", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + email: defaultUser.email, + password: "InvalidPassword", + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(401); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Email ou senha inválido", + action: "Verifique os dados enviados e tente novamente", + }); + }); + + test("Com `email` inexistente", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + email: "email@inexistente.com", + password: "InvalidPassword", + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(401); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Email ou senha inválido", + action: "Verifique os dados enviados e tente novamente", + }); + }); + + test("Com `email` inválido", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + email: "emailinvalido.com", + password: "InvalidPassword", + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(400); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + error: "Email não enviado ou inválido.", + }); + }); + + test("Sem a propriedade `email`", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + password: "InvalidPassword", + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(400); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + error: "Email não enviado ou inválido.", + }); + }); + + test("Sem a propriedade `password`", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({ + email: defaultUser.email, + }), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(400); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + error: "Senha obrigatória.", + }); + }); + + test("Sem as propriedades `email` e `password`", async () => { + const response = await fetch(API.login, { + method: "POST", + body: JSON.stringify({}), + headers: { + "Content-Type": "application/json", + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(400); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + error: "Email não enviado ou inválido. | Senha obrigatória.", + }); + }); +}); From 00c1945e1e487b699d6e96cd366efcbba8659f1b Mon Sep 17 00:00:00 2001 From: Pedro Barreto Date: Sun, 19 Apr 2026 14:37:44 -0300 Subject: [PATCH 7/7] refactor: melhora describe --- tests/controller/task/delete.test.ts | 191 ++++++++++++++------------- tests/controller/user/get.test.ts | 184 +++++++++++++------------- 2 files changed, 191 insertions(+), 184 deletions(-) diff --git a/tests/controller/task/delete.test.ts b/tests/controller/task/delete.test.ts index 3f73c0a..02efbc6 100644 --- a/tests/controller/task/delete.test.ts +++ b/tests/controller/task/delete.test.ts @@ -13,110 +13,113 @@ beforeAll(async () => { }); describe("DELETE `/tasks`", () => { - test("Sem usuário logado", async () => { - const fakeTaskId = crypto.randomUUID(); - - const response = await fetch(`${API.task}/${fakeTaskId}`, { - method: "DELETE", - }); - - expect(response.ok).toBe(false); - expect(response.status).toBe(401); - - const responseBody = await response.json(); - expect(responseBody).toEqual({ - message: "Não autorizado.", - action: "Faça o login.", + describe("Usuário anônimo", () => { + test("Tentando deletar `task`", async () => { + const fakeTaskId = crypto.randomUUID(); + + const response = await fetch(`${API.task}/${fakeTaskId}`, { + method: "DELETE", + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(401); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Não autorizado.", + action: "Faça o login.", + }); }); }); - - let defaultUser: AuthenticatedUser; - let defaultSession: UserSession; - test("Com usuário logado porém `task` com ID inválido", async () => { - const idInvalido = "id-no-formato-invalido"; - defaultUser = await createUser(); - defaultSession = await createSession(defaultUser); - const response = await fetch(`${API.task}/${idInvalido}`, { - method: "DELETE", - headers: { - Cookie: `session=${defaultSession.id}`, - }, + describe("Usuário padrão", () => { + let defaultUser: AuthenticatedUser; + let defaultSession: UserSession; + test("`task` com ID no formato inválido", async () => { + const idInvalido = "id-no-formato-invalido"; + defaultUser = await createUser(); + defaultSession = await createSession(defaultUser); + const response = await fetch(`${API.task}/${idInvalido}`, { + method: "DELETE", + headers: { + Cookie: `session=${defaultSession.id}`, + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(400); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + error: "Id da task deve ser do tipo UUID.", + }); }); - expect(response.ok).toBe(false); - expect(response.status).toBe(400); + test("`task` com ID no formato correto porém inexistente no banco", async () => { + const fakeTaskId = crypto.randomUUID(); - const responseBody = await response.json(); - expect(responseBody).toEqual({ - error: "Id da task deve ser do tipo UUID.", - }); - }); + const response = await fetch(`${API.task}/${fakeTaskId}`, { + method: "DELETE", + headers: { + Cookie: `session=${defaultSession.id}`, + }, + }); - test("Com usuário logado porém `task` com ID válido porém inexistente no banco", async () => { - const fakeTaskId = crypto.randomUUID(); - - const response = await fetch(`${API.task}/${fakeTaskId}`, { - method: "DELETE", - headers: { - Cookie: `session=${defaultSession.id}`, - }, + expect(response.ok).toBe(true); + expect(response.status).toBe(204); }); - expect(response.ok).toBe(true); - expect(response.status).toBe(204); - }); - - test("Com usuário logado e `task` com ID existente", async () => { - const task = await createTask( - { - title: "Teste deletar", - description: "Enviando uma task existente para deletar!", - }, - defaultUser, - ); - - const response = await fetch(`${API.task}/${task.id}`, { - method: "DELETE", - headers: { - Cookie: `session=${defaultSession.id}`, - }, - }); - - expect(response.ok).toBe(true); - expect(response.status).toBe(200); - - const responseBody = await response.json(); - expect(responseBody).toEqual({ - message: `Task ${task.id} deletada com sucesso!`, + test(" e `task` com ID válido e existente", async () => { + const task = await createTask( + { + title: "Teste deletar", + description: "Enviando uma task existente para deletar!", + }, + defaultUser, + ); + + const response = await fetch(`${API.task}/${task.id}`, { + method: "DELETE", + headers: { + Cookie: `session=${defaultSession.id}`, + }, + }); + + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: `Task ${task.id} deletada com sucesso!`, + }); }); - }); - - test("Com um usuário tentando excluir `task` de outro usuário", async () => { - const task = await createTask( - { - title: "Task do usuário padrão", - description: - "Enviando uma task existente para outro usuário tentar deletar!", - }, - defaultUser, - ); - - const otherUser = await createUser(); - const otherSession = await createSession(otherUser); - - const response = await fetch(`${API.task}/${task.id}`, { - method: "DELETE", - headers: { - Cookie: `session=${otherSession.id}`, - }, - }); - - expect(response.ok).toBe(false); - expect(response.status).toBe(403); - const responseBody = await response.json(); - expect(responseBody).toEqual({ - message: "Voce não tem permissão executar essa ação.", + test("Com um usuário tentando excluir `task` de outro usuário", async () => { + const task = await createTask( + { + title: "Task do usuário padrão", + description: + "Enviando uma task existente para outro usuário tentar deletar!", + }, + defaultUser, + ); + + const otherUser = await createUser(); + const otherSession = await createSession(otherUser); + + const response = await fetch(`${API.task}/${task.id}`, { + method: "DELETE", + headers: { + Cookie: `session=${otherSession.id}`, + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(403); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Voce não tem permissão executar essa ação.", + }); }); }); }); diff --git a/tests/controller/user/get.test.ts b/tests/controller/user/get.test.ts index efbea10..b173319 100644 --- a/tests/controller/user/get.test.ts +++ b/tests/controller/user/get.test.ts @@ -20,103 +20,107 @@ describe("GET `/user`", () => { vi.useRealTimers(); }); - test("Sem sessão vinculada", async () => { - const response = await fetch(API.user); - expect(response.ok).toBe(false); - expect(response.status).toBe(401); - - const responseBody = await response.json(); - expect(responseBody).toEqual({ - message: "Não autorizado.", - action: "Verifique se está logado e tente novamente.", + describe("Usuário anônimo", () => { + test("Tentando buscar os dados", async () => { + const response = await fetch(API.user); + expect(response.ok).toBe(false); + expect(response.status).toBe(401); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Não autorizado.", + action: "Verifique se está logado e tente novamente.", + }); }); }); - test("Com Sessão vinculada", async () => { - const newUser = await createUser(); - const session = await createSession(newUser); - const response = await fetch(API.user, { - headers: { - Cookie: `session=${session.id}`, - }, + describe("Usuário padrão", () => { + test("Com Sessão vinculada", async () => { + const newUser = await createUser(); + const session = await createSession(newUser); + const response = await fetch(API.user, { + headers: { + Cookie: `session=${session.id}`, + }, + }); + + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + user: { + id: newUser.id, + name: newUser.name, + email: newUser.email, + created_at: newUser.created_at.toISOString(), + updated_at: newUser.updated_at.toISOString(), + }, + }); }); - expect(response.ok).toBe(true); - expect(response.status).toBe(200); - - const responseBody = await response.json(); - expect(responseBody).toEqual({ - user: { - id: newUser.id, - name: newUser.name, - email: newUser.email, - created_at: newUser.created_at.toISOString(), - updated_at: newUser.updated_at.toISOString(), - }, + test("Com sessão quase expirada", async () => { + const horaAtual = new Date(); + const horaSessaoExpirada = new Date( + horaAtual.getTime() - env.INACTIVITY_TIMEOUT / 2, + ); + vi.setSystemTime(horaSessaoExpirada); + + const newUser = await createUser(); + const session = await createSession(newUser); + + vi.useRealTimers(); + const response = await fetch(API.user, { + headers: { + Cookie: `session=${session.id}`, + }, + }); + + expect(response.ok).toBe(true); + expect(response.status).toBe(200); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + user: { + id: newUser.id, + name: newUser.name, + email: newUser.email, + created_at: newUser.created_at.toISOString(), + updated_at: newUser.updated_at.toISOString(), + }, + }); + + expect(new Date(session.expires_at).getTime()).greaterThan( + horaAtual.getTime(), + ); + expect(session.created_at.getTime()).toBeLessThan(horaAtual.getTime()); }); - }); - - test("Com sessão quase expirada", async () => { - const horaAtual = new Date(); - const horaSessaoExpirada = new Date( - horaAtual.getTime() - env.INACTIVITY_TIMEOUT / 2, - ); - vi.setSystemTime(horaSessaoExpirada); - - const newUser = await createUser(); - const session = await createSession(newUser); - - vi.useRealTimers(); - const response = await fetch(API.user, { - headers: { - Cookie: `session=${session.id}`, - }, - }); - - expect(response.ok).toBe(true); - expect(response.status).toBe(200); - - const responseBody = await response.json(); - expect(responseBody).toEqual({ - user: { - id: newUser.id, - name: newUser.name, - email: newUser.email, - created_at: newUser.created_at.toISOString(), - updated_at: newUser.updated_at.toISOString(), - }, - }); - - expect(new Date(session.expires_at).getTime()).greaterThan( - horaAtual.getTime(), - ); - expect(session.created_at.getTime()).toBeLessThan(horaAtual.getTime()); - }); - - test("Com sessão expirada", async () => { - const horaAtual = new Date(); - const horaSessaoExpirada = new Date( - horaAtual.getTime() - env.INACTIVITY_TIMEOUT, - ); - vi.setSystemTime(horaSessaoExpirada); - - const newUser = await createUser(); - const session = await createSession(newUser); - - vi.useRealTimers(); - const response = await fetch(API.user, { - headers: { - Cookie: `session=${session.id}`, - }, - }); - - expect(response.ok).toBe(false); - expect(response.status).toBe(401); - const responseBody = await response.json(); - expect(responseBody).toEqual({ - message: "Sessão inválida ou expirada.", - action: "Por favor, realize o login novamente.", + test("Com sessão expirada", async () => { + const horaAtual = new Date(); + const horaSessaoExpirada = new Date( + horaAtual.getTime() - env.INACTIVITY_TIMEOUT, + ); + vi.setSystemTime(horaSessaoExpirada); + + const newUser = await createUser(); + const session = await createSession(newUser); + + vi.useRealTimers(); + const response = await fetch(API.user, { + headers: { + Cookie: `session=${session.id}`, + }, + }); + + expect(response.ok).toBe(false); + expect(response.status).toBe(401); + + const responseBody = await response.json(); + expect(responseBody).toEqual({ + message: "Sessão inválida ou expirada.", + action: "Por favor, realize o login novamente.", + }); }); }); });