// The command line's network commands as Java FILIUS runs them. // tools/java-oracle's TerminalRun typed lines into FILIUS' command line on // the hosts of terminal-net at set times and wrote every frame each card // saw, Java's tables of them, and everything each command line told its // window (terminal-java-.java.json). The engine types the same lines // at the same times (apps/terminal.ts): each card's frames in the same // order, with the same content and rows, at about the same time; the same // texts and busy marks, at about the same time. import { readFileSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { describe, expect, it } from 'vitest'; import { NetworkRuntimeEngine, withSnapshot } from '../src/index.js'; import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, watchedEngine, withClientPortText, type JavaFrame, type JavaTables, type SeenFrame } from './javaRun.js'; import { POLL_MILLISECONDS } from './programLines.js'; import { snapshotOf, type Scenario } from './scenario.js'; const scenarioDirectory = join(dirname(fileURLToPath(import.meta.url)), 'scenarios'); const CLIENT = '00000000-0000-0000-0000-000000000401'; const DNS = '00000000-0000-0000-0000-000000000402'; const WEB = '00000000-0000-0000-0000-000000000403'; const ECHO = '00000000-0000-0000-0000-000000000404'; const FAR = '00000000-0000-0000-0000-000000000406'; const HOSTS = [CLIENT, DNS, WEB, ECHO, FAR]; type Told = ['text' | 'busy', string]; interface JavaRun extends JavaTables { frames: Record; acts: [number, string, string][]; /** time, host, kind (call, text or busy), text */ events: [number, string, string, string][]; } const STORIES = [ 'pingLan', 'pingRouted', 'pingNobody', 'pingUnreachable', 'pingBroadcast', 'pingSelf', 'pingErrors', 'interrupt', 'dns', 'dnsTimeout', 'arp', 'netstat', 'traceLan', 'traceRouted', 'traceUnreachable', 'traceNoRoute', 'traceNobody', 'traceSelf', 'traceErrors', 'traceInterrupt', 'tcpdump', ] as const; type Lines = Record; function javaLines(java: JavaRun): Lines { const lines: Lines = Object.fromEntries(HOSTS.map((host) => [host, []])); for (const [time, host, kind, text] of java.events) { if (kind === 'call') continue; lines[host]!.push([time, [kind as Told[0], text]]); } return lines; } function engineLines(engine: NetworkRuntimeEngine): Lines { return Object.fromEntries( HOSTS.map((host) => [ host, engine.terminal(host)!.log.all.map((line): [number, Told] => [line.timeMilliseconds, line.kind === 'text' ? ['text', line.text] : ['busy', String(line.busy)]]), ]), ); } /** * A text with placeholders: a client's port; a ping's time (compared on its * own); the rows of an ARP or netstat table sorted (Java lists them in a * HashMap's order, the engine in the order learned, or made). */ function lineText(text: string): string { const table = /^(\| [^\n]*\|\n-+\n)((?:\|[^\n]*\n)*)(-+\n)?$/.exec(text); // Sorted within each protocol of netstat's, the protocols in the order they come (TCP, then UDP, in Java too). const rows = table?.[2]!.split('\n').filter(Boolean) ?? []; const protocols = [...new Set(rows.map((row) => /^\| (TCP|UDP) /.exec(row)?.[1] ?? ''))]; const protocol = (row: string) => protocols.indexOf(/^\| (TCP|UDP) /.exec(row)?.[1] ?? ''); rows.sort((lhs, rhs) => protocol(lhs) - protocol(rhs) || (lhs < rhs ? -1 : lhs > rhs ? 1 : 0)); const sorted = table === null ? text : `${table[1]}${rows.join('\n')}\n${table[3] ?? ''}`; // tcpdump: a frame's time of day, a ping's identifier, a DNS query's ID. return withClientPortText(sorted) .replace(/time=\d+ms/, 'time=…ms') .replace(/^\n {2}\d{2}:\d{2}:\d{2}\.\d{3} /, '\n