Missing Test Coverage for Payment Edge Case
Summary
This PR adds refund reversal logic to the billing service — a new, non-trivial code path that touches Stripe's external API and involves state transitions (initiated → pending → completed or failed). The implementation looks solid, but the PR ships zero test coverage for this new behavior. Any regression in the refund reversal logic will go undetected until it hits production. With payment logic, that's a business-critical blind spot.
Findings
The diff touches three files (billing/refund_handler.py, billing/models.py, api/routes.py) but no test files are included in the PR. The coverage report (run: pytest --cov=billing --cov-report=term-missing) shows billing/refund_handler.py at 0% coverage.
Refund reversal is a stateful, side-effect-heavy operation that calls an external API with real money at stake. The scenarios that need coverage include:
- Already refunded: the existing guard check — verify this path is tested and that it raises
ValueErroras expected. - Partial reversal:
amountis passed through to Stripe — verify behavior whenamount < charge.amountvs.amount == charge.amount. - Invalid charge ID: Stripe raises
InvalidRequestErrorwhen the charge doesn't exist — verify this surfaces correctly to API callers. - Reversal already pending: the
_get_pending_reversalhelper exists but is never called — is this dead code or is it part of the reversal flow that was forgotten?
_get_pending_reversal Never Called
Medium
The method _get_pending_reversal is defined on line 18 of the new file but has no call site in the diff or in the surrounding codebase. It looks like it was either:
- Part of an earlier draft of this feature that was removed but the helper was left behind, or
- Intended to be integrated but the integration was forgotten.
If it's intended, it needs a test and a call site. If it's not, it should be removed — dead code creates confusion and maintenance debt. Leaving it in with a comment explaining its purpose (or an explicit TODO linking to a Linear issue) is better than silently leaving it unused.
The new logging call at line 23 captures the charge_id and amount, which is good. However, if the Stripe API call raises an exception, there's no structured error log that captures the full request context. When a customer support ticket comes in about a failed reversal, having charge_id, amount, and the Stripe error message in one structured log entry will cut triage time significantly.
Recommendation
This PR should not be merged without tests. The logic is sound, but payment reversal without test coverage is a business risk. Before merging:
- Add at minimum the three happy-path test cases and two error-path cases described in Finding #1. Use
unittest.mockto patchstripe_client— do not hit the Stripe test API in unit tests. - Decide on
_get_pending_reversal: either integrate it into the reversal flow and test it, or remove it. Document the decision with a Linear issue if integration is deferred. - Add a structured error log in the exception handler for the Stripe call — at minimum capture
charge_id, error type, and error message.
The good news: the code quality is high, the validation is correct, and the logging call is well-placed. This is close to ready — it just needs the tests to land alongside it.
Suggested Fix
import pytest from unittest.mock import MagicMock from billing.refund_handler import RefundHandler class TestReverseRefund: def setup_method(self): self.mock_stripe = MagicMock() self.handler = RefundHandler(self.mock_stripe) def test_raises_when_charge_already_refunded(self): charge = MagicMock() charge.refunded = True self.mock_stripe.charges.retrieve.return_value = charge with pytest.raises(ValueError, match="already refunded"): self.handler.reverse_refund("ch_test123") def test_partial_reversal_calls_stripe_with_correct_amount(self): charge = MagicMock() charge.refunded = False charge.amount = 5000 self.mock_stripe.charges.retrieve.return_value = charge self.mock_stripe.refunds.create.return_value = "re_test456" self.handler.reverse_refund("ch_test123", amount=2500) self.mock_stripe.refunds.create.assert_called_once_with( charge="ch_test123", amount=2500 ) def test_invalid_charge_id_propagates_stripe_error(self): from stripe.error import InvalidRequestError self.mock_stripe.charges.retrieve.side_effect = InvalidRequestError( "No such charge", param="id" ) with pytest.raises(InvalidRequestError): self.handler.reverse_refund("ch_invalid")
Why Codio flagged this
Codio runs a coverage diff on every PR that touches Python or TypeScript files. When new logic lands without a corresponding test file, it flags the gap and explains exactly what cases are missing. For payment logic — anything that moves money — zero coverage is a high-severity concern regardless of how clean the code looks. Codio also cross-references the diff to find dead code helpers that are defined but never called, so unused code doesn't silently accumulate.
Get Codio reviewing your PRs
Ship reviews like this on every pull request. No reminders. No missed PRs.