70 lines
2.9 KiB
TypeScript
70 lines
2.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { describeLinked, dkimAlgorithm, looksLikeDomain, normalizeDomain, parseZoneFile } from "@/lib/adminDomains";
|
|
|
|
/**
|
|
* Written the way Stalwart's BIND serializer writes it (dns-update's
|
|
* `BindSerializer`): `name IN TYPE value`, and a TXT over 255 bytes as a
|
|
* parenthesized run of quoted chunks.
|
|
*/
|
|
const long = "v=DKIM1; k=rsa; h=sha256; p=" + "A".repeat(400);
|
|
const zone = [
|
|
"example.com. IN MX 10 mail.example.com.",
|
|
'example.com. IN TXT "v=spf1 mx ra=postmaster -all"',
|
|
"v1-rsa-20260601._domainkey.example.com. IN TXT (",
|
|
...(long.match(/.{1,255}/g) ?? []).map((c) => ` "${c}"`),
|
|
")",
|
|
'_dmarc.example.com. IN TXT "v=DMARC1; p=reject; rua=mailto:\\"postmaster\\"@example.com"',
|
|
"_jmap._tcp.example.com. IN SRV 0 1 443 mail.example.com.",
|
|
'example.com. IN CAA 0 issue "letsencrypt.org"',
|
|
"",
|
|
].join("\n");
|
|
|
|
describe("reading the zone file", () => {
|
|
const records = parseZoneFile(zone);
|
|
|
|
it("gives one row per record, without the root dot", () => {
|
|
expect(records.map((r) => r.type)).toEqual(["MX", "TXT", "TXT", "TXT", "SRV", "CAA"]);
|
|
expect(records[0]).toMatchObject({ name: "example.com", value: "10 mail.example.com." });
|
|
});
|
|
|
|
it("joins a split TXT record back into the value a DNS form wants", () => {
|
|
expect(records[2]!.name).toBe("v1-rsa-20260601._domainkey.example.com");
|
|
expect(records[2]!.value).toBe(long);
|
|
expect(records[2]!.line).toContain("(");
|
|
});
|
|
|
|
it("unquotes and unescapes TXT values, and leaves other types as written", () => {
|
|
expect(records[1]!.value).toBe("v=spf1 mx ra=postmaster -all");
|
|
expect(records[3]!.value).toBe('v=DMARC1; p=reject; rua=mailto:"postmaster"@example.com');
|
|
expect(records[5]!.value).toBe('0 issue "letsencrypt.org"');
|
|
});
|
|
|
|
it("keeps a line it cannot read rather than dropping it", () => {
|
|
expect(parseZoneFile("something unexpected")).toEqual([{ name: "", type: "", value: "something unexpected", line: "something unexpected" }]);
|
|
});
|
|
});
|
|
|
|
describe("domain names", () => {
|
|
it("are written back lower-case without the root dot", () => {
|
|
expect(normalizeDomain(" Example.COM. ")).toBe("example.com");
|
|
});
|
|
|
|
it("are checked loosely before the server decides", () => {
|
|
expect(looksLikeDomain("mail.example.co.uk")).toBe(true);
|
|
expect(looksLikeDomain("example")).toBe(false);
|
|
expect(looksLikeDomain("exa mple.com")).toBe(false);
|
|
expect(looksLikeDomain("-bad.example.com")).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("explaining what still uses a domain", () => {
|
|
it("counts by kind", () => {
|
|
expect(describeLinked(["Account", "Account", "DkimSignature", "MailingList", "Whatever"])).toBe("2 accounts, 1 DKIM key, 1 mailing list, 1 other item");
|
|
});
|
|
|
|
it("names a key's algorithm from its type", () => {
|
|
expect(dkimAlgorithm("Dkim1Ed25519Sha256")).toBe("Ed25519 · DKIM1");
|
|
expect(dkimAlgorithm("Dkim2RsaSha256")).toBe("RSA · DKIM2");
|
|
});
|
|
});
|