160 lines
5.9 KiB
TypeScript
160 lines
5.9 KiB
TypeScript
import { WebSocketService } from '../../../src/Application/Services/WebSocketService';
|
|
import { Server as HttpServer } from 'http';
|
|
import { EventEmitter } from 'events';
|
|
|
|
describe('Chat Configuration', () => {
|
|
let mockHttpServer: HttpServer;
|
|
|
|
beforeAll(() => {
|
|
// Create a more complete HTTP server mock that extends EventEmitter
|
|
const httpServerMock = new EventEmitter();
|
|
|
|
// Add necessary methods that Socket.IO expects
|
|
Object.assign(httpServerMock, {
|
|
on: jest.fn(),
|
|
listen: jest.fn(),
|
|
close: jest.fn(),
|
|
listeners: jest.fn().mockReturnValue([]),
|
|
removeListener: jest.fn(),
|
|
removeAllListeners: jest.fn(),
|
|
setMaxListeners: jest.fn(),
|
|
getMaxListeners: jest.fn().mockReturnValue(0),
|
|
listenerCount: jest.fn().mockReturnValue(0),
|
|
prependListener: jest.fn(),
|
|
prependOnceListener: jest.fn(),
|
|
off: jest.fn(),
|
|
once: jest.fn(),
|
|
emit: jest.fn(),
|
|
// HTTP server specific
|
|
timeout: 0,
|
|
keepAliveTimeout: 5000,
|
|
maxHeadersCount: null,
|
|
headersTimeout: 60000,
|
|
requestTimeout: 0
|
|
});
|
|
|
|
mockHttpServer = httpServerMock as unknown as HttpServer;
|
|
});
|
|
|
|
afterEach(() => {
|
|
// Clean up environment variables
|
|
delete process.env.CHAT_MAX_MESSAGES_PER_USER;
|
|
delete process.env.CHAT_MESSAGE_CLEANUP_WEEKS;
|
|
delete process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES;
|
|
});
|
|
|
|
describe('Environment Variable Configuration', () => {
|
|
it('should use default chat configuration values', () => {
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
expect(service['maxMessagesPerUser']).toBe(100);
|
|
expect(service['messageCleanupWeeks']).toBe(4);
|
|
expect(service['chatTimeout']).toBe(30);
|
|
});
|
|
|
|
it('should use environment variable for CHAT_MAX_MESSAGES_PER_USER', () => {
|
|
process.env.CHAT_MAX_MESSAGES_PER_USER = '50';
|
|
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
expect(service['maxMessagesPerUser']).toBe(50);
|
|
});
|
|
|
|
it('should use environment variable for CHAT_MESSAGE_CLEANUP_WEEKS', () => {
|
|
// Arrange
|
|
process.env.CHAT_MESSAGE_CLEANUP_WEEKS = '8';
|
|
|
|
// Act
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
// Assert
|
|
expect(service['messageCleanupWeeks']).toBe(8);
|
|
});
|
|
|
|
it('should use environment variable for CHAT_INACTIVITY_TIMEOUT_MINUTES', () => {
|
|
// Arrange
|
|
process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES = '60';
|
|
|
|
// Act
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
// Assert
|
|
expect(service['chatTimeout']).toBe(60);
|
|
});
|
|
|
|
it('should handle invalid numeric environment variables gracefully', () => {
|
|
// Arrange
|
|
process.env.CHAT_MAX_MESSAGES_PER_USER = 'invalid';
|
|
process.env.CHAT_MESSAGE_CLEANUP_WEEKS = 'also-invalid';
|
|
process.env.CHAT_INACTIVITY_TIMEOUT_MINUTES = 'not-a-number';
|
|
|
|
// Act
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
// Assert - parseInt of invalid strings returns NaN
|
|
expect(service['maxMessagesPerUser']).toBe(NaN);
|
|
expect(service['messageCleanupWeeks']).toBe(NaN);
|
|
expect(service['chatTimeout']).toBe(NaN);
|
|
});
|
|
});
|
|
|
|
describe('Rate Limiting Logic', () => {
|
|
it('should initialize with empty user message counts', () => {
|
|
// Act
|
|
const service = new WebSocketService(mockHttpServer);
|
|
|
|
// Assert
|
|
expect(service['userMessageCounts']).toBeDefined();
|
|
expect(service['userMessageCounts'].size).toBe(0);
|
|
});
|
|
|
|
it('should allow messages within rate limit', () => {
|
|
// Arrange
|
|
process.env.CHAT_MAX_MESSAGES_PER_USER = '5';
|
|
const service = new WebSocketService(mockHttpServer);
|
|
const userId = 'test-user';
|
|
|
|
// Act & Assert - should allow first 5 messages
|
|
for (let i = 0; i < 5; i++) {
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(true);
|
|
}
|
|
});
|
|
|
|
it('should block messages when rate limit exceeded', () => {
|
|
// Arrange
|
|
process.env.CHAT_MAX_MESSAGES_PER_USER = '3';
|
|
const service = new WebSocketService(mockHttpServer);
|
|
const userId = 'test-user';
|
|
|
|
// Act - send 3 messages (should be allowed)
|
|
for (let i = 0; i < 3; i++) {
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(true);
|
|
}
|
|
|
|
// Assert - 4th message should be blocked
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(false);
|
|
});
|
|
|
|
it('should reset rate limit after time window', (done) => {
|
|
// Arrange
|
|
process.env.CHAT_MAX_MESSAGES_PER_USER = '2';
|
|
const service = new WebSocketService(mockHttpServer);
|
|
const userId = 'test-user';
|
|
|
|
// Act - exhaust rate limit
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(true);
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(true);
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(false); // Should be blocked
|
|
|
|
// Mock time passage by manipulating the internal state
|
|
const userStats = service['userMessageCounts'].get(userId)!;
|
|
userStats.lastReset = Date.now() - (60 * 1000 + 1); // More than 1 minute ago
|
|
service['userMessageCounts'].set(userId, userStats);
|
|
|
|
// Assert - should be allowed again after reset
|
|
expect(service['checkMessageRateLimit'](userId)).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
});
|