😏
This commit is contained in:
@@ -0,0 +1,11 @@
|
||||
# Ledger invariants
|
||||
|
||||
`Ledger.apply(operation_id, account, delta)` applies an operation at most once globally.
|
||||
|
||||
- A successful operation permanently consumes its operation ID.
|
||||
- A rejected overdraft must not consume its operation ID.
|
||||
- A caller may fund the account and retry the same rejected operation.
|
||||
- Balances may never become negative.
|
||||
- Duplicate successful operations return the current balance without applying the delta again.
|
||||
|
||||
The current implementation violates the rejected-operation rule.
|
||||
@@ -0,0 +1,17 @@
|
||||
class Ledger:
|
||||
def __init__(self) -> None:
|
||||
self._balances: dict[str, int] = {}
|
||||
self._applied: set[str] = set()
|
||||
|
||||
def balance(self, account: str) -> int:
|
||||
return self._balances.get(account, 0)
|
||||
|
||||
def apply(self, operation_id: str, account: str, delta: int) -> int:
|
||||
if operation_id in self._applied:
|
||||
return self.balance(account)
|
||||
self._applied.add(operation_id)
|
||||
updated = self.balance(account) + delta
|
||||
if updated < 0:
|
||||
raise ValueError("insufficient funds")
|
||||
self._balances[account] = updated
|
||||
return updated
|
||||
@@ -0,0 +1,27 @@
|
||||
import unittest
|
||||
|
||||
from ledger import Ledger
|
||||
|
||||
|
||||
class LedgerTests(unittest.TestCase):
|
||||
def test_success_is_idempotent(self):
|
||||
ledger = Ledger()
|
||||
self.assertEqual(ledger.apply("deposit-1", "a", 10), 10)
|
||||
self.assertEqual(ledger.apply("deposit-1", "a", 10), 10)
|
||||
|
||||
def test_overdraft_does_not_change_balance(self):
|
||||
ledger = Ledger()
|
||||
with self.assertRaises(ValueError):
|
||||
ledger.apply("withdraw-1", "a", -4)
|
||||
self.assertEqual(ledger.balance("a"), 0)
|
||||
|
||||
def test_rejected_operation_can_be_retried(self):
|
||||
ledger = Ledger()
|
||||
with self.assertRaises(ValueError):
|
||||
ledger.apply("withdraw-1", "a", -4)
|
||||
ledger.apply("deposit-1", "a", 10)
|
||||
self.assertEqual(ledger.apply("withdraw-1", "a", -4), 6)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in New Issue
Block a user