feat: add email-server-worker with clean template architecture
Add new email-server-worker project implementing a self-scheduling background worker pattern with HTTP monitoring. Removed all business-specific code from copied source, creating a clean, reusable template. Key features: - Self-scheduling worker loop with configurable interval - Graceful shutdown support (Docker-compatible) - Prometheus metrics collection - Health check endpoints (/healthcheck, /metrics, /ping) - Example worker template for easy customization - Comprehensive architecture documentation in CLAUDE.md The worker is now ready for email server implementation with no external dependencies on Evolution/MSSQL/ElasticSearch. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
118
email-server-worker/tests/routers/errorRouter.spec.ts
Normal file
118
email-server-worker/tests/routers/errorRouter.spec.ts
Normal file
@@ -0,0 +1,118 @@
|
||||
|
||||
import { errorRouter } from '../../src/routes/errorRouter';
|
||||
import createError from "http-errors";
|
||||
import { mockHttpContext } from "../helpers/mockHttpContext";
|
||||
|
||||
describe("errorRouter", () => {
|
||||
|
||||
beforeEach(() => {
|
||||
mockWrite.mockClear();
|
||||
});
|
||||
|
||||
test("u slučaju greške 404 mora vratiti string poruku 'page not found'", async () => {
|
||||
const err = createError(404)
|
||||
const {req,res,next} = mockHttpContext();
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(404);
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Content-Type', "text/html");
|
||||
expect(res.end).toHaveBeenCalledWith("page not found");
|
||||
});
|
||||
|
||||
test("u slučaju greške 404 mora logirati request, response i tekst greške", async () => {
|
||||
const err = createError(404)
|
||||
const reqPath = "/neki-path/";
|
||||
const {req,res,next} = mockHttpContext({ reqPath });
|
||||
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.locals.logger.info).toHaveBeenCalledWith("response", "page not found");
|
||||
expect(res.locals.logger.error).toHaveBeenCalledWith(err.name, "page "+req.path+" not found");
|
||||
});
|
||||
|
||||
test("ako su header-i već poslani, ne smiju biti poslani još jednom", async () => {
|
||||
const err = createError(404)
|
||||
const {req,res,next} = mockHttpContext({ headersSent:true, writableEnded:true });
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
expect(res.setHeader).not.toHaveBeenCalled();
|
||||
expect(res.end).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("ako NIJE već pozvana [end] metoda, treba je pozvati", async () => {
|
||||
const err = createError(404)
|
||||
const {req,res,next} = mockHttpContext({ headersSent:true, writableEnded:false });
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).not.toHaveBeenCalled();
|
||||
expect(res.setHeader).not.toHaveBeenCalled();
|
||||
expect(res.end).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("mora zaustaviti Prometheus Timer", async () => {
|
||||
const err = createError(404)
|
||||
const {req,res,next} = mockHttpContext({ headersSent:true, writableEnded:false });
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.locals.stopPrometheusTimer).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
test("u slučaju greške 500 mora vratiti string poruku 'internal server error'", async () => {
|
||||
const err = createError(500)
|
||||
const {req,res,next} = mockHttpContext();
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(500);
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Content-Type', "text/html");
|
||||
expect(res.end).toHaveBeenCalledWith("internal server error");
|
||||
});
|
||||
|
||||
test("u slučaju greške 400 mora vratiti string poruku 'bad request' i logirati grešku", async () => {
|
||||
const errorMessage = "mock error text 1";
|
||||
const err = createError(400, errorMessage);
|
||||
const {req,res,next} = mockHttpContext();
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(400);
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Content-Type', "text/html");
|
||||
expect(res.end).toHaveBeenCalledWith("bad request");
|
||||
|
||||
expect(res.locals.logger.errorwrite).toHaveBeenCalledWith(err.name, errorMessage);
|
||||
});
|
||||
|
||||
test("u slučaju greške 401 mora vratiti string poruku 'unauthorized' i logirati grešku", async () => {
|
||||
const errorMessage = "mock error text 2";
|
||||
const err = createError(401, errorMessage)
|
||||
const {req,res,next} = mockHttpContext();
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(401);
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Content-Type', "text/html");
|
||||
expect(res.end).toHaveBeenCalledWith("unauthorized");
|
||||
|
||||
expect(res.locals.logger.error).toHaveBeenCalledWith(err.name, errorMessage);
|
||||
});
|
||||
|
||||
test("u slučaju greške 403 mora vratiti string poruku 'forbidden' i logirati grešku", async () => {
|
||||
const errorMessage = "mock error text 3";
|
||||
const err = createError(403, errorMessage);
|
||||
const {req,res,next} = mockHttpContext();
|
||||
|
||||
await errorRouter(err, req, res, next);
|
||||
|
||||
expect(res.status).toHaveBeenCalledWith(403);
|
||||
expect(res.setHeader).toHaveBeenCalledWith('Content-Type', "text/html");
|
||||
expect(res.end).toHaveBeenCalledWith("forbidden");
|
||||
|
||||
expect(res.locals.logger.error).toHaveBeenCalledWith(err.name, errorMessage);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user