diff --git a/src/workers/__tests__/sms-cluster-worker.test.ts b/src/workers/__tests__/sms-cluster-worker.test.ts
index 8a1adc96..a4340225 100644
--- a/src/workers/__tests__/sms-cluster-worker.test.ts
+++ b/src/workers/__tests__/sms-cluster-worker.test.ts
@@ -1,5 +1,11 @@
import cluster from 'cluster';
-import { startSMSClusterWorker } from '../sms-cluster-worker';
+import {
+ startSMSClusterWorker,
+ sanitizeAndValidateSMS,
+ getSecurityEvents,
+ getQueueSize,
+ getRateLimitStatus
+} from '../sms-cluster-worker';
jest.mock('cluster', () => ({
isPrimary: true,
@@ -14,41 +20,172 @@ jest.mock('os', () => ({
describe('SMS Cluster Worker', () => {
beforeEach(() => {
jest.clearAllMocks();
+ // Clear security events before each test
+ const events = getSecurityEvents();
+ events.length = 0;
});
- it('should fork a worker for each CPU if primary', () => {
- // Override cluster.isPrimary just in case
- Object.defineProperty(cluster, 'isPrimary', { value: true, configurable: true });
+ describe('Cluster Management', () => {
+ it('should fork a worker for each CPU if primary', () => {
+ Object.defineProperty(cluster, 'isPrimary', { value: true, configurable: true });
- const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
- startSMSClusterWorker();
+ startSMSClusterWorker();
- expect(cluster.fork).toHaveBeenCalledTimes(4);
- expect(consoleSpy).toHaveBeenCalledWith(
- expect.stringContaining('Setting up cluster with 4 workers'),
- );
+ expect(cluster.fork).toHaveBeenCalledTimes(4);
+ expect(consoleSpy).toHaveBeenCalledWith(
+ expect.stringContaining('Setting up cluster with 4 workers'),
+ );
- consoleSpy.mockRestore();
+ consoleSpy.mockRestore();
+ });
+
+ it('should bind an exit handler to auto-heal workers', () => {
+ Object.defineProperty(cluster, 'isPrimary', { value: true, configurable: true });
+
+ startSMSClusterWorker();
+
+ expect(cluster.on).toHaveBeenCalledWith('exit', expect.any(Function));
+ });
+
+ it('should execute worker logic if not primary', () => {
+ Object.defineProperty(cluster, 'isPrimary', { value: false, configurable: true });
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
+
+ startSMSClusterWorker();
+
+ expect(cluster.fork).not.toHaveBeenCalled();
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining('Worker'));
+
+ consoleSpy.mockRestore();
+ });
+
+ it('should set worker resource limits', () => {
+ Object.defineProperty(cluster, 'isPrimary', { value: true, configurable: true });
+
+ startSMSClusterWorker();
+
+ expect(cluster.fork).toHaveBeenCalledWith(
+ expect.objectContaining({
+ WORKER_ID: expect.any(Number),
+ NODE_OPTIONS: expect.stringContaining('--max-old-space-size=512'),
+ })
+ );
+ });
});
- it('should bind an exit handler to auto-heal workers', () => {
- Object.defineProperty(cluster, 'isPrimary', { value: true, configurable: true });
+ describe('Input Validation', () => {
+ it('should validate correct phone numbers', () => {
+ const result = sanitizeAndValidateSMS('+15551234567', 'Test message');
+ expect(result.valid).toBe(true);
+ expect(result.sanitized).toBeDefined();
+ });
+
+ it('should reject invalid phone number format', () => {
+ const result = sanitizeAndValidateSMS('5551234567', 'Test message');
+ expect(result.valid).toBe(false);
+ expect(result.reason).toContain('Invalid phone number format');
+ });
- startSMSClusterWorker();
+ it('should reject phone numbers that are too long', () => {
+ const result = sanitizeAndValidateSMS('+155512345678901234567', 'Test message');
+ expect(result.valid).toBe(false);
+ expect(result.reason).toContain('too long');
+ });
- expect(cluster.on).toHaveBeenCalledWith('exit', expect.any(Function));
+ it('should reject non-string phone numbers', () => {
+ const result = sanitizeAndValidateSMS(1234567890 as any, 'Test message');
+ expect(result.valid).toBe(false);
+ expect(result.reason).toContain('must be a string');
+ });
+
+ it('should reject empty messages', () => {
+ const result = sanitizeAndValidateSMS('+15551234567', '');
+ expect(result.valid).toBe(false);
+ expect(result.reason).toContain('cannot be empty');
+ });
+
+ it('should reject messages that are too long', () => {
+ const longMessage = 'a'.repeat(2000);
+ const result = sanitizeAndValidateSMS('+15551234567', longMessage);
+ expect(result.valid).toBe(false);
+ expect(result.reason).toContain('too long');
+ });
+
+ it('should sanitize message content', () => {
+ const result = sanitizeAndValidateSMS('+15551234567', '');
+ expect(result.valid).toBe(true);
+ expect(result.sanitized?.message).not.toContain('