feat(core): the e-mail server keeps /mailserver/konten.txt up to date at run time, measured against Java
The account file's writer moves from the export (packages/fls) into the core, so that the export and the running server write the same. The server writes the file at its start and whenever its accounts change. EmailRun now records Java's file as it left it and as Java writes the accounts it ended with; Java leaves the notice to a local sender out until its next write. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ import { readFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { formatMail, withSnapshot, type LogLine, type NetworkRuntimeEngine } from '../src/index.js';
|
||||
import { EMAIL_SERVER_ACCOUNTS_FILE, formatMail, withSnapshot, type LogLine, type NetworkRuntimeEngine } from '../src/index.js';
|
||||
import { expectSameCaptures, GAP_TOLERANCE_MILLISECONDS, watchedEngine, withClientPortText, type JavaFrame, type JavaTables } from './javaRun.js';
|
||||
import { MAIL_ACCOUNTS } from './mailAccounts.js';
|
||||
import { clientText, emailServerText, isPolled, POLL_MILLISECONDS } from './programLines.js';
|
||||
@@ -32,6 +32,9 @@ interface JavaRun extends JavaTables {
|
||||
events: [number, Program, string][];
|
||||
/** Each account's name and its messages: Email.toString, new, marked for deletion. */
|
||||
accounts: [string, ...[string, string, string][]][];
|
||||
/** The account file as Java left it, and as Java writes the accounts it ended with (kontenSpeichern). */
|
||||
file: string;
|
||||
savedFile: string;
|
||||
}
|
||||
|
||||
type Act = (engine: NetworkRuntimeEngine) => void;
|
||||
@@ -155,6 +158,23 @@ describe('the e-mail server as Java FILIUS runs it', () => {
|
||||
...account.mailbox.map((entry) => [withoutDate(formatMail(entry.mail)), String(entry.isNew), String(entry.isMarkedForDeletion)]),
|
||||
]);
|
||||
expect(accounts).toEqual(java.accounts.map(([username, ...mails]) => [username, ...mails.map(([text, isNew, deleted]) => [withoutDate(text), isNew, deleted])]));
|
||||
|
||||
// The account file at the end: as Java writes those accounts (kontenSpeichern), the engine at every change.
|
||||
const file = snapshotFiles(engine).textFile(EMAIL_SERVER_ACCOUNTS_FILE);
|
||||
expect(fileWithoutDate(file), 'account file').toBe(fileWithoutDate(java.savedFile));
|
||||
expect(fileWithoutDate(java.file) === fileWithoutDate(java.savedFile), "Java's file as it left it is up to date").toBe(!STALE_FILE.has(name));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Where Java left its account file behind its accounts: smtpErrors, the
|
||||
* notice to the local sender (SMTPMitarbeiter saves the accounts before it
|
||||
* sends the notice); the engine writes the file at every change.
|
||||
*/
|
||||
const STALE_FILE = new Set(['smtpErrors']);
|
||||
|
||||
/** A notice's date in the account file, where its line breaks are "|". */
|
||||
const fileWithoutDate = (text: string) => text.replace(/\|Date: [^|]*\|/g, '|Date: <date>|');
|
||||
|
||||
const snapshotFiles = (engine: NetworkRuntimeEngine) => engine.state.topologySnapshot.fileSystemsByNodeId.get(SERVER)!;
|
||||
|
||||
@@ -11,6 +11,9 @@ import { fileURLToPath } from 'node:url';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
addressEntry,
|
||||
EMAIL_SERVER_ACCOUNTS_FILE,
|
||||
serverAccountsFileText,
|
||||
VirtualFileSystem,
|
||||
deviceConfiguration,
|
||||
NetworkRuntimeEngine,
|
||||
topologySnapshot,
|
||||
@@ -362,6 +365,15 @@ describe('the e-mail server in the engine', () => {
|
||||
expect(talk(engine, 110, 'QUIT')).toEqual(['+OK POP3 server ready', '+OK']);
|
||||
});
|
||||
|
||||
it('writes its account file from its accounts when it starts, over one that says otherwise', () => {
|
||||
const files = new VirtualFileSystem();
|
||||
files.createDirectory('/mailserver');
|
||||
files.writeTextFile(EMAIL_SERVER_ACCOUNTS_FILE, 'alt;filius.de;alt;Alt;Alt;\n');
|
||||
const engine = new NetworkRuntimeEngine(14);
|
||||
engine.handle({ type: 'start', snapshot: topologySnapshot({ ...mailNetwork(), fileSystemsByNodeId: new Map([[SERVER, files]]) }), seed: 14, initialTimeMilliseconds: 0 });
|
||||
expect(files.textFile(EMAIL_SERVER_ACCOUNTS_FILE)).toBe(serverAccountsFileText('filius.de', MAIL_ACCOUNTS));
|
||||
});
|
||||
|
||||
it('keeps its mailboxes in the snapshot: a mail taken, a mail deleted; a new start begins from there', () => {
|
||||
const engine = new NetworkRuntimeEngine(14);
|
||||
engine.handle({ type: 'start', snapshot: topologySnapshot({ ...mailNetwork(), language: 'en' }), seed: 14, initialTimeMilliseconds: 0 });
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user