feat(core): the e-mail server after Java, measured against Java

SMTP on port 25 stores mails for the domain's accounts as Java reads
them anew and tells a local sender of unknown recipients; POP3 on port
110 hands them out. The mailboxes are in the snapshot and go back into
the project. EmailRun plays four stories over the network; MailCases
feeds Java's workers without one (26 cases, de and en). 18 cases name
Java bugs that the core does not take.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-01 18:58:26 +00:00
co-authored by Claude Opus 5.5
parent 8438b42018
commit 015635926f
26 changed files with 45188 additions and 14 deletions
+35
View File
@@ -801,6 +801,41 @@ describe('importProject rules', () => {
expect(reopened.firewallConfigurationsByNodeId.get(reopenedRouter)).toEqual(expected);
});
it("keeps a mail the e-mail server took during the run in the project saved after it (projectWithSimulationChanges)", () => {
const project = openProjectArchive(new Uint8Array(readFileSync(join(corpusRoot, 'java-examples/email_komplett.fls'))), counter());
const node = (name: string) => project.graph.nodes.find((each) => each.displayName === name)!.id;
const server = node('Mail-Server (filius.de)');
const sender = node('Rechner 1');
const engine = new NetworkRuntimeEngine(14);
const snapshot = projectSnapshot(project);
expect(snapshot.emailServersByNodeId.get(server)).toEqual({ domain: 'filius.de', accounts: project.emailServerConfigurations.get(server)!.accounts, isActive: true });
const switchedOff = { ...project, activePrograms: new Map([...project.activePrograms].map(([id, programs]) => [id, new Set([...programs].filter((each) => each !== 'emailServer'))])) };
expect(projectSnapshot(switchedOff).emailServersByNodeId.get(server)?.isActive).toBe(false);
engine.handle({
type: 'start',
snapshot: { ...snapshot, simpleClientsByNodeId: new Map([[sender, { targetAddress: '141.99.50.232', targetPort: 25 }]]) },
seed: 14,
initialTimeMilliseconds: 0,
});
const client = engine.simpleClient(sender)!;
client.connect('141.99.50.232', 25);
const commands = ['HELO 141.99.50.181', 'MAIL FROM:<rechner1@filius.de>', 'RCPT TO:<rechner2@filius.de>', 'DATA', 'From: <rechner1@filius.de>\nTo: <rechner2@filius.de>\nSubject: Hallo\n\nGespeichert?\n.\n', 'QUIT'];
for (const [index, command] of commands.entries()) {
engine.handle({ type: 'advance', toMilliseconds: 5_000 + index * 2_000 });
client.send(command);
}
engine.handle({ type: 'advance', toMilliseconds: 20_000 });
// The project itself is as it was opened, until the run's changes go back into it.
expect(project.emailServerConfigurations.get(server)!.accounts[1]!.mailbox).toEqual([]);
const saved = saveProjectArchive(projectWithSimulationChanges(project, engine.state.topologySnapshot)).archive;
const reopened = openProjectArchive(saved, counter());
const reopenedServer = reopened.graph.nodes.find((each) => each.displayName === 'Mail-Server (filius.de)')!.id;
const [first, second] = reopened.emailServerConfigurations.get(reopenedServer)!.accounts;
expect(first!.mailbox).toEqual([]);
expect(second!.mailbox).toMatchObject([{ from: { name: null, mailAddress: 'rechner1@filius.de' }, to: [{ name: null, mailAddress: 'rechner2@filius.de' }], subject: 'Hallo', body: 'Gespeichert?\n' }]);
});
it("keeps a node's IP forwarding where it differs from its kind's: a host that forwards, a router that does not", () => {
const system = (ipForwardingEnabled: boolean) => ({ manualRoutes: [], ipForwardingEnabled });
const project = importProject(