89 lines
4.3 KiB
TypeScript
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("../../stalwart-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");
|
|
});
|