@joystick.js/db-canary
Version:
JoystickDB - A minimalist database server for the Joystick framework
222 lines (159 loc) • 6.27 kB
JavaScript
import test from 'ava';
import sinon from 'sinon';
import { create_connection_manager } from '../../../src/server/lib/connection_manager.js';
test.beforeEach((t) => {
t.context.clock = sinon.useFakeTimers();
t.context.mock_socket = {
id: 'test_socket_1',
destroy: sinon.stub()
};
});
test.afterEach((t) => {
t.context.clock.restore();
sinon.restore();
});
test('create_connection_manager - should create manager with default options', (t) => {
const manager = create_connection_manager();
const stats = manager.get_stats();
t.is(stats.total_connections, 0);
t.is(stats.max_connections, 1000);
t.is(stats.idle_timeout_ms, 10 * 60 * 1000);
t.is(stats.request_timeout_ms, 5 * 1000);
});
test('create_connection_manager - should create manager with custom options', (t) => {
const options = {
max_connections: 500,
idle_timeout: 5 * 60 * 1000,
request_timeout: 3 * 1000
};
const manager = create_connection_manager(options);
const stats = manager.get_stats();
t.is(stats.max_connections, 500);
t.is(stats.idle_timeout_ms, 5 * 60 * 1000);
t.is(stats.request_timeout_ms, 3 * 1000);
});
test('add_connection - should add connection successfully', (t) => {
const manager = create_connection_manager();
const result = manager.add_connection(t.context.mock_socket);
t.true(result);
const stats = manager.get_stats();
t.is(stats.total_connections, 1);
});
test('add_connection - should reject connection when limit reached', (t) => {
const manager = create_connection_manager({ max_connections: 1 });
const socket_1 = { id: 'socket_1', destroy: sinon.stub() };
const socket_2 = { id: 'socket_2', destroy: sinon.stub() };
const result_1 = manager.add_connection(socket_1);
const result_2 = manager.add_connection(socket_2);
t.true(result_1);
t.false(result_2);
t.true(socket_2.destroy.calledOnce);
const stats = manager.get_stats();
t.is(stats.total_connections, 1);
});
test('remove_connection - should remove connection', (t) => {
const manager = create_connection_manager();
manager.add_connection(t.context.mock_socket);
let stats = manager.get_stats();
t.is(stats.total_connections, 1);
manager.remove_connection(t.context.mock_socket.id);
stats = manager.get_stats();
t.is(stats.total_connections, 0);
});
test('remove_connection - should handle non-existent connection', (t) => {
const manager = create_connection_manager();
t.notThrows(() => {
manager.remove_connection('non_existent_id');
});
});
test('update_activity - should update connection activity', (t) => {
const manager = create_connection_manager();
manager.add_connection(t.context.mock_socket);
t.context.clock.tick(5000);
t.notThrows(() => {
manager.update_activity(t.context.mock_socket.id);
});
});
test('update_activity - should handle non-existent connection', (t) => {
const manager = create_connection_manager();
t.notThrows(() => {
manager.update_activity('non_existent_id');
});
});
test('idle timeout - should destroy connection after idle timeout', (t) => {
const manager = create_connection_manager({ idle_timeout: 1000 });
manager.add_connection(t.context.mock_socket);
let stats = manager.get_stats();
t.is(stats.total_connections, 1);
t.context.clock.tick(1001);
t.true(t.context.mock_socket.destroy.calledOnce);
stats = manager.get_stats();
t.is(stats.total_connections, 0);
});
test('idle timeout - should reset timer on activity', (t) => {
const manager = create_connection_manager({ idle_timeout: 1000 });
manager.add_connection(t.context.mock_socket);
t.context.clock.tick(500);
manager.update_activity(t.context.mock_socket.id);
t.context.clock.tick(500);
t.false(t.context.mock_socket.destroy.called);
t.context.clock.tick(501);
t.true(t.context.mock_socket.destroy.calledOnce);
});
test('create_request_timeout - should create timeout that destroys connection', (t) => {
const manager = create_connection_manager({ request_timeout: 1000 });
manager.add_connection(t.context.mock_socket);
const timeout = manager.create_request_timeout(t.context.mock_socket.id, 'test_op');
t.context.clock.tick(1001);
t.true(t.context.mock_socket.destroy.calledOnce);
clearTimeout(timeout);
});
test('create_request_timeout - should handle non-existent connection', (t) => {
const manager = create_connection_manager({ request_timeout: 1000 });
const timeout = manager.create_request_timeout('non_existent_id', 'test_op');
t.context.clock.tick(1001);
clearTimeout(timeout);
t.pass();
});
test('get_stats - should return accurate statistics', (t) => {
const manager = create_connection_manager();
const socket_1 = { id: 'socket_1', destroy: sinon.stub() };
const socket_2 = { id: 'socket_2', destroy: sinon.stub() };
manager.add_connection(socket_1);
manager.update_activity(socket_1.id);
manager.update_activity(socket_1.id);
t.context.clock.tick(1000);
manager.add_connection(socket_2);
manager.update_activity(socket_2.id);
const stats = manager.get_stats();
t.is(stats.total_connections, 2);
t.is(stats.total_requests, 3);
t.is(stats.oldest_connection_age_ms, 1000);
});
test('get_stats - should handle empty connections', (t) => {
const manager = create_connection_manager();
const stats = manager.get_stats();
t.is(stats.total_connections, 0);
t.is(stats.total_requests, 0);
t.is(stats.oldest_connection_age_ms, 0);
});
test('shutdown - should destroy all connections', (t) => {
const manager = create_connection_manager();
const socket_1 = { id: 'socket_1', destroy: sinon.stub() };
const socket_2 = { id: 'socket_2', destroy: sinon.stub() };
manager.add_connection(socket_1);
manager.add_connection(socket_2);
let stats = manager.get_stats();
t.is(stats.total_connections, 2);
manager.shutdown();
t.true(socket_1.destroy.calledOnce);
t.true(socket_2.destroy.calledOnce);
stats = manager.get_stats();
t.is(stats.total_connections, 0);
});
test('shutdown - should handle empty connections', (t) => {
const manager = create_connection_manager();
t.notThrows(() => {
manager.shutdown();
});
});