# The returns-resolution code lab A small, working, deliberately imperfect Python service. You improve it as you work through the course. It is the practice environment for learners who cannot use a work codebase, and a second opinion for those who can. The system is the same case the course uses from lesson 0039 onwards: a customer asks to return something, the service decides whether that is allowed, holds suspicious requests for a human, buys a return label from a carrier, moves the money, and tells the customer what happened. ## Setup There is nothing to install. Python 3.10 or newer, standard library only. No network, no accounts, no containers: the order system, the carrier and the payment gateway are in-process fakes and the database is SQLite in memory. ``` cd lab python3 -m returns_lab.demo # see it work python3 run_tests.py # the one command ``` `run_tests.py` uses pytest if you have it and falls back to `unittest` if you do not. Both runners see exactly the same tests. If you prefer to type the runner yourself: ``` python3 -m pytest -q # if pytest is installed python3 -m unittest discover -s . -p "test_*.py" # always works ``` Optional: `pip install -r requirements-dev.txt` gets you pytest's `-k` filtering, which is convenient once you are working a single checkpoint. Narrow the run while you work: ``` python3 run_tests.py tests # behaviour: characterization + consumers python3 run_tests.py fitness # architecture fitness functions python3 -m unittest tests.test_characterization_refund_and_label python3 -m unittest discover -s fitness -t . ``` ## What a green run looks like today ``` Ran 131 tests ... OK (expected failures=43) ``` 88 tests pass: 85 describe what the code does now, and three are fitness functions that are already true. 43 fail on purpose and are marked as expected failures: those are the architecture fitness functions, and turning them green is the work. Nothing here is broken. ## The map of the system ``` lab/ ├── returns_lab/ the system (17 modules, ~1,600 lines) │ ├── __init__.py │ ├── config.py settings, the current user, counters, the route table │ ├── db.py the SQLite connection │ ├── migrations.py the schema, at version 1 │ ├── models.py ReturnRequest and friends │ ├── policy.py eligibility rules and the refund amount │ ├── fraud.py the guardrail that sends returns to a human │ ├── agent_review.py the manual review queue │ ├── service.py ReturnsService: the workflow │ ├── notifications.py email and SMS, inline and on a worker thread │ ├── orders.py client for the order system (another team owns it) │ ├── vendor_shipmaster.py the carrier SDK │ ├── payments.py the refund gateway SDK │ ├── reporting.py finance's nightly job, the oldest consumer │ ├── ai_helpers.py formatting and validation helpers │ ├── api.py the JSON request layer three other teams call │ └── demo.py the scripted scenario ├── tests/ characterization tests + simulated consumers ├── fitness/ architecture fitness functions (mostly red) ├── checkpoints/ phase-1.md … phase-5.md — the exercises └── solutions/ reference solutions, with the forces written down ``` ### Who talks to whom - The **order system** is authoritative for order facts: what was bought, for how much, where it shipped and when it arrived. The lab reads from it and never writes to it. It can be unavailable. - **ShipMaster** is the carrier. It sells return labels and serves the US, CA and MX. It can fail. - **PayPortal** moves the money. It speaks in minor units. It can fail. - **Finance** reads the `returns` table directly, on a schedule you do not control, from a job you cannot deploy. - **Three client teams** parse the JSON envelope in `api.py`: a support console, the customer web app, and a partner portal. ### The workflow, as the demo runs it 1. A request arrives at `api.handle`. 2. `ReturnsService.submit` asks the order system about the order. 3. `policy.check_eligibility` decides yes or no, and how much money. 4. `fraud.assess` decides whether a human should look first. 5. Either a review case is opened, or the return is approved and a label bought from the carrier. 6. Notifications are queued; a worker thread delivers them. 7. Later, an agent triggers the refund. ## An honest warning **This code is not good.** It works, the demo runs, and the tests pass, but it carries the structural problems this course exists to teach you to see: mixed concerns, hidden global state, leaking vendor contracts, weak invariants, a retry that is not safe to retry, an API other teams are coupled to, a schema that needs a careful migration, almost no observability, and one module that was generated by an assistant and never reviewed. **The defects are not listed here on purpose.** Finding them is the first exercise. When you want to check your work, `solutions/DEFECT_MAP.md` has the full inventory with a lesson number against each one — but read it after you have made your own list, not before. ## How to work the lab 1. Run the demo. Read `api.py`, then `service.py`, then follow one request all the way down. An hour is enough. 2. Open `checkpoints/phase-1.md` and work the exercises in order. Each one names the lessons it belongs to, the test or fitness function that turns green, and the forces you are trading off. 3. Before you change behaviour, check the characterization tests in `tests/`. They describe the system as it is, including the parts nobody likes. If a refactor breaks one, you have made a product decision — make it on purpose and change the test in the same commit, with a message that says why. 4. To take on a fitness function, delete its `@checkpoint(...)` line and make it pass. If you fix the code but leave the decorator, the runner reports an *unexpected success* and goes red. That is deliberate. 5. When you are done with an exercise, read the matching file under `solutions/`. Where a trade-off is genuinely contextual there are two valid answers and a note on when you would pick each. ## Characterization tests vs fitness functions | | Characterization test | Fitness function | |---|---|---| | Question | Does it still do what it did? | Is it still built the way we said? | | Written | Before you change anything | Before the property is true | | Today | Green | Red, and marked as expected | | When it breaks | You changed behaviour — was that on purpose? | The architecture drifted | | Lives in | `tests/` | `fitness/` | Characterization tests are the safety net Michael Feathers describes in *Working Effectively with Legacy Code* — they pin behaviour you do not necessarily like, so that refactoring is safe. Fitness functions are the executable form of an architecture decision: see `fitness/README.md`. ## Course cross-links - Lessons: `../lessons/0001-seeing-coupling-and-cohesion.html` onward. - The worked initiative packet for this same case: `../capstone/index.html`. - Assessments: `../assessments/index.html`. - This guide as a web page: `index.html`.