Import tests: backend/tests/integration/resilience

Import the existing local project snapshot. This commit organizes existing files; it does not represent newly developed functionality.
This commit is contained in:
arsam
2026-09-18 23:09:49 +03:30
parent c275009c30
commit 1286cd1eae
@@ -0,0 +1,152 @@
import { describe, it, expect, beforeAll, afterAll } from 'vitest';
import { execSync } from 'child_process';
import Redis from 'ioredis';
import { buildApp } from '../../src/app.js';
describe('System Resilience & Database Connections', () => {
let app: ReturnType<typeof buildApp>;
beforeAll(async () => {
app = buildApp();
await app.ready();
});
afterAll(async () => {
await app.close();
// Safely ensure both services are running after tests finish
try {
execSync('systemctl start postgresql@18-main.service');
} catch {}
try {
execSync('systemctl start redis-server.service');
} catch {}
});
describe('PostgreSQL Connection Resilience', () => {
it('should report database: connected when PostgreSQL is up', async () => {
const res = await app.inject({
method: 'GET',
url: '/health',
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.status).toBe('ok');
expect(body.database).toBe('connected');
});
it('should gracefully handle PostgreSQL going down', async () => {
// Stop PostgreSQL service
execSync('systemctl stop postgresql@18-main.service');
// Request health check (should return 500 error status and disconnected database)
const res = await app.inject({
method: 'GET',
url: '/health',
});
expect(res.statusCode).toBe(500);
const body = JSON.parse(res.body);
expect(body.status).toBe('error');
expect(body.database).toBe('disconnected');
expect(body.message).toBeDefined();
});
it('should automatically reconnect when PostgreSQL comes back up', async () => {
// Start PostgreSQL service
execSync('systemctl start postgresql@18-main.service');
// Wait a moment for PostgreSQL to start accepting connections
let retries = 5;
let connected = false;
while (retries > 0 && !connected) {
try {
const res = await app.inject({
method: 'GET',
url: '/health',
});
const body = JSON.parse(res.body);
if (res.statusCode === 200 && body.database === 'connected') {
connected = true;
}
} catch {}
if (!connected) {
await new Promise((resolve) => setTimeout(resolve, 1000));
retries--;
}
}
expect(connected).toBe(true);
});
});
describe('Redis Connection Resilience', () => {
let redis: Redis;
beforeAll(() => {
redis = new Redis('redis://127.0.0.1:6379', {
maxRetriesPerRequest: 1,
retryStrategy: () => 100, // retry quickly
});
});
afterAll(async () => {
await redis.quit();
});
it('should ping Redis successfully when it is up', async () => {
const pong = await redis.ping();
expect(pong).toBe('PONG');
});
it('should fail gracefully when Redis goes down', async () => {
// Stop Redis service
execSync('systemctl stop redis-server.service');
// Attempt ping - should fail / throw
await expect(redis.ping()).rejects.toThrow();
});
it('should automatically reconnect when Redis comes back up', async () => {
// Start Redis service
execSync('systemctl start redis-server.service');
// Wait and ping - should succeed again
let retries = 5;
let pong = '';
while (retries > 0 && pong !== 'PONG') {
try {
pong = await redis.ping();
} catch {}
if (pong !== 'PONG') {
await new Promise((resolve) => setTimeout(resolve, 1000));
retries--;
}
}
expect(pong).toBe('PONG');
});
});
describe('Global Error Handler & Config Validator', () => {
it('should categorize unhandled database/connection errors using the global error handler', async () => {
const testApp = buildApp();
// Register a test route that simulates an unhandled database error BEFORE ready()
testApp.get('/test-unhandled-db-error', async () => {
throw new Error('PrismaClientKnownRequestError: Can\'t reach database server');
});
await testApp.ready();
const res = await testApp.inject({
method: 'GET',
url: '/test-unhandled-db-error',
});
expect(res.statusCode).toBe(500);
const body = JSON.parse(res.body);
expect(body.error).toBeDefined();
expect(body.error.code).toBe('NETWORK_ERROR');
expect(body.error.message).toBe('An internal control plane error occurred.');
await testApp.close();
});
});
});