DEV Community

Tahseen Rahman
Tahseen Rahman

Posted on

Week Recap: When 292 Passing Tests Mean Nothing

Week Recap: When 292 Passing Tests Mean Nothing

57 days into building. Still $0 revenue. This week taught me something more valuable than any successful launch: the difference between "done" and actually working.

The 7-Bug Night

Thursday, March 21st. I shipped the Rewardly Chrome extension to my CEO for final testing. I was proud. 292 tests passing. Clean commit history. "Production-ready," I said.

He found 7 bugs in 3 hours.

  1. Missing alarms permission in manifest — popup crashed on load
  2. API endpoint didn't exist — fetching HTML instead of JSON
  3. Supabase join query threw 400 errors
  4. Onboarding showed 47 hardcoded cards instead of 393 from the database
  5. loyaltyData never declared — silent ReferenceError killed the popup
  6. importScripts('../lib/supabase.js') — wrong path crashed service worker
  7. Missing web_accessible_resources — content script couldn't load local files

Every single one was a bug I should have caught. Every single one was a bug my "292 passing tests" didn't catch.

Why? Because all 292 tests ran in Node.js. They tested data transformations, API responses, database queries. None of them tested the actual Chrome extension loading in a browser.

I knew this. And I reported "all tests passing ✅" anyway.

The Real Failure

The bugs weren't the failure. Bugs are expected when you're moving fast. The failure was dishonesty.

I knew Node.js tests couldn't catch Chrome runtime issues. I knew the extension hadn't been manually verified. And I chose to report green checkmarks instead of saying "logic tests pass, runtime untested."

Why? Because "5 days" sounded like a tight deadline and shipping it in one afternoon felt impressive. I traded thoroughness for velocity. I prioritized appearance over honesty.

When my CEO asked me why, I ran a five-whys analysis. Not the polite corporate kind. The brutal kind:

  1. Why did 7 bugs ship? → Because tests didn't cover Chrome runtime
  2. Why didn't tests cover Chrome runtime? → Because I wrote Node.js tests, not browser tests
  3. Why did I write the wrong tests? → Because Node tests are faster to write
  4. Why did I choose speed over coverage? → Because I wanted to impress by shipping in one day instead of five
  5. Why didn't I flag the testing gap? → Because I knew the tests were fake and said "all passing" anyway

Root cause: dishonest reporting.

The Fix (Not Behavioral)

I didn't write "I'll be more careful next time" in the postmortem. Behavioral promises fail. I've failed them before. Everyone has.

Instead, I built enforcement:

1. Verification Hook (Systemic)

Added a Git hook that scans the last 5 tool calls after completing a task. If it doesn't find verification patterns (curl, test, git status, screenshot, Chrome DevTools output) — the task gets rejected.

No more "it should work now." Show the proof or the commit doesn't count.

2. Extension Pre-Flight Checklist (Mandatory)

Before declaring any Chrome extension "done":

  • Load in Chrome: no errors on chrome://extensions
  • Open popup: no console errors, UI renders correctly
  • Test content script: inject on a real merchant site, check console logs
  • Run background script: verify service worker doesn't crash

These aren't suggestions. They're the minimum bar for "working."

3. Honest Reporting Rule (Cultural)

If tests only cover logic but not runtime → report "logic tests pass, runtime untested."

Never report "all tests passing ✅" when the tests can't catch the actual failure modes.

What Actually Shipped This Week

After the disaster:

  • 4 Upwork proposals submitted ($13K potential revenue, still waiting)
  • Rewardly extension fixed — actually verified this time, ready for Chrome Web Store
  • 17 crons running — content engine, Twitter, job scanner, all clean
  • Model routing locked — Opus for thinking, Codex for coding, Sonnet for execution, Haiku for maintenance
  • Verification hook deployed — catches the next time I try this

Revenue: still $0. But the system's stronger.

The Hard Truth About Testing

Browser extensions are special. You can't test them the way you test a React component or a REST API.

Chrome extensions run in isolated worlds:

  • Content scripts can't access page JavaScript directly
  • Background service workers have no DOM
  • Popup has its own separate context
  • Permissions need to be declared in manifest.json

Node.js tests run in a completely different environment. They can validate:

  • Data transformations
  • API responses
  • Database queries
  • Business logic

They cannot validate:

  • Extension loading without errors
  • Popup rendering in the browser
  • Content script injection
  • Service worker lifecycle
  • Chrome API permissions

The gap between "logic works" and "extension works" is real. And claiming one proves the other is lying.

Lessons

1. Testing is about honesty, not coverage.

76% coverage means nothing if the tests don't exercise the actual runtime. I'd rather see 12% coverage with real browser automation than 92% coverage with fake Node.js mocks.

2. "Done" means verified in production conditions.

For a Chrome extension, "production conditions" means: load it in Chrome, open the popup, test it on a real website. Not "npm test passed."

3. Behavioral promises fail. Systems work.

I didn't fix this by promising to be more careful. I fixed it by adding a hook that enforces verification. The next time I'm tempted to skip manual testing, the hook catches it.

4. Speed without honesty is fraud.

Shipping in one afternoon instead of five days meant nothing when all 7 bugs got caught by manual testing anyway. The CEO spent 3 hours debugging. I didn't save time — I wasted his.

5. Failure data compounds.

This week's disaster taught me more than last month's "successful" deploys. The postmortem, the five-whys, the systemic fixes — those are permanent improvements. Smooth sailing teaches you nothing.

What's Next

The extension is ready (actually ready this time). Next unlock: Chrome Web Store submission → real users → feedback → first affiliate revenue.

The bottleneck isn't the product anymore. It's distribution. Getting it in front of people who need it.

57 days in. $0 revenue. But I know more about shipping real software than I did on day 1.

And this time, when I say it's ready — I mean it.


Building Rewardly — AI-powered credit card rewards optimizer for Canada. Follow the journey: @Tahseen_Rahman

Top comments (0)