igo
Version:
Igo is a Node.js Web Framework based on Express
139 lines (112 loc) • 4.61 kB
JavaScript
require('./init');
const assert = require('assert');
const fs = require('fs');
const agent = require('../src/dev/test/agent');
const { _test: throttle } = require('../src/connect/errorhandler');
describe('ErrorHandler', function() {
describe('404 handling', function() {
it('should handle not found pages', async () => {
const res = await agent.get('/this-page-does-not-exist');
assert.strictEqual(res.statusCode, 404);
});
});
describe('Routes', function() {
it('should handle normal GET requests', async () => {
const res = await agent.get('/');
assert.strictEqual(res.statusCode, 200);
assert.strictEqual(res.body, 'Hello Igo');
});
it('should handle normal POST requests', async () => {
const res = await agent.post('/echo', {
body: { test: 'data' }
});
assert.strictEqual(res.statusCode, 200);
});
});
// Note: Error handler tests are skipped in test mode because:
// - initContext is not called (see src/app.js:88-90)
// - Error handler middleware is not registered (see src/app.js:116-118)
// This is by design to allow tests to see raw errors
describe.skip('Error handling (disabled in test mode)', function() {
it('should handle sync errors in routes', async () => {
const res = await agent.get('/error');
assert.strictEqual(res.statusCode, 500);
});
it('should handle promise rejections', async () => {
const res = await agent.get('/promise-rejection');
assert.strictEqual(res.statusCode, 200);
// Promise rejection happens after response is sent
});
});
describe('Email throttling', function() {
beforeEach(function() {
// Clean throttle file before each test
if (fs.existsSync(throttle.THROTTLE_FILE)) {
fs.unlinkSync(throttle.THROTTLE_FILE);
}
});
it('should allow first emails', function() {
const result = throttle.checkThrottle('TestError');
assert.strictEqual(result.throttled, false);
assert.strictEqual(result.shouldAlert, false);
});
it('should allow emails up to limit', function() {
// First 2 emails should pass without alert
for (let i = 0; i < throttle.THROTTLE_LIMIT - 1; i++) {
const result = throttle.checkThrottle('TestError');
assert.strictEqual(result.throttled, false);
assert.strictEqual(result.shouldAlert, false);
}
});
it('should alert on reaching limit', function() {
// Send THROTTLE_LIMIT - 1 emails
for (let i = 0; i < throttle.THROTTLE_LIMIT - 1; i++) {
throttle.checkThrottle('TestError');
}
// The Nth email should trigger alert
const result = throttle.checkThrottle('TestError');
assert.strictEqual(result.throttled, false);
assert.strictEqual(result.shouldAlert, true);
});
it('should block after reaching limit', function() {
// Send THROTTLE_LIMIT emails (last one triggers block)
for (let i = 0; i < throttle.THROTTLE_LIMIT; i++) {
throttle.checkThrottle('TestError');
}
// Next email should be blocked
const result = throttle.checkThrottle('TestError');
assert.strictEqual(result.throttled, true);
assert.strictEqual(result.shouldAlert, false);
});
it('should allow different errors independently', function() {
// Block first error
for (let i = 0; i < throttle.THROTTLE_LIMIT; i++) {
throttle.checkThrottle('Error1');
}
// First error should be blocked
const result1 = throttle.checkThrottle('Error1');
assert.strictEqual(result1.throttled, true);
// Different error should pass
const result2 = throttle.checkThrottle('Error2');
assert.strictEqual(result2.throttled, false);
assert.strictEqual(result2.shouldAlert, false);
});
it('should persist throttle data to file', function() {
throttle.checkThrottle('TestError');
// Read file directly
const data = JSON.parse(fs.readFileSync(throttle.THROTTLE_FILE, 'utf8'));
assert.strictEqual(data.emails.length, 1);
assert.strictEqual(data.emails[0].error, 'TestError');
});
it('should persist block state to file', function() {
// Trigger block
for (let i = 0; i < throttle.THROTTLE_LIMIT; i++) {
throttle.checkThrottle('TestError');
}
// Read file directly
const data = JSON.parse(fs.readFileSync(throttle.THROTTLE_FILE, 'utf8'));
assert.ok(data.blocked['TestError']);
assert.ok(data.blocked['TestError'] > Date.now());
});
});
});