Note · Testing

The CI was green. The tests had never run.

~5 min read · NestJS · Jest · CI · August 2026
A glowing green 3D checkmark with a hidden red crack running through it

A green CI badge feels like proof. On one of my invoicing backends — a NestJS API with Postgres, JWT auth and Stripe billing — it was lying. A whole layer of tests had never actually executed, and when I forced them to run, they immediately failed and dragged a real production bug into the light: every Stripe webhook would have failed signature verification. The badge had been green the entire time.

What "green" was actually testing

A passing pipeline doesn't mean "the software works." It means "the steps that ran, passed." Those are very different claims, and the gap between them is where bugs live. On this repo the unit suite had quietly rotted, and the end-to-end suite — the one that stands up a real database and exercises real HTTP routes — had been silently skipped for its entire life, blocked behind an earlier step that crashed before it. Green wasn't a lie anyone told on purpose; it was a lie of omission that no one had audited.

Rot: tests that describe code that no longer exists

Tests are documentation that runs. When they rot, they document a version of the app that's gone. Ten suites were red once I looked, and the failures were a catalogue of drift: services had grown constructor dependencies the test modules never provided; endpoints had switched to 204 No Content while the tests still asserted a JSON body; a DTO field had been renamed and the fixtures hadn't; and my personal favourite — a mock of the config service that, through a copy-paste, called itself and blew the stack before a single assertion ran. None of that is a bug in the app. It's the tests describing an app that had moved on without them.

The layer that never ran

The end-to-end suite failed to even start. The CI step that runs database migrations invoked the ORM's plain command-line tool against a TypeScript data-source file — and that tool runs under Node, which can't parse TypeScript. It threw a SyntaxError and the job died right there, every time, before the E2E tests were reached. So they'd never run. Not once. Swap the CLI for the TypeScript-aware one and the migration step passes — and the E2E suite finally executes for the first time, and instantly falls over, because it had been dormant while the app evolved around it.

The bug hiding behind the green: Stripe webhooks

Here's the one that mattered. Stripe signs every webhook, and you verify that signature against the raw, unparsed request body — byte for byte. If any middleware has already parsed the body into JSON, the bytes you hash no longer match what Stripe hashed, and verification fails. NestJS only preserves the raw body if you create the app with rawBody: true — and this app didn't. In production, every Stripe webhook — payment succeeded, payment failed — would have failed verification and been rejected. Payments would go through on Stripe's side while my database never heard about it. A silent, revenue-affecting bug, sitting behind a green checkmark, because no test had ever hit that route with a real signed body.

Fix the code, then make the badge honest

  • Two real source bugs, fixed. The raw-body webhook flaw, and a second where an ISO date string was handled as a Date — it worked in most timezones and 500'd in others, the worst kind of intermittent.
  • The E2E layer, rebuilt to actually run — a real Postgres schema built from the entities, FK-safe cleanup between tests, serial execution, and the raw-body wiring. From "never executed" to a suite that genuinely exercises auth, invoicing and payments end to end.
  • The unit suite, un-rotted — brought back in line with the code it's supposed to describe, so it fails for real reasons again.
  • Green now means something. The pipeline is green because the tests that guard the risky paths ran and passed — not because they were quietly stepped over.

The takeaway

A passing badge is a floor, not a ceiling. It tells you the tests that ran passed; it says nothing about the tests that didn't. The most valuable thing I did on this project wasn't writing new tests — it was not trusting the green long enough to ask what it was actually checking. The bug that would have cost real money wasn't hiding in clever code. It was hiding in a gap the badge was never designed to see.

Read nextI built the same app three times, on purpose