Cerberus-Backend / server /test /e2e.test.js
Shravan Pandala
feat: persist scan results + Apply Stored Fix command
de4dcb4
/**
* E2E tests for the Cerberus backend server API
*/
'use strict';
const http = require('http');
const assert = require('assert');
// ──────────────────────────────────────────────
// Helpers
// ──────────────────────────────────────────────
function request(method, path, body) {
return new Promise((resolve, reject) => {
const payload = body ? JSON.stringify(body) : null;
const options = {
hostname: 'localhost',
port: 5000,
path,
method,
headers: {
'Content-Type': 'application/json',
...(payload ? { 'Content-Length': Buffer.byteLength(payload) } : {}),
},
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', (chunk) => (data += chunk));
res.on('end', () => {
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, body: data }); }
});
});
req.on('error', reject);
if (payload) req.write(payload);
req.end();
});
}
function requestRaw(path, contentType, rawBody) {
return new Promise((resolve, reject) => {
const payload = Buffer.from(rawBody);
const options = {
hostname: 'localhost', port: 5000, path, method: 'POST',
headers: { 'Content-Type': contentType, 'Content-Length': payload.length },
};
const req = http.request(options, (res) => {
let data = '';
res.on('data', c => data += c);
res.on('end', () => {
try { resolve({ status: res.statusCode, body: JSON.parse(data) }); }
catch { resolve({ status: res.statusCode, body: data }); }
});
});
req.on('error', reject);
req.write(payload); req.end();
});
}
let passed = 0;
let failed = 0;
async function test(name, fn) {
try {
await fn();
console.log(` βœ… ${name}`);
passed++;
} catch (err) {
console.log(` ❌ ${name}`);
console.log(` ${err.message}`);
failed++;
}
}
// ──────────────────────────────────────────────
// Main
// ──────────────────────────────────────────────
async function main() {
// Start server silently
const origLog = console.log;
process.env.NODE_ENV = 'test';
const app = require('../server.js');
console.log = origLog;
// Give server a moment to bind
await new Promise(r => setTimeout(r, 600));
console.log('\nπŸ” Cerberus Server E2E Tests\n');
// ── Health ────────────────────────────────
console.log('── GET /api/health ──');
await test('returns 200 with { status: "ok" }', async () => {
const res = await request('GET', '/api/health');
assert.strictEqual(res.status, 200);
assert.strictEqual(res.body.status, 'ok');
});
// ── /api/patch-code ───────────────────────
console.log('\n── POST /api/patch-code ──');
await test('400 on non-JSON content-type', async () => {
const res = await requestRaw('/api/patch-code', 'text/plain', 'hello');
assert.strictEqual(res.status, 400);
assert.ok(res.body.error);
});
await test('400 when "code" field is missing', async () => {
const res = await request('POST', '/api/patch-code', { notCode: 'hello' });
assert.strictEqual(res.status, 400);
assert.match(res.body.message, /code/i);
});
await test('400 when "code" field is not a string', async () => {
const res = await request('POST', '/api/patch-code', { code: 12345 });
assert.strictEqual(res.status, 400);
assert.match(res.body.message, /code/i);
});
// ── /api/scan ─────────────────────────────
console.log('\n── POST /api/scan ──');
await test('400 on non-JSON content-type', async () => {
const res = await requestRaw('/api/scan', 'text/plain', 'hello');
assert.strictEqual(res.status, 400);
});
await test('400 when "path" field is missing', async () => {
const res = await request('POST', '/api/scan', { notPath: '/tmp' });
assert.strictEqual(res.status, 400);
assert.match(res.body.message, /path/i);
});
await test('400 when path does not exist', async () => {
const res = await request('POST', '/api/scan', { path: '/tmp/cerberus_nonexistent_dir_xyz123' });
assert.strictEqual(res.status, 400);
assert.match(res.body.error, /path/i);
});
await test('400 when path is a file not a directory', async () => {
const res = await request('POST', '/api/scan', { path: '/etc/hosts' });
assert.strictEqual(res.status, 400);
});
// ── /api/scan-file ────────────────────────
console.log('\n── POST /api/scan-file ──');
await test('400 when "code" field is missing', async () => {
const res = await request('POST', '/api/scan-file', { path: '/tmp/foo.py' });
assert.strictEqual(res.status, 400);
assert.match(res.body.message, /code/i);
});
await test('400 when fields are not strings', async () => {
const res = await request('POST', '/api/scan-file', { path: 123, code: 456 });
assert.strictEqual(res.status, 400);
});
await test('400 on non-JSON content-type', async () => {
const res = await requestRaw('/api/scan-file', 'text/plain', 'hello');
assert.strictEqual(res.status, 400);
});
// ── GET /api/stored-fix ───────────────────
console.log('\n── GET /api/stored-fix ──');
await test('400 when "path" param is missing', async () => {
const res = await request('GET', '/api/stored-fix');
assert.strictEqual(res.status, 400);
assert.ok(res.body.error);
});
await test('404 when no result has been stored for the path', async () => {
const res = await request('GET', '/api/stored-fix?path=/tmp/cerberus_no_such_file_xyz.py');
assert.strictEqual(res.status, 404);
assert.match(res.body.error, /stored fix/i);
});
// ──────────────────────────────────────────
// Summary
// ──────────────────────────────────────────
console.log(`\n${'─'.repeat(40)}`);
const total = passed + failed;
console.log(`Results: ${passed}/${total} passed${failed > 0 ? `, ${failed} failed` : ''}`);
if (failed > 0) {
process.exit(1);
} else {
console.log('All tests passed! πŸŽ‰\n');
process.exit(0);
}
}
main().catch(err => {
console.error('Fatal error:', err);
process.exit(1);
});