Enhance security middleware and package dependencies
- Updated security headers configuration with stricter settings - Modified rate limiting and helmet middleware setup - Added TypeScript type definitions for Express, rate-limit, and Helmet - Refined referrer policy and HSTS configuration - Improved security middleware chain for better protection
This commit is contained in:
272
__tests__/context/index.test.ts
Normal file
272
__tests__/context/index.test.ts
Normal file
@@ -0,0 +1,272 @@
|
||||
import { jest } from '@jest/globals';
|
||||
import { ContextManager, ResourceType, RelationType, ResourceState, ResourceRelationship } from '../../src/context/index.js';
|
||||
|
||||
describe('Context Manager', () => {
|
||||
let contextManager: ContextManager;
|
||||
|
||||
beforeEach(() => {
|
||||
contextManager = new ContextManager();
|
||||
});
|
||||
|
||||
describe('Resource Management', () => {
|
||||
const testResource: ResourceState = {
|
||||
id: 'test1',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'on',
|
||||
attributes: { name: 'Test Device' },
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
|
||||
it('should add resources', () => {
|
||||
const addHandler = jest.fn();
|
||||
contextManager.on('resource_added', addHandler);
|
||||
|
||||
contextManager.addResource(testResource);
|
||||
|
||||
const resource = contextManager.getResource('test1');
|
||||
expect(resource).toEqual(testResource);
|
||||
expect(addHandler).toHaveBeenCalledWith(testResource);
|
||||
});
|
||||
|
||||
it('should update resources', () => {
|
||||
const updateHandler = jest.fn();
|
||||
contextManager.on('resource_updated', updateHandler);
|
||||
|
||||
contextManager.addResource(testResource);
|
||||
contextManager.updateResource('test1', {
|
||||
state: 'off',
|
||||
attributes: { ...testResource.attributes, brightness: 50 }
|
||||
});
|
||||
|
||||
const resource = contextManager.getResource('test1');
|
||||
expect(resource?.state).toBe('off');
|
||||
expect(resource?.attributes.brightness).toBe(50);
|
||||
expect(updateHandler).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should remove resources', () => {
|
||||
const removeHandler = jest.fn();
|
||||
contextManager.on('resource_removed', removeHandler);
|
||||
|
||||
contextManager.addResource(testResource);
|
||||
contextManager.removeResource('test1');
|
||||
|
||||
expect(contextManager.getResource('test1')).toBeUndefined();
|
||||
expect(removeHandler).toHaveBeenCalledWith(testResource);
|
||||
});
|
||||
|
||||
it('should get resources by type', () => {
|
||||
const resources = [
|
||||
testResource,
|
||||
{
|
||||
id: 'test2',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'off',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
},
|
||||
{
|
||||
id: 'area1',
|
||||
type: ResourceType.AREA,
|
||||
state: 'active',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
}
|
||||
];
|
||||
|
||||
resources.forEach(r => contextManager.addResource(r));
|
||||
|
||||
const devices = contextManager.getResourcesByType(ResourceType.DEVICE);
|
||||
expect(devices).toHaveLength(2);
|
||||
expect(devices.every((d: ResourceState) => d.type === ResourceType.DEVICE)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Relationship Management', () => {
|
||||
const testRelationship: ResourceRelationship = {
|
||||
sourceId: 'device1',
|
||||
targetId: 'area1',
|
||||
type: RelationType.CONTAINS
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
// Add test resources
|
||||
contextManager.addResource({
|
||||
id: 'device1',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
});
|
||||
contextManager.addResource({
|
||||
id: 'area1',
|
||||
type: ResourceType.AREA,
|
||||
state: 'active',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
});
|
||||
});
|
||||
|
||||
it('should add relationships', () => {
|
||||
const addHandler = jest.fn();
|
||||
contextManager.on('relationship_added', addHandler);
|
||||
|
||||
contextManager.addRelationship(testRelationship);
|
||||
|
||||
const related = contextManager.getRelatedResources('device1');
|
||||
expect(related).toHaveLength(2);
|
||||
expect(related.some(r => r.id === 'area1')).toBe(true);
|
||||
expect(addHandler).toHaveBeenCalledWith(testRelationship);
|
||||
});
|
||||
|
||||
it('should remove relationships', () => {
|
||||
const removeHandler = jest.fn();
|
||||
contextManager.on('relationship_removed', removeHandler);
|
||||
|
||||
contextManager.addRelationship(testRelationship);
|
||||
contextManager.removeRelationship(
|
||||
'device1',
|
||||
'area1',
|
||||
RelationType.CONTAINS
|
||||
);
|
||||
|
||||
const related = contextManager.getRelatedResources('device1');
|
||||
expect(related).toHaveLength(0);
|
||||
expect(removeHandler).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should get related resources with depth', () => {
|
||||
// Create a chain: device1 -> area1 -> area2
|
||||
contextManager.addResource({
|
||||
id: 'area2',
|
||||
type: ResourceType.AREA,
|
||||
state: 'active',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
});
|
||||
|
||||
contextManager.addRelationship({
|
||||
sourceId: 'device1',
|
||||
targetId: 'area1',
|
||||
type: RelationType.CONTAINS
|
||||
});
|
||||
contextManager.addRelationship({
|
||||
sourceId: 'area1',
|
||||
targetId: 'area2',
|
||||
type: RelationType.CONTAINS
|
||||
});
|
||||
|
||||
const relatedDepth1 = contextManager.getRelatedResources('device1', undefined, 1);
|
||||
expect(relatedDepth1).toHaveLength(3);
|
||||
|
||||
const relatedDepth2 = contextManager.getRelatedResources('device1', undefined, 2);
|
||||
expect(relatedDepth2).toHaveLength(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Resource Analysis', () => {
|
||||
beforeEach(() => {
|
||||
// Setup test resources and relationships
|
||||
const resources = [
|
||||
{
|
||||
id: 'device1',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
},
|
||||
{
|
||||
id: 'automation1',
|
||||
type: ResourceType.AUTOMATION,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
},
|
||||
{
|
||||
id: 'group1',
|
||||
type: ResourceType.GROUP,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
}
|
||||
];
|
||||
|
||||
resources.forEach(r => contextManager.addResource(r));
|
||||
|
||||
const relationships = [
|
||||
{
|
||||
sourceId: 'automation1',
|
||||
targetId: 'device1',
|
||||
type: RelationType.CONTROLS
|
||||
},
|
||||
{
|
||||
sourceId: 'device1',
|
||||
targetId: 'group1',
|
||||
type: RelationType.DEPENDS_ON
|
||||
},
|
||||
{
|
||||
sourceId: 'group1',
|
||||
targetId: 'device1',
|
||||
type: RelationType.GROUPS
|
||||
}
|
||||
];
|
||||
|
||||
relationships.forEach(r => contextManager.addRelationship(r));
|
||||
});
|
||||
|
||||
it('should analyze resource usage', () => {
|
||||
const analysis = contextManager.analyzeResourceUsage('device1');
|
||||
|
||||
expect(analysis.dependencies).toHaveLength(1);
|
||||
expect(analysis.dependencies[0]).toBe('group1');
|
||||
expect(analysis.dependents).toHaveLength(0);
|
||||
expect(analysis.groups).toHaveLength(1);
|
||||
expect(analysis.usage.controlCount).toBe(0);
|
||||
expect(analysis.usage.triggerCount).toBe(0);
|
||||
expect(analysis.usage.groupCount).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Event Subscriptions', () => {
|
||||
it('should handle resource subscriptions', () => {
|
||||
const callback = jest.fn();
|
||||
const unsubscribe = contextManager.subscribeToResource('test1', callback);
|
||||
|
||||
contextManager.addResource({
|
||||
id: 'test1',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
});
|
||||
|
||||
contextManager.updateResource('test1', { state: 'off' });
|
||||
expect(callback).toHaveBeenCalled();
|
||||
|
||||
unsubscribe();
|
||||
contextManager.updateResource('test1', { state: 'on' });
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should handle type subscriptions', () => {
|
||||
const callback = jest.fn();
|
||||
const unsubscribe = contextManager.subscribeToType(ResourceType.DEVICE, callback);
|
||||
|
||||
const resource = {
|
||||
id: 'test1',
|
||||
type: ResourceType.DEVICE,
|
||||
state: 'on',
|
||||
attributes: {},
|
||||
lastUpdated: Date.now()
|
||||
};
|
||||
|
||||
contextManager.addResource(resource);
|
||||
contextManager.updateResource('test1', { state: 'off' });
|
||||
expect(callback).toHaveBeenCalled();
|
||||
|
||||
unsubscribe();
|
||||
contextManager.updateResource('test1', { state: 'on' });
|
||||
expect(callback).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
189
__tests__/performance/index.test.ts
Normal file
189
__tests__/performance/index.test.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { PerformanceMonitor, PerformanceOptimizer, Metric } from '../../src/performance/index.js';
|
||||
import type { MemoryUsage } from 'node:process';
|
||||
|
||||
describe('Performance Module', () => {
|
||||
describe('PerformanceMonitor', () => {
|
||||
let monitor: PerformanceMonitor;
|
||||
|
||||
beforeEach(() => {
|
||||
monitor = new PerformanceMonitor({
|
||||
responseTime: 500,
|
||||
memoryUsage: 1024 * 1024 * 512, // 512MB
|
||||
cpuUsage: 70
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
monitor.stop();
|
||||
});
|
||||
|
||||
it('should collect metrics', () => {
|
||||
const metricHandler = jest.fn();
|
||||
monitor.on('metric', metricHandler);
|
||||
|
||||
monitor.start();
|
||||
|
||||
// Wait for first collection
|
||||
return new Promise(resolve => setTimeout(() => {
|
||||
expect(metricHandler).toHaveBeenCalled();
|
||||
const calls = metricHandler.mock.calls;
|
||||
|
||||
// Verify memory metrics
|
||||
expect(calls.some(([metric]: [Metric]) =>
|
||||
metric.name === 'memory.heapUsed'
|
||||
)).toBe(true);
|
||||
expect(calls.some(([metric]: [Metric]) =>
|
||||
metric.name === 'memory.heapTotal'
|
||||
)).toBe(true);
|
||||
expect(calls.some(([metric]: [Metric]) =>
|
||||
metric.name === 'memory.rss'
|
||||
)).toBe(true);
|
||||
|
||||
// Verify CPU metrics
|
||||
expect(calls.some(([metric]: [Metric]) =>
|
||||
metric.name === 'cpu.user'
|
||||
)).toBe(true);
|
||||
expect(calls.some(([metric]: [Metric]) =>
|
||||
metric.name === 'cpu.system'
|
||||
)).toBe(true);
|
||||
|
||||
resolve(true);
|
||||
}, 100));
|
||||
});
|
||||
|
||||
it('should emit threshold exceeded events', () => {
|
||||
const thresholdHandler = jest.fn();
|
||||
monitor = new PerformanceMonitor({
|
||||
memoryUsage: 1, // Ensure threshold is exceeded
|
||||
cpuUsage: 1
|
||||
});
|
||||
monitor.on('threshold_exceeded', thresholdHandler);
|
||||
|
||||
monitor.start();
|
||||
|
||||
return new Promise(resolve => setTimeout(() => {
|
||||
expect(thresholdHandler).toHaveBeenCalled();
|
||||
resolve(true);
|
||||
}, 100));
|
||||
});
|
||||
|
||||
it('should clean old metrics', () => {
|
||||
const now = Date.now();
|
||||
const oldMetric: Metric = {
|
||||
name: 'test',
|
||||
value: 1,
|
||||
timestamp: now - 25 * 60 * 60 * 1000 // 25 hours old
|
||||
};
|
||||
const newMetric: Metric = {
|
||||
name: 'test',
|
||||
value: 2,
|
||||
timestamp: now - 1000 // 1 second old
|
||||
};
|
||||
|
||||
monitor.addMetric(oldMetric);
|
||||
monitor.addMetric(newMetric);
|
||||
|
||||
const metrics = monitor.getMetrics(now - 24 * 60 * 60 * 1000);
|
||||
expect(metrics).toHaveLength(1);
|
||||
expect(metrics[0]).toEqual(newMetric);
|
||||
});
|
||||
|
||||
it('should calculate metric averages', () => {
|
||||
const now = Date.now();
|
||||
const metrics: Metric[] = [
|
||||
{ name: 'test', value: 1, timestamp: now - 3000 },
|
||||
{ name: 'test', value: 2, timestamp: now - 2000 },
|
||||
{ name: 'test', value: 3, timestamp: now - 1000 }
|
||||
];
|
||||
|
||||
metrics.forEach(metric => monitor.addMetric(metric));
|
||||
|
||||
const average = monitor.calculateAverage(
|
||||
'test',
|
||||
now - 5000,
|
||||
now
|
||||
);
|
||||
expect(average).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
describe('PerformanceOptimizer', () => {
|
||||
it('should process batches correctly', async () => {
|
||||
const items = [1, 2, 3, 4, 5];
|
||||
const batchSize = 2;
|
||||
const processor = jest.fn(async (batch: number[]) =>
|
||||
batch.map(n => n * 2)
|
||||
);
|
||||
|
||||
const results = await PerformanceOptimizer.processBatch(
|
||||
items,
|
||||
batchSize,
|
||||
processor
|
||||
);
|
||||
|
||||
expect(results).toEqual([2, 4, 6, 8, 10]);
|
||||
expect(processor).toHaveBeenCalledTimes(3); // 2 + 2 + 1 items
|
||||
});
|
||||
|
||||
it('should debounce function calls', (done) => {
|
||||
const fn = jest.fn();
|
||||
const debounced = PerformanceOptimizer.debounce(fn, 100);
|
||||
|
||||
debounced();
|
||||
debounced();
|
||||
debounced();
|
||||
|
||||
setTimeout(() => {
|
||||
expect(fn).not.toHaveBeenCalled();
|
||||
}, 50);
|
||||
|
||||
setTimeout(() => {
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
done();
|
||||
}, 150);
|
||||
});
|
||||
|
||||
it('should throttle function calls', (done) => {
|
||||
const fn = jest.fn();
|
||||
const throttled = PerformanceOptimizer.throttle(fn, 100);
|
||||
|
||||
throttled();
|
||||
throttled();
|
||||
throttled();
|
||||
|
||||
expect(fn).toHaveBeenCalledTimes(1);
|
||||
|
||||
setTimeout(() => {
|
||||
throttled();
|
||||
expect(fn).toHaveBeenCalledTimes(2);
|
||||
done();
|
||||
}, 150);
|
||||
});
|
||||
|
||||
it('should optimize memory when threshold is exceeded', async () => {
|
||||
const originalGc = global.gc;
|
||||
global.gc = jest.fn();
|
||||
|
||||
const memoryUsage = process.memoryUsage;
|
||||
process.memoryUsage = jest.fn().mockImplementation((): MemoryUsage => ({
|
||||
heapUsed: 900,
|
||||
heapTotal: 1000,
|
||||
rss: 2000,
|
||||
external: 0,
|
||||
arrayBuffers: 0
|
||||
}));
|
||||
|
||||
await PerformanceOptimizer.optimizeMemory();
|
||||
|
||||
expect(global.gc).toHaveBeenCalled();
|
||||
|
||||
// Cleanup
|
||||
process.memoryUsage = memoryUsage;
|
||||
if (originalGc) {
|
||||
global.gc = originalGc;
|
||||
} else {
|
||||
delete global.gc;
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
212
__tests__/security/index.test.ts
Normal file
212
__tests__/security/index.test.ts
Normal file
@@ -0,0 +1,212 @@
|
||||
import { TokenManager, validateRequest, sanitizeInput, errorHandler } from '../../src/security';
|
||||
import { Request, Response } from 'express';
|
||||
|
||||
describe('Security Module', () => {
|
||||
describe('TokenManager', () => {
|
||||
const testToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxNzE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
||||
const encryptionKey = 'test_encryption_key';
|
||||
|
||||
it('should encrypt and decrypt tokens', () => {
|
||||
const encrypted = TokenManager.encryptToken(testToken, encryptionKey);
|
||||
const decrypted = TokenManager.decryptToken(encrypted, encryptionKey);
|
||||
|
||||
expect(decrypted).toBe(testToken);
|
||||
});
|
||||
|
||||
it('should validate tokens correctly', () => {
|
||||
expect(TokenManager.validateToken(testToken)).toBe(true);
|
||||
expect(TokenManager.validateToken('invalid_token')).toBe(false);
|
||||
expect(TokenManager.validateToken('')).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle expired tokens', () => {
|
||||
const expiredToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiZXhwIjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c';
|
||||
expect(TokenManager.validateToken(expiredToken)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Request Validation', () => {
|
||||
let mockRequest: Partial<Request>;
|
||||
let mockResponse: Partial<Response>;
|
||||
let mockNext: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest = {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'content-type': 'application/json',
|
||||
authorization: 'Bearer validToken'
|
||||
},
|
||||
is: jest.fn().mockReturnValue(true),
|
||||
body: { test: 'data' }
|
||||
};
|
||||
mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
json: jest.fn()
|
||||
};
|
||||
mockNext = jest.fn();
|
||||
});
|
||||
|
||||
it('should pass valid requests', () => {
|
||||
validateRequest(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
expect(mockResponse.status).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should reject invalid content type', () => {
|
||||
mockRequest.is = jest.fn().mockReturnValue(false);
|
||||
|
||||
validateRequest(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(415);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
error: 'Unsupported Media Type - Content-Type must be application/json'
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject missing token', () => {
|
||||
mockRequest.headers = {};
|
||||
|
||||
validateRequest(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(401);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid or expired token'
|
||||
});
|
||||
});
|
||||
|
||||
it('should reject invalid request body', () => {
|
||||
mockRequest.body = null;
|
||||
|
||||
validateRequest(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(400);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
error: 'Invalid request body'
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Input Sanitization', () => {
|
||||
let mockRequest: Partial<Request>;
|
||||
let mockResponse: Partial<Response>;
|
||||
let mockNext: jest.Mock;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest = {
|
||||
body: {}
|
||||
};
|
||||
mockResponse = {};
|
||||
mockNext = jest.fn();
|
||||
});
|
||||
|
||||
it('should sanitize HTML tags from request body', () => {
|
||||
mockRequest.body = {
|
||||
text: 'Test <script>alert("xss")</script>',
|
||||
nested: {
|
||||
html: '<img src="x" onerror="alert(1)">'
|
||||
}
|
||||
};
|
||||
|
||||
sanitizeInput(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockRequest.body).toEqual({
|
||||
text: 'Test alert("xss")',
|
||||
nested: {
|
||||
html: 'img src="x" onerror="alert(1)"'
|
||||
}
|
||||
});
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle non-object body', () => {
|
||||
mockRequest.body = 'string body';
|
||||
|
||||
sanitizeInput(
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockRequest.body).toBe('string body');
|
||||
expect(mockNext).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handler', () => {
|
||||
let mockRequest: Partial<Request>;
|
||||
let mockResponse: Partial<Response>;
|
||||
let mockNext: jest.Mock;
|
||||
const originalEnv = process.env.NODE_ENV;
|
||||
|
||||
beforeEach(() => {
|
||||
mockRequest = {};
|
||||
mockResponse = {
|
||||
status: jest.fn().mockReturnThis(),
|
||||
json: jest.fn()
|
||||
};
|
||||
mockNext = jest.fn();
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env.NODE_ENV = originalEnv;
|
||||
});
|
||||
|
||||
it('should handle errors in production mode', () => {
|
||||
process.env.NODE_ENV = 'production';
|
||||
const error = new Error('Test error');
|
||||
|
||||
errorHandler(
|
||||
error,
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(500);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
error: 'Internal Server Error',
|
||||
message: undefined
|
||||
});
|
||||
});
|
||||
|
||||
it('should include error message in development mode', () => {
|
||||
process.env.NODE_ENV = 'development';
|
||||
const error = new Error('Test error');
|
||||
|
||||
errorHandler(
|
||||
error,
|
||||
mockRequest as Request,
|
||||
mockResponse as Response,
|
||||
mockNext
|
||||
);
|
||||
|
||||
expect(mockResponse.status).toHaveBeenCalledWith(500);
|
||||
expect(mockResponse.json).toHaveBeenCalledWith({
|
||||
error: 'Internal Server Error',
|
||||
message: 'Test error'
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
1
__tests__/websocket/client.test.ts
Normal file
1
__tests__/websocket/client.test.ts
Normal file
@@ -0,0 +1 @@
|
||||
|
||||
Reference in New Issue
Block a user