fix(fls): read and write a network card's wireless flag

A notebook that joins a switch's wireless network has its card marked
wireless (NetzwerkInterface.setWireless, Betriebssystem.wireless()), next
to the wireless cable. The reader did not read it, the writer did not write
it, and the Java oracle did not report it, so the Java-against-Java writer
test could not see the loss. Found while exporting the editor model: the
corpus has 18 wireless cards, one at each wireless cable.

The oracle now reports `wireless` per interface; FlsInterface has it; the
writer sets it. The synthetic topology has a wireless card; a mutation
probe that drops it is caught by Java.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-09-30 08:51:03 +00:00
co-authored by Claude Opus 5.5
parent 34d3788cd2
commit 968dbbc34a
10 changed files with 919 additions and 463 deletions
+6
View File
@@ -31,6 +31,8 @@ export interface FlsInterface {
mac: string | undefined;
gateway: string | null;
dns: string | null;
/** A notebook's or PC's card joining a switch's wireless network (Betriebssystem.wireless). */
wireless: boolean;
}
export interface FlsNode {
@@ -270,6 +272,7 @@ class Reader {
mac: undefined,
gateway: '',
dns: '',
wireless: false,
};
if (!isJavaObject(nic)) return result;
// Only the constructor's own interface gets the WAN address; one the file
@@ -302,6 +305,9 @@ class Reader {
if (typeof value === 'string' || value === null) result[field] = value;
else this.unexpected(at, property, value);
}
const wireless = nic.properties.get('wireless');
if (typeof wireless === 'boolean') result.wireless = wireless;
else if (wireless !== undefined) this.unexpected(at, 'wireless', wireless);
return result;
}
+1
View File
@@ -469,6 +469,7 @@ function interfaceStatements(nic: FlsInterface): BeanStatement[] {
if (nic.mask !== null) statements.push({ set: 'subnetzMaske', value: nic.mask });
if (nic.mac !== undefined) statements.push({ set: 'mac', value: nic.mac });
statements.push({ set: 'gateway', value: nic.gateway }, { set: 'dns', value: nic.dns });
if (nic.wireless) statements.push({ set: 'wireless', value: true });
return statements;
}
+2 -2
View File
@@ -526,7 +526,7 @@ function topology(fields: Partial<FlsTopology>): FlsTopology {
return { versionLine: null, nodes: [], links: [], documentation: [], warnings: [], ...fields };
}
const nic = (ip: string | null, mask: string | null = '255.255.255.0') => ({ ip, mask, mac: undefined, gateway: '', dns: '' });
const nic = (ip: string | null, mask: string | null = '255.255.255.0') => ({ ip, mask, mac: undefined, gateway: '', dns: '', wireless: false });
describe('importProject rules', () => {
it('labels a gateway wan0, lan0, gw2 and gives a rejected address the default', () => {
@@ -857,7 +857,7 @@ describe('importProject rules', () => {
});
const host = (dns: string, recursive: boolean) =>
flsNode('Rechner', {
interfaces: [{ ip: '10.0.0.1', mask: '255.255.255.0', mac: undefined, gateway: '', dns }],
interfaces: [{ ip: '10.0.0.1', mask: '255.255.255.0', mac: undefined, gateway: '', dns, wireless: false }],
system: { applications: [application('filius.software.dns.DNSServer', { isActive: true, port: 53, recursiveResolutionEnabled: recursive })] },
});
const project = importProject(
+2 -2
View File
@@ -292,8 +292,8 @@ describe('readTopology rules', () => {
</void></object>`)}</object>`);
expect(nodes[0]!.interfaces).toEqual([
{ ip: '10.0.0.1', mask: null, mac: undefined, gateway: '', dns: '' },
{ ip: '192.168.0.10', mask: '255.255.255.0', mac: '0A:0B:0C:0D:0E:0F', gateway: '', dns: '' },
{ ip: '10.0.0.1', mask: null, mac: undefined, gateway: '', dns: '', wireless: false },
{ ip: '192.168.0.10', mask: '255.255.255.0', mac: '0A:0B:0C:0D:0E:0F', gateway: '', dns: '', wireless: false },
]);
});
+5 -3
View File
@@ -169,7 +169,7 @@ const SYNTHETIC: TopologyToWrite = {
y: 20,
useIpAsName: false,
useMacAsName: true,
interfaces: [{ ip: '10.0.0.1', mask: '255.0.0.0', mac: 'AA:BB:CC:DD:EE:01', gateway: null, dns: '10.0.0.53' }],
interfaces: [{ ip: '10.0.0.1', mask: '255.0.0.0', mac: 'AA:BB:CC:DD:EE:01', gateway: null, dns: '10.0.0.53', wireless: true }],
portCount: 1,
system: {
ssid: 'Lab',
@@ -301,6 +301,7 @@ const SYNTHETIC: TopologyToWrite = {
mac: `AA:BB:CC:DD:EE:1${octet}`,
gateway: '',
dns: '',
wireless: false,
})),
portCount: 3,
system: {
@@ -346,6 +347,7 @@ const SYNTHETIC: TopologyToWrite = {
mac: `AA:BB:CC:DD:EE:1${octet}`,
gateway: '',
dns: '',
wireless: false,
})),
portCount: 2,
system: {
@@ -406,7 +408,7 @@ describe('a topology beyond the corpus, as Java loads it', () => {
expect(java?.sha256, 'written.java.json is not about this output: UPDATE_WRITTEN=1, then npm run oracle:java').toBe(
createHash('sha256').update(archive).digest('hex'),
);
const plainInterface = (octet: string) => ({ ip: `10.0.${octet}.1`, mask: '255.255.255.0', mac: `AA:BB:CC:DD:EE:1${octet}`, gateway: '', dns: '' });
const plainInterface = (octet: string) => ({ ip: `10.0.${octet}.1`, mask: '255.255.255.0', mac: `AA:BB:CC:DD:EE:1${octet}`, gateway: '', dns: '', wireless: false });
expect(topologyOf(java!)).toEqual({
success: true,
nodes: [
@@ -416,7 +418,7 @@ describe('a topology beyond the corpus, as Java loads it', () => {
position: [10, 20],
useIpAsName: false,
useMacAsName: true,
ports: [{ ip: '10.0.0.1', mask: '255.0.0.0', mac: 'AA:BB:CC:DD:EE:01', gateway: null, dns: '10.0.0.53' }],
ports: [{ ip: '10.0.0.1', mask: '255.0.0.0', mac: 'AA:BB:CC:DD:EE:01', gateway: null, dns: '10.0.0.53', wireless: true }],
system: {
ssid: 'Lab',
manualRoutes: [['10.1.0.0', '255.255.0.0', '10.0.0.254', '10.0.0.1']],
File diff suppressed because it is too large Load Diff