Make lazy export behavior observable
Goal
You verify the number of rows consumed, newline escaping, public fields, and the response format.
Why it matters
Export code tested with a small list looked normal, but it converted a large input entirely to a list and exhausted memory. Having the same result does not mean the contract about how it runs is the same. You have to turn lazy consumption into a state you can see.
Steps
- In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: validate_row(row) returns row when it is a dict whose id is a positive int (excluding bool) and whose name is a non-empty str. Everything else is ValueError. Extra internal fields are allowed. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab
-
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: project(row) returns a new dict that has only id and name after validate_row. The internal fields of the original are preserved as they are. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: encode_line(row) is a str that JSON-encodes the result of project with ensure_ascii=False, separators=(',',':'), and sort_keys=True, and appends exactly one trailing newline character at the end. A newline inside name must be a JSON escape. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: validate_max(value) returns the value as it is only for an int from 1 to 1000 that is not a bool, and everything else is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: take_rows(rows, maximum) is an iterator that lazily returns at most maximum rows, using islice or similar. It validates maximum when called, and each next consumes the input only once. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: json_lines(rows, maximum=100) yields encode_line for each row received from take_rows. It does not return a string or a list with everything combined. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: decode_lines(text) runs json.loads and then validate_row on each non-empty line of splitlines and returns a list. An empty string gives [], and an empty line in the middle is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function. -
In
/root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: create_app(rows) returns json_lines(rows, 100) as an application/x-ndjson StreamingResponse at GET /export. rows is a list that can be iterated again. There must be no internal fields, and the content and order of each row must be preserved. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Notes
- You work in the existing lab-dev environment with no internet and no package installation.
- Each step runs within a 45-second grading budget. Do not add real sleeps or network calls.
- The submitted tests are run in a separate temporary folder against the correct and defective implementations. Against the correct one, every test that actually runs must pass, and against a defective one, the body of a test must fail. A collection error, zero tests run, everything skipped, and forced termination are not a pass. Use only the basic pytest features and the provided libraries.
- FastAPI official documentation · pytest official documentation · Python sqlite3
- Limitation: TestClient buffers the response, so it does not prove the delay of the first byte over the network or the whole memory cap. Whether evaluation is lazy is checked with a separate counting generator. Once the stream has started, it is hard to change the status to a normal error JSON when you meet a bad row. A real service has to decide which to pick among prior validation, a per-row error format, and an abort policy. You may read the provided implementation, but grading uses a separate copy. Do not work around a defect by checking the wording of the source or by modifying files; check the execution results of the public interface.
Validate the row contract — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: validate_row(row) returns row when it is a dict whose id is a positive int (excluding bool) and whose name is a non-empty str. Everything else is ValueError. Extra internal fields are allowed. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Prepare this once at the start. Existing files are not overwritten.
mkdir -p /root/work/test-lazy-export-lab
test -e /root/work/test-lazy-export-lab/service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/service.py /root/work/test-lazy-export-lab/service.py
test -e /root/work/test-lazy-export-lab/test_service.py || cp /opt/fixtures/ten_labs/test-lazy-export-lab/test_service.py /root/work/test-lazy-export-lab/test_service.py
cd /root/work/test-lazy-export-lab
Distinguish bool from numbers and treat an empty name as an error. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/01-contract.sh.
Build only public rows — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: project(row) returns a new dict that has only id and name after validate_row. The internal fields of the original are preserved as they are. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
The export path must apply the same public-field policy as an ordinary API. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/02-contract.sh.
Encode while preserving line boundaries — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: encode_line(row) is a str that JSON-encodes the result of project with ensure_ascii=False, separators=(',',':'), and sort_keys=True, and appends exactly one trailing newline character at the end. A newline inside name must be a JSON escape. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
If you build JSON by appending strings, the format breaks at quotes and newlines. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/03-contract.sh.
Validate the output count cap — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: validate_max(value) returns the value as it is only for an int from 1 to 1000 that is not a bool, and everything else is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Require a cap from the caller so that an infinite input is not read to the end by mistake. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/04-contract.sh.
Consume only the rows you need — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: take_rows(rows, maximum) is an iterator that lazily returns at most maximum rows, using islice or similar. It validates maximum when called, and each next consumes the input only once. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
The moment you turn it into list(rows), you can no longer handle infinite input or very large input. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/05-contract.sh.
Serialize rows lazily — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: json_lines(rows, maximum=100) yields encode_line for each row received from take_rows. It does not return a string or a list with everything combined. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Keep object selection and representation conversion each as a lazy stage. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/06-contract.sh.
Validate the downloaded lines again — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: decode_lines(text) runs json.loads and then validate_row on each non-empty line of splitlines and returns a list. An empty string gives [], and an empty line in the middle is ValueError. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Distinguish an empty file from a malformed empty record. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/07-contract.sh.
Complete the HTTP download — test
In /root/work/test-lazy-export-lab/test_service.py, test the following public contract of the provided service.py: create_app(rows) returns json_lines(rows, 100) as an application/x-ndjson StreamingResponse at GET /export. rows is a list that can be iterated again. There must be no internal fields, and the content and order of each row must be preserved. It must pass against the correct implementation and be caught, through a failure in the body of an actual test, in an implementation that breaks this contract. Keep the tests from the earlier steps and add a test_ function.
Check, together with a generator test, that the code does not just state streaming in the Content-Type while gathering everything internally. Do not modify the implementation file. Use pytest.raises to check the expected exception, and assert a concrete expected value for the normal result.
After saving, check with bash /opt/lab/checks/test-lazy-export-lab/08-contract.sh.