server/src/mock/index.ts was 1,545 lines, the largest file in the repo.
It had carried `/* ---------- data ---------- */` style markers for a
while, so the seams were already drawn; this turns six of them into
files.
mock/config.ts 51 env-derived constants, `account`, `state`
mock/data.ts 410 fixtures and the builders that make them
mock/engine.ts 442 the generic JMAP machinery -- get/set, filters,
patches, refs, limits, recurrence plumbing
mock/handlers.ts 455 the `Method/name` dispatch table
mock/events.ts 26 SSE fan-out and the Email/changes ring buffer
mock/auth.ts 11 checkOtp
mock/index.ts 195 HTTP routing, the session document, listen
TWO THINGS THAT COULD NOT JUST MOVE:
`counter` and `vacation` were module-level `let`s written from both the
fixture builders and the handlers. An ES module can export a `let` and
importers see it update, but they cannot assign to it, so both became
containers: `seq.counter` and `vacationBox.current`. Seven call sites.
`recordEmailChange`, `broadcast`, `sseClients` and `checkOtp` lived in
the HTTP section, but the handlers call them -- and index.ts imports the
handlers. Leaving them there is a cycle, so they became events.ts and
auth.ts rather than being dragged into data.ts, which is fixtures.
`account` is still exported from index.ts, because account.test.ts and
login-guard.test.ts reach for `mock.account` and `mock.server`.
Verified by running it, not only by compiling it: `npm run mock` boots
and listens, `/.well-known/jmap` returns a session, and a POST to
`/jmap/` answers Mailbox/get with the nine seeded folders and
Email/query with the seeded messages.
52 lines
2.7 KiB
TypeScript
52 lines
2.7 KiB
TypeScript
import { readFileSync } from "node:fs";
|
|
|
|
|
|
export const PERMISSION_SNAPSHOT = (JSON.parse(readFileSync(new URL("../../../web/src/locales/permissions/source.json", import.meta.url), "utf8")) as { permissions: Array<{ name: string; label: string }> }).permissions;
|
|
|
|
export const PORT = Number(process.env.MOCK_PORT ?? 8788);
|
|
/**
|
|
* Omit `urn:stalwart:jmap` from the session, so a sign-in can be tested
|
|
* against a server ihasmail does not support. This is only that: the rest of
|
|
* the mock still behaves like 0.16. Emulating 0.15 properly went with the
|
|
* support for it.
|
|
*/
|
|
export const NO_REGISTRY = process.env.MOCK_NO_REGISTRY === "1";
|
|
/**
|
|
* Stalwart advertises FUTURERELEASE in the session but only honors it when
|
|
* the MTA's own `futureRelease` setting is on -- and that setting defaults to
|
|
* off, in which case the hold is dropped without a word and the message goes
|
|
* out at once. Set MOCK_NO_FUTURE_RELEASE=1 to reproduce that trap.
|
|
*/
|
|
export const NO_FUTURE_RELEASE = process.env.MOCK_NO_FUTURE_RELEASE === "1";
|
|
/** What the session advertises, matching Stalwart's own 30 days. */
|
|
export const MAX_DELAYED_SEND = 86400 * 30;
|
|
export const ACCOUNT = "a1";
|
|
/** How long a push subscription lives before the server drops it. */
|
|
export const PUSH_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
/** An account somebody has shared with the demo user. See the session below. */
|
|
export const SHARED_ACCOUNT = "a2";
|
|
export const SHARED_CAPS: Obj = {
|
|
"urn:ietf:params:jmap:mail": {}, "urn:ietf:params:jmap:submission": {}, "urn:ietf:params:jmap:vacationresponse": {},
|
|
"urn:ietf:params:jmap:sieve": {}, "urn:ietf:params:jmap:calendars": {}, "urn:ietf:params:jmap:contacts": {},
|
|
"urn:ietf:params:jmap:principals": {}, "urn:ietf:params:jmap:quota": {}, "urn:ietf:params:jmap:filenode": {},
|
|
};
|
|
export const USER = process.env.MOCK_USER ?? "[email protected]";
|
|
/** Locale the fake directory reports for the account (POSIX style, as Stalwart does). */
|
|
export const MOCK_LOCALE = process.env.MOCK_LOCALE ?? "en_US";
|
|
/** What /api/account reports. Tenants are managed only on "enterprise"; MOCK_EDITION=enterprise to develop them. */
|
|
export const MOCK_EDITION = process.env.MOCK_EDITION ?? "oss";
|
|
export const PASS = process.env.MOCK_PASS ?? "demo";
|
|
/**
|
|
* Credential state, mutable so the self-service flows can be exercised against
|
|
* the mock the way they run against a real 0.16 server: the password changes,
|
|
* 2FA starts demanding a code on every request, and app passwords keep working
|
|
* without one.
|
|
*/
|
|
export const account = { password: PASS, otpUrl: null as string | null, appPasswords: [] as Obj[] };
|
|
export const MASKED = "[********]";
|
|
|
|
export type Obj = Record<string, unknown>;
|
|
export const state = { n: 1 };
|
|
export const nextState = () => String(state.n++);
|
|
|