// Ported from filius-ipad ios/FiliusPadTests/TopologyJavaRouteTableTests.swift. // Left out: column titles and geometry (editor), and the reducer tests // (they move with the document layer, packages/doc). import { describe, expect, it } from 'vitest'; import { appendingBlankDraftIfNeeded, bestMatchingManualRoute, blankRouteDraft, isValidJavaIPAddress, isValidJavaSubnetMask, manualRouteFromDraft, routeTableRows, visibleRouteRows, type ManualRoute, } from '../src/index.js'; function route(destinationNetwork: string, subnetMask: string, gateway: string, interfaceIPAddress: string): ManualRoute { return { destinationNetwork, subnetMask, gateway, interfaceIPAddress }; } describe('Java route table', () => { it('projects multiple interfaces in Java order with Java values', () => { const rows = routeTableRows( [ { ipAddress: '10.0.0.1', subnetMask: '255.255.255.0' }, { ipAddress: '192.168.0.1', subnetMask: '255.255.255.0' }, ], [ route('172.16.0.0', '255.255.0.0', '10.0.0.2', '10.0.0.1'), route('203.0.113.0', '255.255.255.0', '192.168.0.2', '192.168.0.1'), ], '10.0.0.254', ); expect(rows.map((row) => row.destinationNetwork)).toEqual([ '192.168.0.1', '10.0.0.1', '192.168.0.0', '10.0.0.0', '127.0.0.0', '172.16.0.0', '203.0.113.0', '0.0.0.0', ]); expect(rows.map((row) => row.origin)).toEqual([ 'localHost', 'localHost', 'connected', 'connected', 'localhost', 'manual', 'manual', 'defaultGateway', ]); expect(rows.map((row) => row.isEditable)).toEqual([false, false, false, false, false, true, true, false]); expect(rows[0]).toMatchObject({ subnetMask: '255.255.255.255', nextHop: '127.0.0.1', interfaceIPAddress: '127.0.0.1' }); expect(rows[2]?.nextHop).toBe('192.168.0.1'); expect(rows[4]?.nextHop).toBe('127.0.0.1'); expect(rows.at(-1)).toMatchObject({ nextHop: '10.0.0.254', interfaceIPAddress: '10.0.0.1' }); }); it('puts the default route on the last matching interface and falls back to the primary one', () => { const interfaces = [ { ipAddress: '10.0.0.1', subnetMask: '255.0.0.0' }, { ipAddress: '10.1.0.1', subnetMask: '255.0.0.0' }, ]; const matching = routeTableRows(interfaces, [], '10.2.0.1'); expect(matching.at(-1)).toMatchObject({ origin: 'defaultGateway', interfaceIPAddress: '10.1.0.1' }); const fallback = routeTableRows(interfaces, [], '192.168.0.1'); expect(fallback.at(-1)?.interfaceIPAddress).toBe('10.0.0.1'); }); it('shows only manual rows when generated rows are hidden', () => { const rows = routeTableRows( [{ ipAddress: '10.0.0.1', subnetMask: '255.255.255.0' }], [route('172.16.0.0', '255.255.0.0', '10.0.0.2', '10.0.0.1')], '10.0.0.254', ); expect(visibleRouteRows(rows, false).map((row) => row.origin)).toEqual(['manual']); expect(visibleRouteRows(rows, true)).toEqual(rows); }); it('accepts leading zeros and non-contiguous masks like Java', () => { expect(isValidJavaIPAddress('001.002.003.004')).toBe(true); expect(isValidJavaSubnetMask('255.0.255.0')).toBe(true); expect(isValidJavaIPAddress('256.0.0.1')).toBe(false); expect(isValidJavaSubnetMask('255.255.255')).toBe(false); expect(isValidJavaIPAddress(' 10.0.0.1')).toBe(false); }); it('commits a draft only when all four cells are valid', () => { const draft = blankRouteDraft('00000000-0000-0000-0000-000000000013'); draft.destinationNetwork = '10.0.0.0'; draft.subnetMask = '255.0.255.0'; draft.nextGateway = '10.0.0.2'; expect(manualRouteFromDraft(draft)).toBeUndefined(); draft.interfaceIPAddress = '10.0.0.1'; expect(manualRouteFromDraft(draft)).toEqual(route('10.0.0.0', '255.0.255.0', '10.0.0.2', '10.0.0.1')); }); it('keeps at most one pending blank draft', () => { let counter = 0; const newId = () => `draft-${(counter += 1)}`; const first = appendingBlankDraftIfNeeded([], newId); const second = appendingBlankDraftIfNeeded(first, newId); expect(first).toHaveLength(1); expect(second).toEqual(first); expect(second[0]?.isPendingInsertion).toBe(true); }); it('picks the greatest numeric mask and keeps the first row on a tie', () => { const broad = route('10.0.0.0', '255.0.0.0', '1.1.1.1', '10.0.0.1'); const firstSpecific = route('10.1.0.0', '255.255.0.0', '2.2.2.2', '10.0.0.1'); const tiedSpecific = route('10.1.0.0', '255.255.0.0', '3.3.3.3', '10.0.0.1'); expect(bestMatchingManualRoute('10.1.2.3', [broad, firstSpecific, tiedSpecific])).toBe(firstSpecific); }); it('never includes learned RIP rows in the static projection', () => { const rows = routeTableRows([{ ipAddress: '10.0.0.1', subnetMask: '255.255.255.0' }], [], undefined); expect(rows.some((row) => row.origin === 'rip')).toBe(false); }); });