Spaces:
Running
Running
| /** | |
| * E2E tests for the Cerberus backend server API | |
| */ | |
| ; | |
| 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); | |
| }); | |