import { tap, expect } from '@git.zone/tstest/tapbundle'; import { SmartNftables } from '../ts/nft.manager.js'; tap.test('should create SmartNftables with default options', async () => { const nft = new SmartNftables(); expect(nft.tableName).toEqual('smartnftables'); expect(nft.family).toEqual('ip'); expect(nft.nat).toBeDefined(); expect(nft.firewall).toBeDefined(); expect(nft.rateLimit).toBeDefined(); }); tap.test('should create SmartNftables with custom options', async () => { const nft = new SmartNftables({ tableName: 'myapp', family: 'inet', }); expect(nft.tableName).toEqual('myapp'); expect(nft.family).toEqual('inet'); }); tap.test('should track rule groups when not root', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); // NAT port forwarding await nft.nat.addPortForwarding('web', { sourcePort: 443, targetHost: '10.0.0.5', targetPort: 8443, }); const status = nft.status(); expect(status.initialized).toBeTrue(); expect(status.activeGroups).toBeGreaterThan(0); expect(status.groups['nat:web']).toBeDefined(); expect(status.groups['nat:web'].ruleCount).toEqual(2); // DNAT + masquerade await nft.cleanup(); const statusAfter = nft.status(); expect(statusAfter.initialized).toBeFalse(); expect(statusAfter.activeGroups).toEqual(0); }); tap.test('should track firewall rules when not root', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.firewall.addRule('block-badguy', { direction: 'input', action: 'drop', sourceIP: '192.168.1.100', }); const status = nft.status(); expect(status.groups['fw:block-badguy']).toBeDefined(); expect(status.groups['fw:block-badguy'].ruleCount).toEqual(1); await nft.firewall.removeRule('block-badguy'); const statusAfter = nft.status(); expect(statusAfter.groups['fw:block-badguy']).toBeUndefined(); await nft.cleanup(); }); tap.test('should track rate limit rules when not root', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.rateLimit.addRateLimit('api-throttle', { port: 8080, protocol: 'tcp', rate: '100/second', burst: 50, perSourceIP: true, }); const status = nft.status(); expect(status.groups['ratelimit:api-throttle']).toBeDefined(); await nft.rateLimit.removeRateLimit('api-throttle'); const statusAfter = nft.status(); expect(statusAfter.groups['ratelimit:api-throttle']).toBeUndefined(); await nft.cleanup(); }); tap.test('should handle blockIP convenience method', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.firewall.blockIP('1.2.3.4'); const status = nft.status(); expect(status.groups['fw:block-1_2_3_4']).toBeDefined(); await nft.cleanup(); }); tap.test('should handle stateful tracking convenience', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.firewall.enableStatefulTracking('input'); const status = nft.status(); expect(status.groups['fw:stateful:input']).toBeDefined(); expect(status.groups['fw:stateful:input'].ruleCount).toEqual(2); // established+related, invalid await nft.cleanup(); }); tap.test('should handle connection rate limiting', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.rateLimit.addConnectionRateLimit('ssh-limit', { port: 22, protocol: 'tcp', rate: '5/second', perSourceIP: true, }); const status = nft.status(); expect(status.groups['connrate:ssh-limit']).toBeDefined(); await nft.cleanup(); }); tap.test('should handle port range forwarding', async () => { const nft = new SmartNftables({ tableName: 'test' }); await nft.initialize(); await nft.nat.addPortRange('gameservers', 27015, 27020, '10.0.0.10', 27015, 'udp'); const status = nft.status(); const group = status.groups['nat:range:gameservers']; expect(group).toBeDefined(); // 6 ports * 2 commands each (DNAT + masquerade) = 12 expect(group.ruleCount).toEqual(12); await nft.cleanup(); }); tap.test('should report correct status', async () => { const nft = new SmartNftables({ tableName: 'statustest', family: 'inet' }); const status = nft.status(); expect(status.tableName).toEqual('statustest'); expect(status.family).toEqual('inet'); expect(status.initialized).toBeFalse(); expect(status.activeGroups).toEqual(0); }); export default tap.start();