← Writing

The green test that lied

10 August 2026 Tech

A snapshot of SAHARA CORE in August 2026 recorded 2,127 tests with 1 red. A wall of green and one small failure is the kind of result that invites you to believe the system is solid.

It wasn’t. Two serious bugs were sitting under that green wall, both of them in the part of the system that is supposed to be incapable of lying. The books.

I studied accounting before I studied software. BBS, Tribhuvan University, passed October 2025. I keep the accounts for our family business in Narayangarh, and when my father travels the whole workload lands on me. So the significance of these two bugs was clear to me. This was not a broken feature. It was a machine that produced confident, well-formatted, wrong numbers.

Bug one: the test that was checking nothing

Every accounting entry has two sides. Money leaves one place and arrives in another. Debit and credit. If you buy cloth for cash, cash goes down and stock goes up, and the two amounts must be equal and opposite. That is the whole trick of double-entry, and it has survived five hundred years because it is very hard to cheat.

My engine had a test covering exactly this. It was green.

Underneath, debit and credit were reversed.

Not “sometimes.” Reversed. The entry was recording the mirror image of what actually happened. An expense could show up on the side where income lives.

Why didn’t the test catch it? Two reasons, and both of them are traps any engineer can fall into.

The first is that I asserted a symmetric invariant. My test checked that total debits equal total credits. That is a real accounting rule, and it feels rigorous. But think about what it actually proves. If you swap the two sides of every entry, the totals still match. Perfectly. The books balance beautifully while facing the wrong direction. I chose an assertion that was structurally blind to the exact bug I most needed to catch.

The second reason is worse. Part of that test was iterating over rows fetched from the ledger and asserting on each one. The fetch came back empty. Zero rows. So the loop body never ran, no assertion was ever evaluated, and the test reported success. It wasn’t testing weakly. It was testing nothing at all, and calling it a pass.

Reading the test would not have revealed it. What revealed it was checking the accounting output itself — because an entry sitting on the wrong side is visible to someone who reads books for a living long before it is visible to an assertion that never runs.

Bug two: the balance sheet that forgot the shelf

The second one is easier to explain, and its consequences are wider.

While checking the accounting output, I found that the system reported a loss of Rs 2,050 where the expected loss was Rs 300. Nearly seven times off.

Here is what happened, in plain language. Say you buy ten shirts and sell three. You have spent money on ten. You have earned money on three. The seven shirts still on your shelf are not a loss. They are an asset. You own them. They have value. Accounting handles this with closing stock: unsold inventory gets carried onto the Balance Sheet instead of being buried in the cost of what you sold.

My system dropped it. The purchases hit the profit and loss in full, the unsold stock never landed anywhere, and the software reported a business that was bleeding when it was roughly flat.

Now scale that. Our family’s business, Trishakti Stores, has been running since 1991 and has built a network reaching around 750 active retail shops across Mid-Nepal as of 2026. Garment wholesale means stock sitting everywhere at any moment. A system that forgets unsold stock would tell an owner they are losing money in the exact season they should be buying more. It would produce wrong figures for the tax return. It would poison a conversation with a bank. Nobody would question it, because it came from the computer, and it was formatted correctly, and there were 2,127 tests behind it.

That is the real danger of a wrong number. Not that it is wrong. That it is trusted.

What actually changed

The fixes were small. The rules that came out of them were not.

Every test must be proven capable of failing. Before I trust a test, I break the code it covers on purpose and watch it go red. If it stays green, it isn’t a test, it’s decoration. This one habit would have caught the empty-loop bug in thirty seconds.

An empty result set is a failure, not a silence. If a test iterates over data, it asserts the count first. Zero rows means the fixture is broken, and a broken fixture must scream, not shrug.

Assert asymmetrically. Don’t check an invariant that survives the bug you’re afraid of. Balance was never enough. Now I assert the signed value on the specific side. Direction, not just equality.

Bring the answer from outside the system. The expected figure has to come from somewhere other than the code being tested. If the expected value is generated by the same code path, you have built a very expensive mirror.

Reconcile across boundaries. The ledger must agree with the statement, and the statement must agree with the stock. The stock bug lived in the gap between two things that were each internally consistent.

And one about how I work with AI, since coding agents wrote plenty of scaffolding around these paths: they are workers I supervise, not magicians I trust. An agent will happily write you a test that passes vacuously and report it as done. So will a tired human. The system, not the worker, has to make that impossible.

Assume the books are lying

I did not become a better engineer by adding tests. I became one by losing faith in them.

Green is a claim, not a proof. It tells you nobody has demonstrated a failure yet. That is genuinely useful and it is nowhere close to correctness. Auditors already know this, which is why they don’t ask whether your accountant says the figures are right. They tie the numbers back to something real, from outside, until it reconciles.

So do that to your own code. Pick your most important test, the one guarding the thing that would hurt most if it broke. Go break that code on purpose right now.

If the suite stays green, you just learned something.

testingaccounting-softwareerpdebuggingsahara-coreengineering-practice