Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions src/host-iptables-chain-branches.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
/**
* Branch coverage tests for host-iptables-chain.ts and host-iptables-validation.ts.
*
* These tests cover the two remaining uncovered branches identified by the
* coverage report:
*
* 1. host-iptables-chain.ts — `checkPermissionsAndSetupChain`:
* `throw new Error('iptables is required but was not found...')` inside the
* DOCKER-USER check catch block — triggered when the DOCKER-USER list command
* fails with an ENOENT / "not found" error.
*
* 2. host-iptables-validation.ts — `isMissingIptablesError`:
* `error instanceof Error ? error.message : ''` — the `''` fallback branch
* exercised when `isMissingIptablesError` receives a non-Error thrown value
* (e.g. a plain object or string).
*/

import {
execaResult,
execaMissingCommandError,
mockedExeca,
setupHostIptablesTestSuite,
} from './test-helpers/host-iptables-test-setup';
import { checkPermissionsAndSetupChain } from './host-iptables-chain';
import { isMissingIptablesError } from './host-iptables-validation';
import { iptablesSharedTestHelpers } from './host-iptables-shared.test-utils';

describe('host-iptables-chain branch coverage', () => {
setupHostIptablesTestSuite(iptablesSharedTestHelpers.resetIpv6State);

// -------------------------------------------------------------------------
// checkPermissionsAndSetupChain – the DOCKER-USER list check fails with an
// ENOENT / "not found" error. This path re-throws as a user-readable
// "iptables is required but was not found" message.
// -------------------------------------------------------------------------
describe('checkPermissionsAndSetupChain – DOCKER-USER check fails with ENOENT', () => {
it('throws a user-readable message when DOCKER-USER list reports iptables not found', async () => {
mockedExeca
// iptables --version — succeeds (iptables binary IS present)
.mockResolvedValueOnce(execaResult({ exitCode: 0 }))
// iptables -L DOCKER-USER — fails with ENOENT (iptables binary absent on this call)
.mockRejectedValueOnce(execaMissingCommandError('iptables'));

await expect(checkPermissionsAndSetupChain('FW_TEST')).rejects.toThrow(
'iptables is required but was not found. Please install iptables and try again.',
);
});

it('throws a user-readable message when DOCKER-USER list rejects with a "not found" message', async () => {
const notFoundError = new Error('iptables: not found');

mockedExeca
// iptables --version — succeeds
.mockResolvedValueOnce(execaResult({ exitCode: 0 }))
// iptables -L DOCKER-USER — fails with "not found" in message
.mockRejectedValueOnce(notFoundError);

await expect(checkPermissionsAndSetupChain('FW_TEST')).rejects.toThrow(
'iptables is required but was not found. Please install iptables and try again.',
);
});
});
});

// -------------------------------------------------------------------------
// isMissingIptablesError – the `''` branch of the ternary:
// `const message = error instanceof Error ? error.message : ''`
// This branch fires when the caught value is NOT an Error instance.
// -------------------------------------------------------------------------
describe('isMissingIptablesError – non-Error thrown values', () => {
it('returns false for a plain object with no code or message (takes the "" branch)', () => {
// Not an Error instance → message = '' → all three conditions false → returns false
expect(isMissingIptablesError({ someKey: 'someValue' })).toBe(false);
});

it('returns false for a thrown string (takes the "" branch)', () => {
// Strings are not Error instances; the '' branch produces no match
expect(isMissingIptablesError('something went wrong')).toBe(false);
});

it('returns false for null (takes the "" branch)', () => {
expect(isMissingIptablesError(null)).toBe(false);
});

it('returns true for a plain object with code ENOENT (takes the "" branch for message)', () => {
// code === 'ENOENT' is true even though message falls to '' — confirms short-circuit
expect(isMissingIptablesError({ code: 'ENOENT' })).toBe(true);
});

it('returns false for a plain object with non-ENOENT code (takes the "" branch)', () => {
// code !== 'ENOENT', message = '' → no match
expect(isMissingIptablesError({ code: 'EAGAIN', message: 'resource temporarily unavailable' })).toBe(false);
});
});
Loading