Backend Complete: Interface Refactoring & Service Container Enhancements
Repository Interface Optimization: - Created IBaseRepository.ts and IPaginatedRepository.ts - Refactored all 7 repository interfaces to extend base interfaces - Eliminated ~200 lines of redundant code (70% reduction) - Improved type safety and maintainability Dependency Injection Improvements: - Added EmailService and GameTokenService to DIContainer - Updated CreateUserCommandHandler constructor for DI - Updated RequestPasswordResetCommandHandler constructor for DI - Enhanced testability and service consistency Environment Configuration: - Created comprehensive .env.example with 40+ variables - Organized into 12 logical sections (Database, Security, Email, etc.) - Added security guidelines and best practices - Documented all backend environment requirements Documentation: - Added comprehensive codebase review - Created refactoring summary report - Added frontend implementation guide Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
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();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user