feat(fls): export the editor model's programs
exportProject.ts, step X4a: each installed program of a PC or notebook as the FILIUS application of its class, keyed by that class name as Java keys them, with its settings: servers switched on as the model has them, a web server's port and virtual hosts, the DNS server's recursion on port 53, the echo server's port, the generic client's target (none as null, as its constructor leaves it), the peer count, the personal firewall. E-mail contents are the next step. The model lost a web server's virtual hosts switch (useVHost, on for 3 web servers in the corpus); Swift sets it off on import. The core now has WebServerConfiguration.virtualHosts (Swift's name), the import carries Java's value, and the Swift comparison names Swift's error. Checked by Java: every corpus file, imported, exported and written, loads with the programs of the original, with no new departure. In the synthetic model the PC has every program with settings away from the defaults, the notebook programs without settings (Java's defaults, 3 peers), where Java makes /peer2peer itself. 12 mutation probes, each loaded by Java, were all caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,7 @@ import {
|
||||
firewallRule,
|
||||
stableGeneratedMacAddress,
|
||||
type Id,
|
||||
type InstallableProgram,
|
||||
type TopologyNode,
|
||||
VirtualFileSystem,
|
||||
} from '@mdg-filius/core';
|
||||
@@ -68,9 +69,21 @@ const original = JSON.parse(readFileSync(join(corpusRoot, 'corpus.java.json'), '
|
||||
const exportedPath = join(here, 'exported.java.json');
|
||||
const exported = JSON.parse(readFileSync(exportedPath, 'utf8')) as Record<string, JavaFile>;
|
||||
|
||||
/** The system software without its applications, a later step; files in any order. */
|
||||
/**
|
||||
* The system software without what a later step exports — the e-mail
|
||||
* programs' domain, bean accounts, folders and address book; files in any
|
||||
* order.
|
||||
*/
|
||||
function systemOf(system: Record<string, unknown>) {
|
||||
const { applications: _applications, ...settings } = system;
|
||||
const settings =
|
||||
system.applications === undefined
|
||||
? system
|
||||
: {
|
||||
...system,
|
||||
applications: (system.applications as Record<string, unknown>[]).map(
|
||||
({ mailDomain: _domain, beanAccounts: _accounts, received: _received, sent: _sent, drafts: _drafts, addressBook: _book, ...rest }) => rest,
|
||||
),
|
||||
};
|
||||
return settings.fileSystem === undefined || settings.fileSystem === null
|
||||
? settings
|
||||
: { ...settings, fileSystem: sortedTree(settings.fileSystem as JavaTree) };
|
||||
@@ -315,6 +328,13 @@ function emptyProject(): ProjectToExport {
|
||||
portForwardingRowsByNodeId: new Map(),
|
||||
remoteLinkConfigurations: new Map(),
|
||||
virtualFileSystems: new Map(),
|
||||
installedPrograms: new Map(),
|
||||
activePrograms: new Map(),
|
||||
webServerConfigurations: new Map(),
|
||||
gnutellaConfigurations: new Map(),
|
||||
dnsServerConfigurations: new Map(),
|
||||
echoServerConfigurations: new Map(),
|
||||
simpleClientConfigurations: new Map(),
|
||||
documentationItems: [],
|
||||
};
|
||||
}
|
||||
@@ -341,6 +361,10 @@ function syntheticFiles(): VirtualFileSystem {
|
||||
files.writeImageFile('/foto.jpg', bytesOf(100), 'image/jpeg');
|
||||
files.writeBinaryFile('/daten.bin', bytesOf(58), 'application/zip');
|
||||
files.writeBinaryFile('/roh', bytesOf(3));
|
||||
// The web server's and the peer-to-peer application's own: Java makes none.
|
||||
files.createDirectory('/webserver');
|
||||
files.writeTextFile('/webserver/index.html', '<p>own</p>');
|
||||
files.createDirectory('/peer2peer');
|
||||
return files;
|
||||
}
|
||||
|
||||
@@ -400,6 +424,14 @@ const SYNTHETIC: ProjectToExport = {
|
||||
]),
|
||||
ripEnabledByNodeId: new Map([['rt', true]]),
|
||||
firewallConfigurationsByNodeId: new Map([
|
||||
[
|
||||
'pc',
|
||||
firewallConfiguration({
|
||||
isActive: false,
|
||||
defaultPolicy: 'accept',
|
||||
rules: [firewallRule({ sourceIPAddress: '10.0.0.0', sourceSubnetMask: '255.0.0.0', port: 22, action: 'accept' })],
|
||||
}),
|
||||
],
|
||||
[
|
||||
'rt',
|
||||
firewallConfiguration({
|
||||
@@ -437,6 +469,34 @@ const SYNTHETIC: ProjectToExport = {
|
||||
]),
|
||||
portForwardingRowsByNodeId: new Map([['gw', [{ protocolValue: 'TCP', publicPortValue: '8080', lanIPAddress: '192.168.1.10', lanPortValue: '80' }]]]),
|
||||
virtualFileSystems: new Map([['pc', syntheticFiles()]]),
|
||||
installedPrograms: new Map<Id, Set<InstallableProgram>>([
|
||||
[
|
||||
'pc',
|
||||
new Set<InstallableProgram>([
|
||||
'webServer',
|
||||
'dnsServer',
|
||||
'echoServer',
|
||||
'simpleClient',
|
||||
'gnutella',
|
||||
'firewall',
|
||||
'commandPrompt',
|
||||
'textEditor',
|
||||
'fileExplorer',
|
||||
'imageViewer',
|
||||
'webBrowser',
|
||||
]),
|
||||
],
|
||||
['nb', new Set<InstallableProgram>(['gnutella', 'simpleClient', 'firewall'])],
|
||||
]),
|
||||
activePrograms: new Map([['pc', new Set<InstallableProgram>(['webServer', 'dnsServer', 'echoServer'])]]),
|
||||
webServerConfigurations: new Map([['pc', { port: 8080, documentRoot: '/webserver', virtualHosts: true }]]),
|
||||
dnsServerConfigurations: new Map([['pc', { recursiveResolutionEnabled: true, records: [] }]]),
|
||||
echoServerConfigurations: new Map([['pc', { port: 7777 }]]),
|
||||
simpleClientConfigurations: new Map([
|
||||
['pc', { targetAddress: '10.0.0.9', targetPort: 7777 }],
|
||||
['nb', { targetAddress: '', targetPort: 55_555 }],
|
||||
]),
|
||||
gnutellaConfigurations: new Map([['pc', { maximumKnownPeers: 7 }]]),
|
||||
remoteLinkConfigurations: new Map([
|
||||
['m1', { pairIdentifier: 'java-modem-port-4242', latencyMilliseconds: 20, isEnabled: true }],
|
||||
['m2', { pairIdentifier: 'java-modem-port-4242', latencyMilliseconds: 20, isEnabled: true }],
|
||||
@@ -520,8 +580,33 @@ describe('a model beyond the corpus, as Java loads it', () => {
|
||||
javaFile('foto.jpg', 'jpg', javaBase64(bytesOf(100))),
|
||||
javaFile('index.HTML', 'html', '<p>upper case extension</p>'),
|
||||
javaFile('roh', '', javaBase64(bytesOf(3))),
|
||||
{ directory: 'webserver', children: [javaFile('index.html', 'html', '<p>own</p>')] },
|
||||
{ directory: 'peer2peer', children: [] },
|
||||
],
|
||||
}),
|
||||
// In the order of Java's key.
|
||||
applications: [
|
||||
{ key: 'filius.software.clientserver.ClientBaustein', class: 'filius.software.clientserver.ClientBaustein', zielIPAdresse: '10.0.0.9', zielPort: 7777 },
|
||||
{ key: 'filius.software.clientserver.ServerBaustein', class: 'filius.software.clientserver.ServerBaustein', aktiv: true, port: 7777 },
|
||||
{ key: 'filius.software.dateiaustausch.PeerToPeerAnwendung', class: 'filius.software.dateiaustausch.PeerToPeerAnwendung', maxTeilnehmerZahl: 7 },
|
||||
{ key: 'filius.software.dns.DNSServer', class: 'filius.software.dns.DNSServer', aktiv: true, port: 53, recursiveResolutionEnabled: true },
|
||||
{
|
||||
key: 'filius.software.firewall.Firewall',
|
||||
class: 'filius.software.firewall.Firewall',
|
||||
activated: false,
|
||||
defaultPolicy: 1,
|
||||
dropICMP: false,
|
||||
filterSYNSegmentsOnly: true,
|
||||
filterUdp: true,
|
||||
rules: [['10.0.0.0', '255.0.0.0', '', '', 22, 6, 1]],
|
||||
},
|
||||
{ key: 'filius.software.lokal.FileExplorer', class: 'filius.software.lokal.FileExplorer' },
|
||||
{ key: 'filius.software.lokal.ImageViewer', class: 'filius.software.lokal.ImageViewer' },
|
||||
{ key: 'filius.software.lokal.Terminal', class: 'filius.software.lokal.Terminal' },
|
||||
{ key: 'filius.software.lokal.TextEditor', class: 'filius.software.lokal.TextEditor' },
|
||||
{ key: 'filius.software.www.WebBrowser', class: 'filius.software.www.WebBrowser' },
|
||||
{ key: 'filius.software.www.WebServer', class: 'filius.software.www.WebServer', aktiv: true, port: 8080, useVHost: true },
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -540,7 +625,29 @@ describe('a model beyond the corpus, as Java loads it', () => {
|
||||
useIpAsName: false,
|
||||
useMacAsName: true,
|
||||
ports: [card('10.0.0.2', '255.255.255.0', stableGeneratedMacAddress('n0'), '', '', true)],
|
||||
system: { ssid: 'net', manualRoutes: [], dhcpClient: false, dhcpServer: JAVA_DHCP_DEFAULTS, fileSystem: { directory: 'root', children: [] } },
|
||||
system: {
|
||||
ssid: 'net',
|
||||
manualRoutes: [],
|
||||
dhcpClient: false,
|
||||
dhcpServer: JAVA_DHCP_DEFAULTS,
|
||||
// Made by the peer-to-peer application.
|
||||
fileSystem: { directory: 'root', children: [{ directory: 'peer2peer', children: [] }] },
|
||||
applications: [
|
||||
// Without a setting: no target (null), the model's 3 peers, Java's personal firewall.
|
||||
{ key: 'filius.software.clientserver.ClientBaustein', class: 'filius.software.clientserver.ClientBaustein', zielIPAdresse: null, zielPort: 55_555 },
|
||||
{ key: 'filius.software.dateiaustausch.PeerToPeerAnwendung', class: 'filius.software.dateiaustausch.PeerToPeerAnwendung', maxTeilnehmerZahl: 3 },
|
||||
{
|
||||
key: 'filius.software.firewall.Firewall',
|
||||
class: 'filius.software.firewall.Firewall',
|
||||
activated: true,
|
||||
defaultPolicy: 0,
|
||||
dropICMP: false,
|
||||
filterSYNSegmentsOnly: true,
|
||||
filterUdp: true,
|
||||
rules: [],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
class: 'Vermittlungsrechner',
|
||||
|
||||
+1723
-119
File diff suppressed because it is too large
Load Diff
@@ -140,7 +140,7 @@ function systemOf(project: ImportedProject, nodeId: Id) {
|
||||
const web = project.webServerConfigurations.get(nodeId);
|
||||
if (web !== undefined) {
|
||||
// Swift serves the same files from /www after moving them there.
|
||||
result.webServer = { port: web.port, documentRoot: web.documentRoot === '/webserver' ? '/www' : web.documentRoot, virtualHosts: false };
|
||||
result.webServer = { port: web.port, documentRoot: web.documentRoot === '/webserver' ? '/www' : web.documentRoot, virtualHosts: web.virtualHosts };
|
||||
}
|
||||
const gnutella = project.gnutellaConfigurations.get(nodeId);
|
||||
if (gnutella !== undefined) result.gnutella = gnutella;
|
||||
@@ -330,7 +330,15 @@ const unnumberedMailbox = (numbered: string[]) => (node: SwiftNode) => {
|
||||
},
|
||||
};
|
||||
};
|
||||
/** Swift reads a web server's useVHost as off. */
|
||||
const servesVirtualHosts = (node: SwiftNode) => ({
|
||||
...node,
|
||||
system: { ...node.system, webServer: { ...(node.system.webServer as object), virtualHosts: true } },
|
||||
});
|
||||
const SWIFT_ERRORS: Record<string, Record<number, (node: SwiftNode) => SwiftNode>> = {
|
||||
'ipad-fixtures/Public_and_Private_Networks_2_EN.fls': { 2: servesVirtualHosts },
|
||||
'java-examples/Public_and_Private_Networks_2_EN.fls': { 2: servesVirtualHosts },
|
||||
'java-examples/private_und_public_networks_mit_provider_DE.fls': { 6: servesVirtualHosts },
|
||||
'java-examples/man-in-the-middle.fls': { 5: unnumberedMailbox(['1', '2']) },
|
||||
'java-examples/multicast_server.fls': {
|
||||
// useIPAsName is `<object idref="Boolean0"/>`, the shared result of a
|
||||
@@ -894,7 +902,7 @@ describe('importProject rules', () => {
|
||||
|
||||
expect([...project.installedPrograms.get(notebook!)!].sort()).toEqual(['commandPrompt', 'echoServer', 'firewall', 'simpleClient', 'webServer']);
|
||||
expect([...project.activePrograms.get(notebook!)!]).toEqual(['webServer']);
|
||||
expect(project.webServerConfigurations.get(notebook!)).toEqual({ port: 8080, documentRoot: '/webserver' });
|
||||
expect(project.webServerConfigurations.get(notebook!)).toEqual({ port: 8080, documentRoot: '/webserver', virtualHosts: false });
|
||||
expect(project.echoServerConfigurations.get(notebook!)).toEqual({ port: 7 });
|
||||
expect(project.simpleClientConfigurations.get(notebook!)).toEqual({ targetAddress: '10.0.0.9', targetPort: 7 });
|
||||
expect(project.firewallConfigurationsByNodeId.get(notebook!)).toMatchObject({ isActive: true, defaultPolicy: 'accept' });
|
||||
|
||||
Reference in New Issue
Block a user