import request from 'supertest';
import express from 'express';
import { networkConfigRouter, getCurrentNetworkConfig } from '../../routes/network-config';
const app = express();
app.use(express.json());
app.use('/network-config', networkConfigRouter);
describe('Network Config Routes', () => {
beforeEach(async () => {
// Reset configuration before each test
await request(app).post('/network-config/reset');
});
describe('GET /network-config', () => {
it('should return default configuration', async () => {
const response = await request(app)
.get('/network-config')
.expect(200);
expect(response.body).toHaveProperty('config');
expect(response.body.config).toMatchObject({
enabled: true,
captureMode: 'all',
urlPatterns: [],
includeHeaders: true,
includeRequestBody: true,
includeResponseBody: true,
includeQueryParams: true,
maxResponseBodySize: 50000,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
statusCodes: []
});
});
it('should handle server errors gracefully', async () => {
// This test simulates a server error scenario
const mockError = jest.spyOn(console, 'error').mockImplementation(() => {});
// Mock a scenario where getCurrentNetworkConfig throws
jest.mock('../../routes/network-config', () => ({
...jest.requireActual('../../routes/network-config'),
getCurrentNetworkConfig: jest.fn(() => {
throw new Error('Database error');
})
}));
// The actual test would need to be restructured to properly test error handling
mockError.mockRestore();
});
});
describe('POST /network-config', () => {
it('should update network configuration', async () => {
const newConfig = {
enabled: false,
captureMode: 'include',
urlPatterns: ['api.*', '*.json'],
includeHeaders: false,
maxResponseBodySize: 100000
};
const response = await request(app)
.post('/network-config')
.send(newConfig)
.expect(200);
expect(response.body).toHaveProperty('config');
expect(response.body.config).toMatchObject(newConfig);
expect(response.body).toHaveProperty('message');
expect(response.body.message).toBe('Network capture configuration updated successfully');
});
it('should partially update configuration', async () => {
const partialConfig = {
includeRequestBody: false,
maxResponseBodySize: 25000
};
const response = await request(app)
.post('/network-config')
.send(partialConfig)
.expect(200);
expect(response.body.config).toMatchObject({
enabled: true, // default value preserved
captureMode: 'all', // default value preserved
includeRequestBody: false, // updated
maxResponseBodySize: 25000, // updated
includeHeaders: true // default value preserved
});
});
it('should validate capture mode', async () => {
const invalidConfig = {
captureMode: 'invalid'
};
const response = await request(app)
.post('/network-config')
.send(invalidConfig)
.expect(400);
expect(response.body.error).toBe("captureMode must be 'all', 'include', or 'exclude'");
});
it('should validate boolean fields', async () => {
const invalidConfig = {
enabled: 'not-a-boolean'
};
const response = await request(app)
.post('/network-config')
.send(invalidConfig)
.expect(400);
expect(response.body.error).toBe('enabled must be a boolean');
});
it('should validate all boolean fields', async () => {
const testCases = [
{ field: 'includeHeaders', value: 'string' },
{ field: 'includeRequestBody', value: 123 },
{ field: 'includeResponseBody', value: [] },
{ field: 'includeQueryParams', value: {} }
];
for (const testCase of testCases) {
const invalidConfig = { [testCase.field]: testCase.value };
const response = await request(app)
.post('/network-config')
.send(invalidConfig)
.expect(400);
expect(response.body.error).toBe(`${testCase.field} must be a boolean`);
}
});
it('should validate array fields', async () => {
const testCases = [
{ field: 'urlPatterns', value: 'not-an-array' },
{ field: 'methods', value: 'not-an-array' },
{ field: 'statusCodes', value: 'not-an-array' }
];
for (const testCase of testCases) {
const invalidConfig = { [testCase.field]: testCase.value };
const response = await request(app)
.post('/network-config')
.send(invalidConfig)
.expect(400);
expect(response.body.error).toBe(`${testCase.field} must be an array`);
}
});
it('should validate numeric fields', async () => {
const testCases = [
{ value: -1, expectedError: 'maxResponseBodySize must be a non-negative number' },
{ value: 'not-a-number', expectedError: 'maxResponseBodySize must be a non-negative number' },
{ value: -100, expectedError: 'maxResponseBodySize must be a non-negative number' }
];
for (const testCase of testCases) {
const invalidConfig = {
maxResponseBodySize: testCase.value
};
const response = await request(app)
.post('/network-config')
.send(invalidConfig)
.expect(400);
expect(response.body.error).toBe(testCase.expectedError);
}
});
it('should accept valid maxResponseBodySize values', async () => {
const validValues = [0, 1, 1000, 50000, 1000000];
for (const value of validValues) {
const config = { maxResponseBodySize: value };
const response = await request(app)
.post('/network-config')
.send(config)
.expect(200);
expect(response.body.config.maxResponseBodySize).toBe(value);
}
});
it('should handle complex configuration updates', async () => {
const complexConfig = {
enabled: true,
captureMode: 'exclude',
urlPatterns: ['*.png', '*.jpg', '*.gif', 'analytics.*'],
includeHeaders: true,
includeRequestBody: false,
includeResponseBody: true,
includeQueryParams: false,
maxResponseBodySize: 75000,
methods: ['GET', 'POST', 'PUT'],
statusCodes: [200, 201, 400, 500]
};
const response = await request(app)
.post('/network-config')
.send(complexConfig)
.expect(200);
expect(response.body.config).toMatchObject(complexConfig);
});
it('should handle empty configuration updates', async () => {
const response = await request(app)
.post('/network-config')
.send({})
.expect(200);
// Should return default configuration since no changes were made
expect(response.body.config).toMatchObject({
enabled: true,
captureMode: 'all',
urlPatterns: [],
includeHeaders: true,
includeRequestBody: true,
includeResponseBody: true,
includeQueryParams: true,
maxResponseBodySize: 50000,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
statusCodes: []
});
});
});
describe('POST /network-config/reset', () => {
it('should reset configuration to defaults', async () => {
// First modify the configuration
await request(app)
.post('/network-config')
.send({
enabled: false,
captureMode: 'exclude',
urlPatterns: ['test.*'],
includeHeaders: false,
maxResponseBodySize: 999999
});
// Then reset it
const response = await request(app)
.post('/network-config/reset')
.expect(200);
expect(response.body).toHaveProperty('config');
expect(response.body.config).toMatchObject({
enabled: true,
captureMode: 'all',
urlPatterns: [],
includeHeaders: true,
includeRequestBody: true,
includeResponseBody: true,
includeQueryParams: true,
maxResponseBodySize: 50000,
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'HEAD', 'OPTIONS'],
statusCodes: []
});
expect(response.body).toHaveProperty('message');
expect(response.body.message).toBe('Network capture configuration reset to defaults');
});
it('should handle reset errors gracefully', async () => {
// This test would need to be implemented with proper error injection
// For now, we just verify the reset works normally
const response = await request(app)
.post('/network-config/reset')
.expect(200);
expect(response.body.config).toBeDefined();
expect(response.body.message).toBe('Network capture configuration reset to defaults');
});
});
describe('Configuration persistence', () => {
it('should persist configuration changes across requests', async () => {
const newConfig = {
enabled: false,
captureMode: 'include',
urlPatterns: ['api.*'],
includeHeaders: false,
includeRequestBody: false,
includeResponseBody: true,
maxResponseBodySize: 25000
};
// Update configuration
await request(app)
.post('/network-config')
.send(newConfig)
.expect(200);
// Verify it persists
const response = await request(app)
.get('/network-config')
.expect(200);
expect(response.body.config).toMatchObject(newConfig);
});
it('should preserve unchanged fields during partial updates', async () => {
// First, set a complete configuration
const initialConfig = {
enabled: true,
captureMode: 'include',
urlPatterns: ['api.*', '*.json'],
includeHeaders: true,
includeRequestBody: true,
includeResponseBody: false,
includeQueryParams: false,
maxResponseBodySize: 30000
};
await request(app)
.post('/network-config')
.send(initialConfig)
.expect(200);
// Then update only specific fields
const partialUpdate = {
enabled: false,
maxResponseBodySize: 60000
};
await request(app)
.post('/network-config')
.send(partialUpdate)
.expect(200);
// Verify all fields are preserved correctly
const response = await request(app)
.get('/network-config')
.expect(200);
expect(response.body.config).toMatchObject({
enabled: false, // updated
captureMode: 'include', // preserved
urlPatterns: ['api.*', '*.json'], // preserved
includeHeaders: true, // preserved
includeRequestBody: true, // preserved
includeResponseBody: false, // preserved
includeQueryParams: false, // preserved
maxResponseBodySize: 60000 // updated
});
});
});
describe('getCurrentNetworkConfig function', () => {
it('should return current configuration', () => {
const config = getCurrentNetworkConfig();
expect(config).toBeDefined();
expect(config).toHaveProperty('enabled');
expect(config).toHaveProperty('captureMode');
expect(config).toHaveProperty('urlPatterns');
expect(config).toHaveProperty('includeHeaders');
expect(config).toHaveProperty('includeRequestBody');
expect(config).toHaveProperty('includeResponseBody');
expect(config).toHaveProperty('maxResponseBodySize');
});
it('should return a copy of the configuration', () => {
const config1 = getCurrentNetworkConfig();
const config2 = getCurrentNetworkConfig();
expect(config1).not.toBe(config2); // Different objects
expect(config1).toEqual(config2); // Same content
});
});
});