Checking that a rejected charge leaves state unchanged would make the HTTP regression stronger than checking 409 alone. An implementation can write the negative balance and then throw the expected exception, so both endpoint tests could appear repaired while the protected state is still corrupted.
A useful sequence is an invalid charge followed by a valid charge in the same fresh application instance. If the valid charge of 20 still returns 80, the earlier rejection preserved the balance. Keep the existing isolated tests too: the sequence adds an observable state-transition assertion without making unrelated tests depend on each other’s execution order.
Thanks, Ahmet. Testing a rejected charge of 150 followed by a valid charge of 20 in the same app instance would strengthen this example. I agree that checking only the error response is not enough: the balance should still be 100 after rejection, then 80 after the valid charge. The current tests use separate app instances, so they do not cover that sequence.
The article focuses on closing a module-boundary bypass: every charge must go through WalletOperations, rather than access WalletStorage directly. In a production payment system, I would extend that principle into a shared transaction workflow with explicit states and handling rules for success, failure, pending or unknown outcomes, and duplicate requests.
We cannot predict every external failure, but we can define how the system handles uncertainty. For example, if an external transfer succeeds but the internal balance update fails, we need reconciliation rather than another transfer. Stable idempotency keys and atomic balance updates also help prevent duplicate effects and concurrent overspending.
This matters especially for direct crypto transfers, where a confirmed transfer generally cannot be reversed; returning funds usually requires a new transfer and the recipient’s cooperation. Your suggested test is a useful regression check, alongside those production controls.
That keeps the article's boundary claim appropriately narrow: routing every charge through WalletOperations closes the demonstrated bypass, while the rejection sequence checks the state guarantee independently.
For the production extension, I would make an unknown external outcome a distinct persisted state that the caller can inspect. A timeout should not be translated into a definitive failed transfer. A fixture where the provider succeeds but its response is lost would then exercise your reconciliation path without treating a new transfer as the recovery operation.
Ahmet Özel
AI Engineer. Computer Vision, RAG and LLM agents.
Checking that a rejected charge leaves state unchanged would make the HTTP regression stronger than checking 409 alone. An implementation can write the negative balance and then throw the expected exception, so both endpoint tests could appear repaired while the protected state is still corrupted.
A useful sequence is an invalid charge followed by a valid charge in the same fresh application instance. If the valid charge of 20 still returns 80, the earlier rejection preserved the balance. Keep the existing isolated tests too: the sequence adds an observable state-transition assertion without making unrelated tests depend on each other’s execution order.