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
155 changes: 155 additions & 0 deletions tests/config/redis.config.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
describe("redis config", () => {
const originalEnv = process.env;

const loadRedisConfig = ({ redisUrl = "", nodeEnv = "test", clientOverrides = {} } = {}) => {
vi.resetModules();
process.env = { ...originalEnv, NODE_ENV: nodeEnv, REDIS_URL: redisUrl };

const listeners = new Map();

const defaultClient = {
status: "ready",
on: vi.fn((event, handler) => {
listeners.set(event, handler);
}),
disconnect: vi.fn(),
quit: vi.fn().mockResolvedValue(undefined),
connector: {
stream: {
destroyed: false,
destroy: vi.fn(),
},
},
};

const client = { ...defaultClient, ...clientOverrides };
const RedisMock = vi.fn(() => client);

vi.doMock("ioredis", () => RedisMock);

const redisConfig = require("../../src/config/redis.ts");

return {
redisConfig,
RedisMock,
client,
getListener: (event) => listeners.get(event),
};
};

afterEach(() => {
process.env = originalEnv;
vi.restoreAllMocks();
vi.resetModules();
});

it("não inicializa redis quando REDIS_URL está vazio", async () => {
const { redisConfig, RedisMock } = loadRedisConfig({ redisUrl: "" });

expect(redisConfig.redisEnabled).toBe(false);
expect(redisConfig.redisClient).toBeNull();
expect(RedisMock).not.toHaveBeenCalled();

await expect(redisConfig.closeRedisConnection()).resolves.toBeUndefined();
});

it("loga erro fora de test", () => {
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const { getListener } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
nodeEnv: "production",
});

const onError = getListener("error");
expect(typeof onError).toBe("function");
onError(new Error("redis down"));

expect(consoleSpy).toHaveBeenCalledWith("[redis] connection error:", "redis down");
});

it("não loga erro em test", () => {
const consoleSpy = vi.spyOn(console, "error").mockImplementation(() => {});
const { getListener } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
nodeEnv: "test",
});

const onError = getListener("error");
expect(typeof onError).toBe("function");
onError(new Error("redis down"));

expect(consoleSpy).not.toHaveBeenCalled();
});

it("retorna cedo quando status é end", async () => {
const { redisConfig, client } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
clientOverrides: { status: "end" },
});

await redisConfig.closeRedisConnection();

expect(client.quit).not.toHaveBeenCalled();
expect(client.disconnect).not.toHaveBeenCalled();
});

it("força disconnect quando status não é ready", async () => {
const { redisConfig, client } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
nodeEnv: "test",
clientOverrides: { status: "connecting" },
});

await redisConfig.closeRedisConnection();

expect(client.disconnect).toHaveBeenCalledTimes(1);
expect(client.connector.stream.destroy).toHaveBeenCalledTimes(1);
});

it("não destrói stream já destruído", async () => {
const stream = { destroyed: true, destroy: vi.fn() };

const { redisConfig, client } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
nodeEnv: "test",
clientOverrides: {
status: "connecting",
connector: { stream },
},
});

await redisConfig.closeRedisConnection();

expect(client.disconnect).toHaveBeenCalledTimes(1);
expect(stream.destroy).not.toHaveBeenCalled();
});

it("usa quit quando status é ready", async () => {
const { redisConfig, client } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
clientOverrides: { status: "ready" },
});

await redisConfig.closeRedisConnection();

expect(client.quit).toHaveBeenCalledTimes(1);
expect(client.disconnect).not.toHaveBeenCalled();
});

it("faz fallback para disconnect quando quit falha", async () => {
const { redisConfig, client } = loadRedisConfig({
redisUrl: "redis://localhost:6379",
nodeEnv: "test",
clientOverrides: {
status: "ready",
quit: vi.fn().mockRejectedValue(new Error("quit failed")),
},
});

await redisConfig.closeRedisConnection();

expect(client.quit).toHaveBeenCalledTimes(1);
expect(client.disconnect).toHaveBeenCalledTimes(1);
expect(client.connector.stream.destroy).toHaveBeenCalledTimes(1);
});
});
47 changes: 47 additions & 0 deletions tests/routes/docs.routes.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
const express = require("express");
const request = require("supertest");

describe("docsRoutes", () => {
const createApp = () => {
jest.resetModules();
const router = require("../../src/routes/docsRoutes.ts");
const app = express();
app.use(router);
return app;
};

afterEach(() => {
jest.restoreAllMocks();
jest.resetModules();
});

it("GET /docs.json retorna spec OpenAPI", async () => {
const app = createApp();

const res = await request(app).get("/docs.json");

expect(res.statusCode).toBe(200);
expect(res.body).toHaveProperty("openapi");
expect(res.body).toHaveProperty("info");
});

it("GET /docs responde com Swagger UI", async () => {
const app = createApp();

const res = await request(app).get("/docs").redirects(1);

expect(res.statusCode).toBe(200);
expect(res.text).toContain("Swagger UI");
});

it("GET /docs.json duas vezes cobre cache de spec", async () => {
const app = createApp();

const first = await request(app).get("/docs.json");
const second = await request(app).get("/docs.json");

expect(first.statusCode).toBe(200);
expect(second.statusCode).toBe(200);
expect(second.body).toEqual(first.body);
});
});