Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion src/config/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,6 @@ async function closeRedisConnection() {
}
}


module.exports = {
redisClient,
redisEnabled,
Expand Down
114 changes: 114 additions & 0 deletions tests/controllers/auth.controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
jest.mock("../../src/services/authService.ts", () => ({
register: vi.fn(),
login: vi.fn(),
refreshToken: vi.fn(),
logout: vi.fn(),
listSessions: vi.fn(),
logoutSession: vi.fn(),
logoutAll: vi.fn(),
}));

const authService = require("../../src/services/authService.ts");
const authController = require("../../src/controllers/authController.ts");

const createRes = () => {
const res = {
status: vi.fn(),
json: vi.fn(),
};
res.status.mockReturnValue(res);
return res;
};

describe("authController", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("register retorna 400 quando faltam campos", async () => {
const req = { body: { email: "john@test.com" } };
const res = createRes();
const next = vi.fn();

await authController.register(req, res, next);

expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({ error: "missing required fields" });
expect(next).not.toHaveBeenCalled();
});

it("login retorna 400 quando faltam campos", async () => {
const req = { body: { email: "john@test.com" } };
const res = createRes();
const next = vi.fn();

await authController.login(req, res, next);

expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({ error: "missing required fields" });
expect(next).not.toHaveBeenCalled();
});

it("refresh retorna 400 quando refreshToken não é enviado", async () => {
const req = { body: {} };
const res = createRes();
const next = vi.fn();

await authController.refresh(req, res, next);

expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({ error: "refresh token required" });
expect(next).not.toHaveBeenCalled();
});

it("logout retorna 400 quando refreshToken não é enviado", async () => {
const req = { body: {} };
const res = createRes();
const next = vi.fn();

await authController.logout(req, res, next);

expect(res.status).toHaveBeenCalledWith(400);
expect(res.json).toHaveBeenCalledWith({ error: "refresh token required" });
expect(next).not.toHaveBeenCalled();
});

it("sessions encaminha erro no next quando service falha", async () => {
const err = new Error("service failure");
authService.listSessions.mockRejectedValue(err);

const req = { userId: 1 };
const res = createRes();
const next = vi.fn();

await authController.sessions(req, res, next);

expect(next).toHaveBeenCalledWith(err);
});

it("logoutSession encaminha erro no next quando service falha", async () => {
const err = new Error("service failure");
authService.logoutSession.mockRejectedValue(err);

const req = { userId: 1, body: { jti: "jti-1" } };
const res = createRes();
const next = vi.fn();

await authController.logoutSession(req, res, next);

expect(next).toHaveBeenCalledWith(err);
});

it("logoutAll encaminha erro no next quando service falha", async () => {
const err = new Error("service failure");
authService.logoutAll.mockRejectedValue(err);

const req = { userId: 1 };
const res = createRes();
const next = vi.fn();

await authController.logoutAll(req, res, next);

expect(next).toHaveBeenCalledWith(err);
});
});
69 changes: 69 additions & 0 deletions tests/controllers/health.controller.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
jest.mock("../../src/config/prisma.ts", () => ({
$queryRaw: vi.fn(),
}));

const prisma = require("../../src/config/prisma.ts");
const healthController = require("../../src/controllers/healthController.ts");

const createRes = () => {
const res = {
status: vi.fn(),
json: vi.fn(),
};
res.status.mockReturnValue(res);
return res;
};

describe("healthController", () => {
beforeEach(() => {
vi.clearAllMocks();
});

it("health retorna status ok", async () => {
const res = createRes();

await healthController.health({}, res);

expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
status: "ok",
service: "auth-api",
timestamp: expect.any(String),
}),
);
});

it("ready retorna 200 quando banco está disponível", async () => {
prisma.$queryRaw.mockResolvedValue([1]);
const res = createRes();

await healthController.ready({}, res);

expect(prisma.$queryRaw).toHaveBeenCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(200);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
status: "ready",
database: "up",
timestamp: expect.any(String),
}),
);
});

it("ready retorna 503 quando banco falha", async () => {
prisma.$queryRaw.mockRejectedValue(new Error("db down"));
const res = createRes();

await healthController.ready({}, res);

expect(res.status).toHaveBeenCalledWith(503);
expect(res.json).toHaveBeenCalledWith(
expect.objectContaining({
status: "not_ready",
database: "down",
timestamp: expect.any(String),
}),
);
});
});
39 changes: 39 additions & 0 deletions tests/errors/appError.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const AppError = require("../../src/errors/AppError.ts");
const BadRequestError = require("../../src/errors/BadRequestError.ts");
const ConflictError = require("../../src/errors/ConflictError.ts");
const UnauthorizedError = require("../../src/errors/UnauthorizedError.ts");

describe("Error classes", () => {
it("AppError aplica valores default", () => {
const err = new AppError("something went wrong");

expect(err.message).toBe("something went wrong");
expect(err.name).toBe("AppError");
expect(err.statusCode).toBe(500);
expect(err.code).toBe("APP_ERROR");
});

it("BadRequestError usa status 400", () => {
const err = new BadRequestError();

expect(err).toBeInstanceOf(AppError);
expect(err.statusCode).toBe(400);
expect(err.message).toBe("bad request");
});

it("ConflictError usa status 409", () => {
const err = new ConflictError("already exists");

expect(err).toBeInstanceOf(AppError);
expect(err.statusCode).toBe(409);
expect(err.message).toBe("already exists");
});

it("UnauthorizedError usa status 401", () => {
const err = new UnauthorizedError();

expect(err).toBeInstanceOf(AppError);
expect(err.statusCode).toBe(401);
expect(err.message).toBe("unauthorized");
});
});
58 changes: 58 additions & 0 deletions tests/middleware/errorHandler.middleware.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
const errorHandler = require("../../src/middlewares/errorHandler.ts");
const AppError = require("../../src/errors/AppError.ts");

const createRes = () => {
const res = {
status: vi.fn(),
json: vi.fn(),
};
res.status.mockReturnValue(res);
return res;
};

describe("errorHandler middleware", () => {
const originalNodeEnv = process.env.NODE_ENV;

afterEach(() => {
process.env.NODE_ENV = originalNodeEnv;
vi.restoreAllMocks();
});

it("retorna status/code do AppError", () => {
const res = createRes();

errorHandler(new AppError("invalid payload", 422, "INVALID_PAYLOAD"), {}, res, vi.fn());

expect(res.status).toHaveBeenCalledWith(422);
expect(res.json).toHaveBeenCalledWith({
error: "invalid payload",
code: "INVALID_PAYLOAD",
});
});

it("retorna 500 para erro genérico sem log no ambiente de teste", () => {
process.env.NODE_ENV = "test";
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const res = createRes();

errorHandler(new Error("boom"), {}, res, vi.fn());

expect(consoleSpy).not.toHaveBeenCalled();
expect(res.status).toHaveBeenCalledWith(500);
expect(res.json).toHaveBeenCalledWith({
error: "internal server error",
code: "INTERNAL_SERVER_ERROR",
});
});

it("loga erro genérico fora do ambiente de teste", () => {
process.env.NODE_ENV = "production";
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const res = createRes();

errorHandler(new Error("boom"), {}, res, vi.fn());

expect(consoleSpy).toHaveBeenCalledTimes(1);
expect(res.status).toHaveBeenCalledWith(500);
});
});