Four Hundred-Thousandths of a Penny
A discount at exactly the configured ceiling was routed to a manager instead of auto-approving. The cause was 0.00004 of a penny, and the textbook boundary pair walks straight past it.
Enter £31.99 and the discount auto-approves. Enter £32.00 — the exact configured limit — and it goes to a manager.
Not a penny over. The limit itself.
Why the Threshold Existed
Ten ceilings were configured in custom metadata, and every one of them had been chosen for a reason. A product at £57 with a maximum discount of £32 lands the customer on exactly £25.00 — a round floor price somebody in the business had picked deliberately.
The whole point of the number was to permit that price without a manager's involvement.
Not one of the ten thresholds actually permitted it.
The Round Trip That Is Not Lossless
The discount is stored net, to four decimal places. The approval resolver converts it back to gross before comparing against the ceiling.
Enter £32.00 gross. Divide by 1.2 and it stores as −26.6667. Multiply back by 1.2 and you get 32.00004.
The comparison is <= against 32. It fails by four hundred-thousandths of a penny, and the discount goes to a manager who has no idea why it landed in their queue.
Both the flat-amount and the percentage paths behaved the same way. It reproduced on the exact ceiling value and nowhere else — one penny either side and everything behaved correctly.
The Bit That Should Change How You Test
Here is what makes this defect worth a whole article.
The textbook boundary technique is a pair: one value just below the limit, one just above. Every syllabus teaches it, every reviewer expects it, and it is a good technique.
It walks straight past this bug.
£31.99 auto-approves — correct. £32.01 routes to a manager — correct. Both cases pass, both were tested, and the defect sits untouched between them, on the one value neither case exercises.
When a threshold exists to enable a specific target value, that value is its own mandatory test case. Not a stand-in for it. Not "something under." The number itself, exactly as a business user would type it.
And that is the second lesson: the failing input was the most natural one in the whole range. Nobody enters £31.99 when the agreed maximum is £32.00. They enter the maximum. The defect was reachable by the single most likely keystroke in production, and unreachable by the two values a well-trained tester would have chosen.
Where This Class of Bug Lives
Any comparison that crosses a unit conversion. That is the pattern to remember.
- Net to gross, or gross to net. VAT, tax, any markup applied in one direction and reversed in the other.
- Percentage to absolute. A percentage discount converted to a currency amount before storage or before validation.
- Currency to currency, or any storage precision that differs from display precision.
In each case the question is not "is the logic right." The logic is right. The question is: at how many decimal places is this comparison actually running, and what does the round trip do to the boundary?
That is a thirty-second question for a developer and it is worth asking every time you see a threshold applied to a calculated value.
Why the Unit Tests Did Not Catch It
They asserted values that were comfortably inside and comfortably outside the range. That is the same habit as the manual boundary pair, expressed in Apex.
There is a general point here. A test suite written by the person who wrote the logic tends to test the logic as they understand it — and their understanding is exactly what produced the defect. The rounding was invisible to the author because in their mental model the round trip was lossless.
So the boundary value has to be asserted by someone who does not share that model. Usually that is you.
The Three Cases That Should Exist
For any threshold on a calculated value, write three, not two:
- the value just below the limit,
- the limit itself,
- the value just above.
The middle one is not redundant. It is the only one that exercises the equality branch of the comparison, and equality is where rounding artefacts surface. Two cases test the inequality. Three test the rule.
That is one extra row in a test case, and in this instance it was the difference between a business-agreed price point working and ten configured thresholds each quietly failing to do the one thing they were configured for.
When you test a limit, do you test the limit — or only its neighbours?