freightdesk/webapp/tests/integration/app.test.js
FreightDesk 8ae3b403ab [OWL] Roadmap batch: CI/CD, observability, testing, UX polish
CI/CD:
- Add .github/workflows/deploy.yml (lint, test, build Docker, Coolify deploy)

Observability:
- Add Pino logger (services/logger.js) — structured JSON logging
- Add Prometheus metrics (services/metrics.js) — /metrics endpoint
- Replace console.error with pino in error handler
- Track http_request_duration, http_requests_total, active_loads, total_commission

Testing:
- Add Jest config to package.json
- Add integration tests (tests/integration/app.test.js) — health, metrics, auth, 404
- Add unit tests (tests/unit/utils.test.js) — formatINR, getStatusColor, calcCommission, WhatsApp parser
- Add devDeps: jest, supertest, eslint, prettier

UX:
- Debounced search (400ms) on Loads list page
- Cache-busting asset versioning (?v=timestamp) on CSS/JS includes
- ESLint + Prettier configs

Package updates:
- Add pino, pino-http, prom-client to dependencies
- Add jest, eslint, prettier, supertest, nodemon to devDependencies
2026-06-07 19:46:45 +00:00

77 lines
2.3 KiB
JavaScript

const request = require('supertest');
const app = require('../../src/server');
describe('Health Check', () => {
test('GET /health returns 200', async () => {
const res = await request(app).get('/health');
expect(res.status).toBe(200);
expect(res.body.status).toBe('ok');
expect(res.body.ts).toBeDefined();
});
});
describe('Metrics Endpoint', () => {
test('GET /metrics returns 200 with prometheus format', async () => {
const res = await request(app).get('/metrics');
expect(res.status).toBe(200);
expect(res.text).toContain('http_requests_total');
expect(res.text).toContain('process_cpu_user_seconds');
});
});
describe('Auth Flow', () => {
test('GET /login returns 200', async () => {
const res = await request(app).get('/login');
expect(res.status).toBe(200);
expect(res.text).toContain('Login');
});
test('GET /setup returns 200 when no users exist', async () => {
const res = await request(app).get('/setup');
// Should either show setup form or redirect to login
expect([200, 302]).toContain(res.status);
});
test('POST /login with empty body returns 200 with error', async () => {
const res = await request(app)
.post('/login')
.send({})
.set('Content-Type', 'application/x-www-form-urlencoded');
expect(res.status).toBe(200);
});
test('GET / redirects to /login when not authenticated', async () => {
const res = await request(app).get('/');
expect(res.status).toBe(302);
expect(res.headers.location).toBe('/login');
});
});
describe('Protected Routes', () => {
test('GET /loads redirects to login', async () => {
const res = await request(app).get('/loads');
expect(res.status).toBe(302);
});
test('GET /shippers redirects to login', async () => {
const res = await request(app).get('/shippers');
expect(res.status).toBe(302);
});
test('GET /payments redirects to login', async () => {
const res = await request(app).get('/payments');
expect(res.status).toBe(302);
});
test('GET /reports redirects to login', async () => {
const res = await request(app).get('/reports');
expect(res.status).toBe(302);
});
});
describe('404 Handler', () => {
test('GET /nonexistent returns 404', async () => {
const res = await request(app).get('/nonexistent-page');
expect(res.status).toBe(404);
});
});