Code coverage and the lies it tells
100% coverage means every line ran during the suite. It says nothing about whether the right values were checked. A real bug that coverage missed.

A repo with a shiny "100% coverage" badge shipped a bug where a single query parameter returned every row in a table instead of one. The function responsible for stopping that had 100% coverage the entire time. Coverage told the truth about what it measures and nothing about what people assumed it measured, which is the whole problem with the number.
What coverage actually counts
Run vitest --coverage and you get a report showing which lines, branches, and functions executed at least once while the suite ran. That's it. It's an execution tracker, not a correctness checker. It can't tell you whether the assertion on line 40 checked the right thing, or whether it checked anything meaningful at all.
That one call is enough for a coverage tool to mark every line of clamp as executed. It never sends a negative number, never sends something above the max, never sends a non-number. A tool measuring "did this line run" has no opinion on any of that. It ran. Done. Green.
The real bug: a negative limit in Linkstash
This is exactly what happened with intParam in Linkstash's pagination:
/** Query-string numbers are strings from an untrusted client, so every one is
* parsed, rejected if it is not a whole number, and clamped into range. A
* negative LIMIT is the dangerous case: SQLite reads it as "no limit", which
* turns a pagination bug into an unbounded result set. */
function intParam(raw: unknown, fallback: number, min: number, max: number): number {
const n = Number(raw);
if (!Number.isInteger(n)) return fallback;
return Math.min(Math.max(n, min), max);
}Before the clamp existed, a call like GET /links?limit=-5 would pass -5 straight through to a SQL LIMIT -5. In most databases that's an error. In SQLite, a negative LIMIT means no limit at all, silently. What looks like "give me at most negative five rows," a request that should obviously fail or return nothing, instead returns every row in the table. A single test calling the endpoint with any positive number, the kind of test someone writes first without thinking twice, gives intParam full line and branch coverage while never once exercising the exact input that broke production.
The test that actually catches it looks like this:
// The dangerous case: a negative limit must not become "no limit" in SQLite.
const neg = await request(app).get("/links?limit=-5").set("Authorization", `Bearer ${token}`);
expect(neg.status).toBe(200);
expect(neg.body.limit).toBe(1);
expect(neg.body.data).toHaveLength(1);That's not a coverage number going up. It's a specific, adversarial input, chosen because someone thought about what a client could send and what SQLite specifically does with it. Coverage would never have pointed anyone at -5 as the value worth trying. A person had to know the database's actual behavior.
What coverage is good for
None of this means the number is worthless. A coverage report is genuinely useful for one specific thing: finding code nobody's tests ever touch at all. A function sitting at 0% coverage is either dead code that should be deleted, or a real gap in the suite that deserves a look. That's a legitimate signal. What it can't do is confirm the code that is covered is correct, which is why "100% coverage" is a floor worth having and a ceiling not worth trusting.
A stronger check exists, if you need it
Mutation testing tools (Stryker is the common one in the JS ecosystem) deliberately introduce small bugs into your code, like flipping < to <=, and check whether any test fails. A test suite that stays green after the mutation wasn't really testing that line, no matter what the coverage report said. It's slower to run than a coverage report, so most teams reach for it only on the code that matters most.
Quick check
A function reports 100% line coverage. What does that actually guarantee?
The lesson underneath this one isn't new: it's the same argument from edge cases and the bugs you would have shipped, just measured. A number that goes up when you test the happy path is a number that can hide the one input that mattered.
Previous: test databases, fixtures and isolation. Next: testing React components.

Written by
Rhythm Bhiwani
Engineer and relentless builder, happiest reverse-engineering hard problems until they click.
Enjoyed this?
Tap the heart to leave some love.
Be the first to react
Comments
Join the conversation.
Loading comments…


