Test failure paths and side effects in isolation
Goal
You reproduce call counts, backoff, file preservation, and save failures without a network and without real waiting.
Why it matters
Success on one normal request does not guarantee boundary values or failure recovery. In this lab you implement or test the contract of each function in small pieces and then connect them in real execution. The grader does not look only at whether code exists or at the wording of a report; it checks results, exceptions, and stored state. Keep the code from the earlier steps as you move to the next step.
Steps
- In
/root/work/test-failures-lab/test_service.py, test that fetch returns the string from send unchanged and calls send exactly once. Pass cache as a pathlib.Path under tmp_path. Do the first preparation with the following command.
mkdir -p /root/work/test-failures-lab
cp /opt/fixtures/practice_depth/test-failures-lab/* /root/work/test-failures-lab/
cd /root/work/test-failures-lab
- In
/root/work/test-failures-lab/test_service.py, set it up so that the first send raises TimeoutError and the second returns 'fresh', and check that fetch returns 'fresh'. - In
/root/work/test-failures-lab/test_service.py, supply a send that keeps raising TimeoutError and check that TimeoutError propagates after a total of 3 calls. - In
/root/work/test-failures-lab/test_service.py, test that a send that raises ValueError is called exactly once and that the same kind of exception propagates. - In
/root/work/test-failures-lab/test_service.py, check that when every send raises TimeoutError, the values passed to sleep are exactly [0.1, 0.2]. It does not actually sleep. - In
/root/work/test-failures-lab/test_service.py, succeed with a Korean string, then read the cache file as UTF-8 and check that the same content was saved. - In
/root/work/test-failures-lab/test_service.py, write 'old' to cache beforehand and then make send keep failing. After the exception, check that the file content is still 'old'. - In
/root/work/test-failures-lab/test_service.py, test that OSError propagates when writing to cache fails even though send succeeds. If you pass the tmp_path directory itself as cache, the write fails deterministically.
Notes
- The packages are installed in the image. You do not need an internet connection or pip install.
- Do the preparation copy only once, at the start. If you copy again, your working files are reset.
/root/work/test-failures-lab/service.pyis the provided code for local runs, copied in the initial preparation. Do not modify it or overwrite it with sample code; write onlytest_service.py.- In your tests, import the provided functions with
from service import .... The grader copies only your tests into a separate temporary directory and runs them against the correct and defective implementations. In each step, the test run itself must catch the corresponding defect, and against the correct code every test you ran must pass. Collection errors, import errors, and forced termination do not count as detection. Do not depend on external files or the network. - Grading of each step runs within 45 seconds. Do not create infinite loops or real waiting.
Catch needless calls on the success path
In /root/work/test-failures-lab/test_service.py, test that fetch returns the string from send unchanged and calls send exactly once. Pass cache as a pathlib.Path under tmp_path. Do the first preparation with the following command.
mkdir -p /root/work/test-failures-lab
cp /opt/fixtures/practice_depth/test-failures-lab/* /root/work/test-failures-lab/
cd /root/work/test-failures-lab
Inject a function that records into a calls list. If you look only at the return value, you miss duplicate calls.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/01-contract.sh. Save the file and run it again.
Fail once and then recover
In /root/work/test-failures-lab/test_service.py, set it up so that the first send raises TimeoutError and the second returns 'fresh', and check that fetch returns 'fresh'.
Control the order with an iterator or a call count. The test itself must not connect to a real network.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/02-contract.sh. Save the file and run it again.
Check the total number of attempts and the last exception
In /root/work/test-failures-lab/test_service.py, supply a send that keeps raising TimeoutError and check that TimeoutError propagates after a total of 3 calls.
Three retries and three total attempts are different. In this contract it is 3 calls, including the first request.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/03-contract.sh. Save the file and run it again.
Prevent retries of a permanent error
In /root/work/test-failures-lab/test_service.py, test that a send that raises ValueError is called exactly once and that the same kind of exception propagates.
You have to check the length of calls as well as the kind of exception to catch the two pointless extra calls.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/04-contract.sh. Save the file and run it again.
Check the backoff sequence and the last wait
In /root/work/test-failures-lab/test_service.py, check that when every send raises TimeoutError, the values passed to sleep are exactly [0.1, 0.2]. It does not actually sleep.
Pass delays.append as the sleep argument. After the last failure there is no next attempt, so there must be no waiting either.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/05-contract.sh. Save the file and run it again.
Check the return value and the disk content together
In /root/work/test-failures-lab/test_service.py, succeed with a Korean string, then read the cache file as UTF-8 and check that the same content was saved.
An implementation that returns the success result but forgets to write the file cannot be caught by a return value test alone.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/06-contract.sh. Save the file and run it again.
Keep a network failure from erasing the existing cache
In /root/work/test-failures-lab/test_service.py, write 'old' to cache beforehand and then make send keep failing. After the exception, check that the file content is still 'old'.
Structure it in the order of setup, then execution with the exception check, then a check of the file state. Do not look only at whether the file exists.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/07-contract.sh. Save the file and run it again.
Do not turn a save failure into a fake success
In /root/work/test-failures-lab/test_service.py, test that OSError propagates when writing to cache fails even though send succeeds. If you pass the tmp_path directory itself as cache, the write fails deterministically.
Create an error that does not depend on changing permissions. This is a different responsibility from a test that only checks that the success string is returned.
You can reproduce the grading directly with bash /opt/lab/checks/test-failures-lab/08-contract.sh. Save the file and run it again.