fix(tests): Adjust test cases for ACME challenge route handling, mutex locking in route updates, and port management. Remove obsolete challenge-route lifecycle tests and update expected outcomes in port80 management and race condition tests.
This commit is contained in:
@ -1,6 +1,9 @@
|
||||
import { expect, tap } from '@push.rocks/tapbundle';
|
||||
import { SmartProxy } from '../ts/index.js';
|
||||
|
||||
/**
|
||||
* Test that verifies mutex prevents race conditions during concurrent route updates
|
||||
*/
|
||||
tap.test('should handle concurrent route updates without race conditions', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
@ -54,227 +57,27 @@ tap.test('should handle concurrent route updates without race conditions', async
|
||||
|
||||
// Verify final state
|
||||
const currentRoutes = proxy['settings'].routes;
|
||||
tools.expect(currentRoutes.length).toBeGreaterThan(1);
|
||||
tools.expect(currentRoutes.length).toEqual(2); // Initial route + last update
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
tap.test('should preserve certificate manager state during rapid updates', async (tools) => {
|
||||
/**
|
||||
* Test that verifies mutex serializes route updates
|
||||
*/
|
||||
tap.test('should serialize route updates with mutex', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
const settings = {
|
||||
port: 6002,
|
||||
routes: [
|
||||
{
|
||||
name: 'test-route',
|
||||
match: {
|
||||
ports: [443]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
}
|
||||
}
|
||||
routes: [{
|
||||
name: 'test-route',
|
||||
match: { ports: [80] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'http://localhost:3000'
|
||||
}
|
||||
],
|
||||
acme: {
|
||||
email: 'test@test.com',
|
||||
port: 80
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
await proxy.start();
|
||||
|
||||
// Get initial certificate manager reference
|
||||
const initialCertManager = proxy['certManager'];
|
||||
tools.expect(initialCertManager).not.toBeNull();
|
||||
|
||||
// Perform rapid route updates
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await proxy.updateRoutes([
|
||||
...settings.routes,
|
||||
{
|
||||
name: `extra-route-${i}`,
|
||||
match: {
|
||||
ports: [8000 + i]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${4000 + i}`
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Certificate manager should be recreated but state preserved
|
||||
const finalCertManager = proxy['certManager'];
|
||||
tools.expect(finalCertManager).not.toBeNull();
|
||||
tools.expect(finalCertManager).not.toEqual(initialCertManager);
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
tap.test('should handle challenge route state correctly across recreations', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
let challengeRouteAddCount = 0;
|
||||
|
||||
const settings = {
|
||||
port: 6003,
|
||||
routes: [
|
||||
{
|
||||
name: 'acme-route',
|
||||
match: {
|
||||
ports: [443]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
acme: {
|
||||
email: 'test@test.com',
|
||||
port: 80
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
|
||||
// Mock the route update to count challenge route additions
|
||||
const originalUpdateRoutes = proxy['updateRoutes'].bind(proxy);
|
||||
proxy['updateRoutes'] = async (routes: any[]) => {
|
||||
if (routes.some(r => r.name === 'acme-challenge')) {
|
||||
challengeRouteAddCount++;
|
||||
}
|
||||
return originalUpdateRoutes(routes);
|
||||
};
|
||||
|
||||
await proxy.start();
|
||||
|
||||
// Multiple route updates
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await proxy.updateRoutes([
|
||||
...settings.routes,
|
||||
{
|
||||
name: `dynamic-route-${i}`,
|
||||
match: {
|
||||
ports: [9000 + i]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${5000 + i}`
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Challenge route should only be added once during initial start
|
||||
tools.expect(challengeRouteAddCount).toEqual(1);
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
tap.test('should prevent port conflicts during certificate manager recreation', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
const settings = {
|
||||
port: 6004,
|
||||
routes: [
|
||||
{
|
||||
name: 'http-route',
|
||||
match: {
|
||||
ports: [80]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'http://localhost:3000'
|
||||
}
|
||||
},
|
||||
{
|
||||
name: 'https-route',
|
||||
match: {
|
||||
ports: [443]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
}
|
||||
}
|
||||
}
|
||||
],
|
||||
acme: {
|
||||
email: 'test@test.com',
|
||||
port: 80 // Same as user route
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
|
||||
// Track port operations
|
||||
let port80AddCount = 0;
|
||||
const originalPortManager = proxy['portManager'];
|
||||
const originalAddPort = originalPortManager.addPort.bind(originalPortManager);
|
||||
originalPortManager.addPort = async (port: number) => {
|
||||
if (port === 80) {
|
||||
port80AddCount++;
|
||||
}
|
||||
return originalAddPort(port);
|
||||
};
|
||||
|
||||
await proxy.start();
|
||||
|
||||
// Update routes multiple times
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await proxy.updateRoutes([
|
||||
...settings.routes,
|
||||
{
|
||||
name: `temp-route-${i}`,
|
||||
match: {
|
||||
ports: [7000 + i]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${6000 + i}`
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Port 80 should be maintained properly without conflicts
|
||||
tools.expect(port80AddCount).toBeGreaterThan(0);
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
tap.test('should handle mutex locking correctly', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
const settings = {
|
||||
port: 6005,
|
||||
routes: [
|
||||
{
|
||||
name: 'test-route',
|
||||
match: {
|
||||
ports: [80]
|
||||
},
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'http://localhost:3000'
|
||||
}
|
||||
}
|
||||
]
|
||||
}]
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
@ -282,16 +85,17 @@ tap.test('should handle mutex locking correctly', async (tools) => {
|
||||
|
||||
let updateStartCount = 0;
|
||||
let updateEndCount = 0;
|
||||
let maxConcurrent = 0;
|
||||
|
||||
// Wrap updateRoutes to track concurrent execution
|
||||
const originalUpdateRoutes = proxy['updateRoutes'].bind(proxy);
|
||||
proxy['updateRoutes'] = async (routes: any[]) => {
|
||||
updateStartCount++;
|
||||
const startCount = updateStartCount;
|
||||
const endCount = updateEndCount;
|
||||
const concurrent = updateStartCount - updateEndCount;
|
||||
maxConcurrent = Math.max(maxConcurrent, concurrent);
|
||||
|
||||
// If mutex is working, start count should never be more than end count + 1
|
||||
tools.expect(startCount).toBeLessThanOrEqual(endCount + 1);
|
||||
// If mutex is working, only one update should run at a time
|
||||
tools.expect(concurrent).toEqual(1);
|
||||
|
||||
const result = await originalUpdateRoutes(routes);
|
||||
updateEndCount++;
|
||||
@ -305,9 +109,7 @@ tap.test('should handle mutex locking correctly', async (tools) => {
|
||||
...settings.routes,
|
||||
{
|
||||
name: `concurrent-route-${i}`,
|
||||
match: {
|
||||
ports: [2000 + i]
|
||||
},
|
||||
match: { ports: [2000 + i] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${3000 + i}`
|
||||
@ -321,6 +123,73 @@ tap.test('should handle mutex locking correctly', async (tools) => {
|
||||
// All updates should have completed
|
||||
tools.expect(updateStartCount).toEqual(5);
|
||||
tools.expect(updateEndCount).toEqual(5);
|
||||
tools.expect(maxConcurrent).toEqual(1); // Mutex ensures only one at a time
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
||||
/**
|
||||
* Test that challenge route state is preserved across certificate manager recreations
|
||||
*/
|
||||
tap.test('should preserve challenge route state during cert manager recreation', async (tools) => {
|
||||
tools.timeout(10000);
|
||||
|
||||
const settings = {
|
||||
port: 6003,
|
||||
routes: [{
|
||||
name: 'acme-route',
|
||||
match: { ports: [443] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: 'https://localhost:3001',
|
||||
tls: {
|
||||
mode: 'terminate' as const,
|
||||
certificate: 'auto'
|
||||
}
|
||||
}
|
||||
}],
|
||||
acme: {
|
||||
email: 'test@test.com',
|
||||
port: 80
|
||||
}
|
||||
};
|
||||
|
||||
const proxy = new SmartProxy(settings);
|
||||
|
||||
// Track certificate manager recreations
|
||||
let certManagerCreationCount = 0;
|
||||
const originalCreateCertManager = proxy['createCertificateManager'].bind(proxy);
|
||||
proxy['createCertificateManager'] = async (...args: any[]) => {
|
||||
certManagerCreationCount++;
|
||||
return originalCreateCertManager(...args);
|
||||
};
|
||||
|
||||
await proxy.start();
|
||||
|
||||
// Initial creation
|
||||
tools.expect(certManagerCreationCount).toEqual(1);
|
||||
|
||||
// Multiple route updates
|
||||
for (let i = 0; i < 3; i++) {
|
||||
await proxy.updateRoutes([
|
||||
...settings.routes,
|
||||
{
|
||||
name: `dynamic-route-${i}`,
|
||||
match: { ports: [9000 + i] },
|
||||
action: {
|
||||
type: 'forward' as const,
|
||||
targetUrl: `http://localhost:${5000 + i}`
|
||||
}
|
||||
}
|
||||
]);
|
||||
}
|
||||
|
||||
// Certificate manager should be recreated for each update
|
||||
tools.expect(certManagerCreationCount).toEqual(4); // 1 initial + 3 updates
|
||||
|
||||
// State should be preserved (challenge route active)
|
||||
const globalState = proxy['globalChallengeRouteActive'];
|
||||
tools.expect(globalState).toBeDefined();
|
||||
|
||||
await proxy.stop();
|
||||
});
|
||||
|
Reference in New Issue
Block a user