From b9e4615b462543b5f5215bff56c4e8af3685e106 Mon Sep 17 00:00:00 2001 From: anthony Date: Fri, 31 Jul 2026 19:53:01 +0000 Subject: [PATCH] test: catch the vendored DNS bridge drifting from the published one MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit src/dns.mjs is a copy of @moshcoder/moshpit-dns, and it stays a copy on purpose: moshcode ships as a tarball that nothing runs `npm install` over, so a runtime dependency would break every install. The cost of that choice is drift, and this makes drift loud instead of silent. Behaviour, not bytes. The two differ cosmetically — the standalone tool names itself in the config comments it writes — so a byte comparison would fail on that forever while missing a real divergence in what the protocol does. This runs both over the same inputs and requires the same answers: encoded names, decoded names including the rejections, parsed queries, and responses compared byte-for-byte on the wire, which is the one that matters most. A resolver that disagrees about bytes answers differently depending on which copy ran. A dev dependency, so the shipped tarball is unaffected and only CI pays for it. Skips cleanly when the package is not installed. Verified it can fail: changing DEFAULT_TTL in the vendored copy alone turns the suite red. A drift test that cannot catch drift is worse than none, because it reads like assurance. 485 pass, 120 skipped. Co-Authored-By: Claude Opus 5 (1M context) --- package.json | 5 +- pnpm-lock.yaml | 17 +++++- test/dns-drift.test.mjs | 120 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 2 deletions(-) create mode 100644 test/dns-drift.test.mjs diff --git a/package.json b/package.json index e37be02..034594c 100644 --- a/package.json +++ b/package.json @@ -21,5 +21,8 @@ "README.md" ], "license": "MIT", - "packageManager": "pnpm@10.32.1" + "packageManager": "pnpm@10.32.1", + "devDependencies": { + "@moshcoder/moshpit-dns": "^0.2.1" + } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 9b60ae1..871554a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -6,4 +6,19 @@ settings: importers: - .: {} + .: + devDependencies: + '@moshcoder/moshpit-dns': + specifier: ^0.2.1 + version: 0.2.1 + +packages: + + '@moshcoder/moshpit-dns@0.2.1': + resolution: {integrity: sha512-GwTWmI31aNz58ddIgF+3BnOWPsjL+Lgg5NyBpRbDgYjYA6Y+94LVzGIJeMp/jFvjo/0EOTWAQfHldEijwcoMUA==} + engines: {node: '>=20'} + hasBin: true + +snapshots: + + '@moshcoder/moshpit-dns@0.2.1': {} diff --git a/test/dns-drift.test.mjs b/test/dns-drift.test.mjs new file mode 100644 index 0000000..9664f3f --- /dev/null +++ b/test/dns-drift.test.mjs @@ -0,0 +1,120 @@ +// The vendored DNS bridge, against the published one. +// +// src/dns.mjs is a copy of @moshcoder/moshpit-dns. It is a copy on purpose: +// moshcode ships as a tarball that nothing runs `npm install` over, so a +// runtime dependency would break every install. The cost of that choice is +// drift, and the point of this file is to make drift loud instead of silent. +// +// Behaviour rather than bytes. The two differ cosmetically — the standalone +// tool names itself in the config comments it writes — and a byte comparison +// would fail on that forever while missing a real divergence in what the +// protocol actually does. So this runs both over the same inputs and requires +// the same answers. +// +// Skips when the package is not installed, so a checkout without dev +// dependencies still runs the rest of the suite. +import assert from "node:assert/strict"; +import test from "node:test"; +import { createRequire } from "node:module"; + +import * as vendored from "../src/dns.mjs"; + +const require = createRequire(import.meta.url); +let published = null; +try { + published = await import("@moshcoder/moshpit-dns"); +} catch { + published = null; +} + +test("vendored DNS bridge matches the published package", { + skip: published ? false : "@moshcoder/moshpit-dns not installed", +}, async (t) => { + await t.test("the wire codec encodes names identically", () => { + for (const name of ["blue.eggs", "a.b", "x".repeat(63) + ".eggs", "california.oranges"]) { + assert.deepEqual( + [...vendored.encodeName(name)], + [...published.encodeName(name)], + name, + ); + } + }); + + await t.test("names decode identically, including the failures", () => { + for (const name of ["blue.eggs", "california.oranges"]) { + const buf = vendored.encodeName(name); + assert.deepEqual(vendored.decodeName(buf, 0), published.decodeName(buf, 0), name); + } + // A compression pointer in a question is rejected by both, or neither is + // safe to put on a socket. + const pointer = Buffer.from([0xc0, 0x0c]); + assert.throws(() => vendored.decodeName(pointer, 0)); + assert.throws(() => published.decodeName(pointer, 0)); + }); + + await t.test("a query parses to the same question", () => { + const question = Buffer.concat([ + Buffer.from([0x12, 0x34, 0x01, 0x00, 0, 1, 0, 0, 0, 0, 0, 0]), + vendored.encodeName("blue.eggs"), + Buffer.from([0, 1, 0, 1]), + ]); + assert.deepEqual(vendored.parseQuery(question), published.parseQuery(question)); + }); + + await t.test("responses are byte-identical on the wire", () => { + const question = Buffer.concat([ + Buffer.from([0x12, 0x34, 0x01, 0x00, 0, 1, 0, 0, 0, 0, 0, 0]), + vendored.encodeName("blue.eggs"), + Buffer.from([0, 1, 0, 1]), + ]); + const q = vendored.parseQuery(question); + // This is the one that matters most: a resolver that disagrees about bytes + // is a resolver that answers differently depending on which copy ran. + assert.deepEqual( + [...vendored.buildResponse(q, question, "203.0.113.7", 30)], + [...published.buildResponse(q, question, "203.0.113.7", 30)], + ); + }); + + await t.test("the same hostnames are Moshpit names", () => { + for (const h of ["blue.eggs", "a.b.c", "1.2.3.4", "localhost", "", "eggs", "blue.420", "1.420"]) { + assert.deepEqual(vendored.parseRegistryName(h), published.parseRegistryName(h), h); + } + }); + + await t.test("resolution decides the same way on the same registry answer", async () => { + const cases = [ + { registered: true, name_registered: true, target: "203.0.113.9" }, + { registered: true, name_registered: true, target: null }, + { registered: true, name_registered: false, target: null }, + { registered: false }, + ]; + for (const body of cases) { + const fetchImpl = async () => ({ ok: true, status: 200, json: async () => body }); + assert.deepEqual( + await vendored.resolveName("blue.eggs", { fetchImpl }), + await published.resolveName("blue.eggs", { fetchImpl }), + JSON.stringify(body), + ); + } + }); + + await t.test("an unreachable registry fails the same way in both", async () => { + const fetchImpl = async () => { throw new Error("offline"); }; + assert.deepEqual( + await vendored.resolveName("blue.eggs", { fetchImpl }), + await published.resolveName("blue.eggs", { fetchImpl }), + ); + }); + + await t.test("the defaults have not drifted apart", () => { + for (const key of ["DEFAULT_REGISTRY_BASE", "DEFAULT_PORT", "DEFAULT_HOST", "DEFAULT_TTL"]) { + assert.equal(vendored[key], published[key], key); + } + }); + + await t.test("the published version is recorded, so a bump is a visible change", () => { + const { version } = require("@moshcoder/moshpit-dns/package.json"); + assert.match(version, /^\d+\.\d+\.\d+/); + }); +});