164 lines
5.2 KiB
TypeScript
164 lines
5.2 KiB
TypeScript
import { SmartProxy } from '../ts/proxies/smart-proxy/index.js';
|
|
import { NFTablesManager } from '../ts/proxies/smart-proxy/nftables-manager.js';
|
|
import { createNfTablesRoute } from '../ts/proxies/smart-proxy/utils/route-helpers.js';
|
|
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
|
import * as child_process from 'child_process';
|
|
import { promisify } from 'util';
|
|
|
|
const exec = promisify(child_process.exec);
|
|
|
|
// Check if we have root privileges
|
|
async function checkRootPrivileges(): Promise<boolean> {
|
|
try {
|
|
const { stdout } = await exec('id -u');
|
|
return stdout.trim() === '0';
|
|
} catch (err) {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// Skip tests if not root
|
|
const isRoot = await checkRootPrivileges();
|
|
if (!isRoot) {
|
|
console.log('');
|
|
console.log('========================================');
|
|
console.log('NFTables status tests require root privileges');
|
|
console.log('Skipping NFTables status tests');
|
|
console.log('========================================');
|
|
console.log('');
|
|
}
|
|
|
|
// Define the test function based on root privileges
|
|
const testFn = isRoot ? tap.test : tap.skip.test;
|
|
|
|
testFn('NFTablesManager status functionality', async () => {
|
|
const nftablesManager = new NFTablesManager({ routes: [] });
|
|
|
|
// Create test routes
|
|
const testRoutes = [
|
|
createNfTablesRoute('test-route-1', { host: 'localhost', port: 8080 }, { ports: 9080 }),
|
|
createNfTablesRoute('test-route-2', { host: 'localhost', port: 8081 }, { ports: 9081 }),
|
|
createNfTablesRoute('test-route-3', { host: 'localhost', port: 8082 }, {
|
|
ports: 9082,
|
|
ipAllowList: ['127.0.0.1', '192.168.1.0/24']
|
|
})
|
|
];
|
|
|
|
// Get initial status (should be empty)
|
|
let status = await nftablesManager.getStatus();
|
|
expect(Object.keys(status).length).toEqual(0);
|
|
|
|
// Provision routes
|
|
for (const route of testRoutes) {
|
|
await nftablesManager.provisionRoute(route);
|
|
}
|
|
|
|
// Get status after provisioning
|
|
status = await nftablesManager.getStatus();
|
|
expect(Object.keys(status).length).toEqual(3);
|
|
|
|
// Check status structure
|
|
for (const routeStatus of Object.values(status)) {
|
|
expect(routeStatus).toHaveProperty('active');
|
|
expect(routeStatus).toHaveProperty('ruleCount');
|
|
expect(routeStatus).toHaveProperty('lastUpdate');
|
|
expect(routeStatus.active).toBeTrue();
|
|
}
|
|
|
|
// Deprovision one route
|
|
await nftablesManager.deprovisionRoute(testRoutes[0]);
|
|
|
|
// Check status after deprovisioning
|
|
status = await nftablesManager.getStatus();
|
|
expect(Object.keys(status).length).toEqual(2);
|
|
|
|
// Cleanup remaining routes
|
|
await nftablesManager.stop();
|
|
|
|
// Final status should be empty
|
|
status = await nftablesManager.getStatus();
|
|
expect(Object.keys(status).length).toEqual(0);
|
|
});
|
|
|
|
testFn('SmartProxy getNfTablesStatus functionality', async () => {
|
|
const smartProxy = new SmartProxy({
|
|
routes: [
|
|
createNfTablesRoute('proxy-test-1', { host: 'localhost', port: 3000 }, { ports: 3001 }),
|
|
createNfTablesRoute('proxy-test-2', { host: 'localhost', port: 3002 }, { ports: 3003 }),
|
|
// Include a non-NFTables route to ensure it's not included in the status
|
|
{
|
|
name: 'non-nftables-route',
|
|
match: { ports: 3004 },
|
|
action: {
|
|
type: 'forward',
|
|
target: { host: 'localhost', port: 3005 }
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
// Start the proxy
|
|
await smartProxy.start();
|
|
|
|
// Get NFTables status
|
|
const status = await smartProxy.getNfTablesStatus();
|
|
|
|
// Should only have 2 NFTables routes
|
|
const statusKeys = Object.keys(status);
|
|
expect(statusKeys.length).toEqual(2);
|
|
|
|
// Check that both NFTables routes are in the status
|
|
const routeIds = statusKeys.sort();
|
|
expect(routeIds).toContain('proxy-test-1:3001');
|
|
expect(routeIds).toContain('proxy-test-2:3003');
|
|
|
|
// Verify status structure
|
|
for (const [routeId, routeStatus] of Object.entries(status)) {
|
|
expect(routeStatus).toHaveProperty('active', true);
|
|
expect(routeStatus).toHaveProperty('ruleCount');
|
|
expect(routeStatus.ruleCount).toHaveProperty('total');
|
|
expect(routeStatus.ruleCount.total).toBeGreaterThan(0);
|
|
}
|
|
|
|
// Stop the proxy
|
|
await smartProxy.stop();
|
|
|
|
// After stopping, status should be empty
|
|
const finalStatus = await smartProxy.getNfTablesStatus();
|
|
expect(Object.keys(finalStatus).length).toEqual(0);
|
|
});
|
|
|
|
testFn('NFTables route update status tracking', async () => {
|
|
const smartProxy = new SmartProxy({
|
|
routes: [
|
|
createNfTablesRoute('update-test', { host: 'localhost', port: 4000 }, { ports: 4001 })
|
|
]
|
|
});
|
|
|
|
await smartProxy.start();
|
|
|
|
// Get initial status
|
|
let status = await smartProxy.getNfTablesStatus();
|
|
expect(Object.keys(status).length).toEqual(1);
|
|
const initialUpdate = status['update-test:4001'].lastUpdate;
|
|
|
|
// Wait a moment
|
|
await new Promise(resolve => setTimeout(resolve, 10));
|
|
|
|
// Update the route
|
|
await smartProxy.updateRoutes([
|
|
createNfTablesRoute('update-test', { host: 'localhost', port: 4002 }, { ports: 4001 })
|
|
]);
|
|
|
|
// Get status after update
|
|
status = await smartProxy.getNfTablesStatus();
|
|
expect(Object.keys(status).length).toEqual(1);
|
|
const updatedTime = status['update-test:4001'].lastUpdate;
|
|
|
|
// The update time should be different
|
|
expect(updatedTime.getTime()).toBeGreaterThan(initialUpdate.getTime());
|
|
|
|
await smartProxy.stop();
|
|
});
|
|
|
|
export default tap.start(); |