Testing the part of the codebase that documents its own footgun
My affiliate-link package has a remark plugin that rewrites [text](affiliate:key) links to their real, tagged URLs at build time. Its README has a warning in bold: use the [plugin, options] tuple, not the pre-invoked form, because Astro and unified call the plugin function themselves with the options. Passing an already-invoked transformer means unified calls that with no arguments as if it were the attacher, which silently no-ops instead of rewriting anything. The build stays green with affiliate:key links left untouched in the output.
I wrote that warning myself, from experience. And until this week, the plugin the warning is about had zero test coverage. Two other modules in the same package had solid tests. The remark plugin, the one piece I’d already documented as having a silent-failure mode, had none.
Noticing the gap
It wasn’t a mystery bug this time, just an honest backlog scan: what in this codebase is undertested relative to how badly it can fail. The remark plugin stood out for a simple reason. I’d already written down what its worst failure mode looks like, in prose, in the README, months ago. A build that stays green while quietly shipping unrewritten affiliate:atomicHabits text straight into the rendered page. Not a crash, a silent no-op, the kind of bug a reader notices before I do because nothing in CI complains.
Documenting a failure mode and then never writing a test for it is a strange half-measure. The warning protects against someone doing the wrong thing by hand. It does nothing to catch a future refactor that breaks the same code in a related way.
What was actually easy to test
The plugin turned out to be a plain function once I looked past the “remark plugin” framing: it takes a small tree-shaped object and a file-like object with frontmatter.affiliates, walks the tree looking for links, rewrites their URLs, and throws if anything’s inconsistent. None of that needs a real Markdown parser or the unified pipeline running. A bare { type: 'link', url: 'affiliate:atomicHabits', children: [] } object exercises the same code path.
const link = { type: 'link', url: 'affiliate:atomicHabits', children: [] };
const tree = { type: 'root', children: [{ type: 'paragraph', children: [link] }] };
remarkAffiliate(config)(tree, fileWithAffiliates(['amazon']));
assert.equal(link.url, 'https://www.amazon.com/dp/B07RFSSYBH/ref=nosim?tag=example-20');
I nested the link inside a paragraph deliberately, not at the tree’s top level. The plugin recurses through node.children to find affiliate links wherever they are, and a flat top-level fixture would never exercise that recursion. It would have passed even if the recursive case were broken - the same trap as a test that proves nothing.
The four cases that mattered
The rewrite itself on a nested link. An unknown catalog key throwing, so a typo’d affiliate:atomicHabbits fails the build instead of shipping a broken link. A used program not declared in the post’s affiliates: frontmatter throwing, which is the actual compliance mechanism that makes an FTC disclosure impossible to forget. And a tree with no affiliate links at all doing nothing, so the plugin doesn’t accidentally require frontmatter on every post in a site.
Four short tests, no mocks, no fixture files, no unified() pipeline setup. Fifteen minutes of work for a piece of code I’d already flagged, in writing, as risky.
What I’d do differently
When I write a README warning about a specific failure mode, that’s the signal to write the test for it right then, not add a backlog item to revisit later. The warning is proof I already understand exactly what could go wrong, which means I already know exactly what the test should assert. Letting time pass between “I know this can fail this way” and “I wrote the test for that” just lets the knowledge decay before it gets used.
Related reading
The escaping bug that only shows up with a second query param
A rewrite function that worked in every test and every real URL it had ever seen, and would still have broken the moment someone added a second query param.
The 200KB decode nobody needed to redo
A one-line memoization fix, and the small test that actually proves memoization happened instead of just trusting the diff.
Proving a fallback actually falls back
A test that passes whether or not your override option works is not a test, it's a coin flip that happens to land the same way every time.