Files
ihasmail-inbuxa/server/src/settingspolicy.test.ts
T
jcoffey-dev bb25355c23 AGPL source offer and name cleanup
Every build writes the exact source it was built from, uncommitted work and
new files included, as source.tar.gz next to the app, named after that tree.
Docker builds, which have no git, pack the build context and name it by a
hash of its files. The sign-in page and Settings > About link to it instead of
a repository that can drift.

What users, operators and packagers see no longer names the upstream server:
- interface text, in all nine catalogues, with a token-session line for
  Security;
- server messages;
- the settings, now MAIL_SERVER_URL, MAIL_SERVERS_FILE, ADMIN_URL and
  MAIL_SERVER_FOLLOW_ADVERTISED_URLS, and mail-servers.example.json;
- the Tenants notice, which is gone;
- the README, CONTRIBUTING and SECURITY.

ihasmail's own FEATURES, KNOWN-ISSUES and ROADMAP stay with public ihasmail,
and INBUXA.md is folded into the README.
2026-09-19 00:13:12 -07:00

89 lines
4.3 KiB
TypeScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { readFileSync } from "node:fs";
import { fileURLToPath } from "node:url";
/**
* The shipped example policy, checked against the rules the server enforces.
*
* An example that has drifted out of step with the parser is worse than no
* example: somebody copies it, the server refuses to start, and the first
* experience of the feature is a crash loop. This does not import the config
* module -- reading it has side effects and wants a whole environment -- so the
* rules it checks are restated here, and both are short enough that saying them
* twice is cheaper than the machinery to say them once.
*/
const EXAMPLE = fileURLToPath(new URL("../../settings-policy.example.json", import.meta.url));
test("the example policy is valid JSON", () => {
assert.doesNotThrow(() => JSON.parse(readFileSync(EXAMPLE, "utf8")));
});
test("the example policy has the three sections, in the shapes the server reads", () => {
const p = JSON.parse(readFileSync(EXAMPLE, "utf8")) as Record<string, unknown>;
for (const section of ["defaults", "enforced"]) {
const v = p[section];
assert.ok(v && typeof v === "object" && !Array.isArray(v), `${section} must be an object`);
}
assert.ok(Array.isArray(p.changes), "changes must be a list");
});
test("every change in the example has a unique version and settings", () => {
const p = JSON.parse(readFileSync(EXAMPLE, "utf8")) as { changes: Array<{ version?: unknown; settings?: unknown }> };
const seen = new Set<string>();
for (const [i, c] of p.changes.entries()) {
assert.equal(typeof c.version, "string", `changes[${i}] needs a string version`);
assert.ok((c.version as string).trim(), `changes[${i}] needs a non-empty version`);
assert.ok(!seen.has(c.version as string), `changes[${i}] repeats version ${String(c.version)}`);
seen.add(c.version as string);
assert.ok(c.settings && typeof c.settings === "object" && !Array.isArray(c.settings), `changes[${i}] needs a settings object`);
}
});
test("the example's commentary cannot be mistaken for a section", () => {
/*
* JSON has no comments, so the example explains itself in `_`-prefixed keys.
* The server reads three names and ignores everything else, which is what
* makes that safe -- but only for as long as no comment key collides with a
* real one.
*/
const p = JSON.parse(readFileSync(EXAMPLE, "utf8")) as Record<string, unknown>;
const real = new Set(["defaults", "enforced", "changes"]);
for (const key of Object.keys(p)) {
assert.ok(real.has(key) || key.startsWith("_"), `unexpected top-level key ${key}`);
}
});
/**
* The shipped server-mapping example, checked the same way and for the same
* reason: an example that no longer loads is worse than no example, because
* the first experience of the feature is a server that refuses to start.
*/
const SERVERS = fileURLToPath(new URL("../../mail-servers.example.json", import.meta.url));
test("the example server mapping is valid JSON", () => {
assert.doesNotThrow(() => JSON.parse(readFileSync(SERVERS, "utf8")));
});
test("every entry in the example mapping is a domain and an http(s) URL", () => {
const m = JSON.parse(readFileSync(SERVERS, "utf8")) as Record<string, unknown>;
const seen = new Set<string>();
for (const [key, value] of Object.entries(m)) {
if (key.startsWith("_")) continue;
const domain = key.trim().toLowerCase().replace(/\.$/, "");
assert.ok(domain, "a domain key is empty");
assert.ok(!seen.has(domain), `${domain} appears twice once normalized`);
seen.add(domain);
// A URL, or an object naming the server's URL and its administration's.
const entry = value && typeof value === "object" ? (value as Record<string, unknown>) : { url: value };
for (const [field, v] of Object.entries(entry)) {
assert.ok(field === "url" || field === "adminUrl", `${domain} has an unknown field ${field}`);
assert.equal(typeof v, "string", `${domain} ${field} is not a string`);
const url = new URL(v as string);
assert.ok(url.protocol === "http:" || url.protocol === "https:", `${domain} ${field} must be http or https`);
}
assert.equal(typeof entry.url, "string", `${domain} has no url`);
}
assert.ok(seen.size > 0, "the example should show at least one mapping");
});