Repair a real SQLite checkout race
Requires Python 3.11+ with sqlite3. No packages or production data needed.

Reference solution: not included here, so the exercise does not hand you the answer.
Attempt the repair first. Then download checkout-sqlite-solution.zip from the project card
on https://softwaresecurity.com/labs/#project-checkout-sqlite and extract it into this folder;
it adds reference.py without changing your files, and the reference commands below
need it.

1. Run python3 reproduce_race.py. Two connections read 100 before either writes; the barrier forces this schedule. Expected final balance: -60.
2. Run python3 -m unittest -v test_checkout.py. The starter fails validation cases and can fail concurrent cases. A concurrent test alone is not proof a race is absent.
3. Edit exercise.py. Validate positive integer cents. Execute a conditional UPDATE within BEGIN IMMEDIATE, then use affected-row count to decide paid/declined.
4. Run the tests again. Two purchases of 80 must produce one paid, one declined, and balance 20. Two purchases of 40 must both succeed.
5. Compare reference.py. To run its tests separately: EXERCISE_MODULE=reference python3 -m unittest -v test_checkout.py

SQLite serializes writers here. Other databases require their own isolation/locking analysis. This project does not process payments, authenticate users or implement idempotency. Temporary databases are deleted automatically. The vulnerable reproduction writes only synthetic local temporary data.

Dev container
.devcontainer/devcontainer.json opens this folder in VS Code Dev Containers or
GitHub Codespaces using the Dev Containers Python 3.13 image (Debian 13, Expat
2.8), pinned by digest. Run the commands above in its terminal.
