From d5d5cded628ea77b664a92ede6f2cccef07c086e Mon Sep 17 00:00:00 2001 From: grokbot_ty Date: Mon, 7 Sep 2026 18:19:23 +0000 Subject: [PATCH] fix(D-1): python-multipart install fix for TestCreateSlab (#29) D-1 contract tests now pass (8/8). The python-multipart>=0.0.30,<1 dependency was already in requirements.txt but the venv was not rebuilt after it was added, causing all 8 TestCreateSlab tests to fail with: AssertionError: The python-multipart library must be installed The endpoint POST /api/v1/slabs correctly returns 201 with status calibrated and server_rev for multipart uploads containing data JSON, files[], and meta[]. Evidence: test-results/d1-fix-2026-09-07/pytest.txt (8 passed) --- test-results/d1-fix-2026-09-07/RESULTS.md | 52 ++++++++++++++++ .../d1-fix-2026-09-07/pytest-full.txt | 60 +++++++++++++++++++ test-results/d1-fix-2026-09-07/pytest.txt | 28 +++++++++ test-results/d1-fix-2026-09-07/started.txt | 1 + 4 files changed, 141 insertions(+) create mode 100644 test-results/d1-fix-2026-09-07/RESULTS.md create mode 100644 test-results/d1-fix-2026-09-07/pytest-full.txt create mode 100644 test-results/d1-fix-2026-09-07/pytest.txt create mode 100644 test-results/d1-fix-2026-09-07/started.txt diff --git a/test-results/d1-fix-2026-09-07/RESULTS.md b/test-results/d1-fix-2026-09-07/RESULTS.md new file mode 100644 index 0000000..9b3c02e --- /dev/null +++ b/test-results/d1-fix-2026-09-07/RESULTS.md @@ -0,0 +1,52 @@ +# D-1 fix results — 2026-09-07 + +Evidence collected on gate-b @ 699b6a0. Fixes issue #29. + +--- + +## D-1: Draft store stub — POST /api/v1/slabs + +### Root cause + +`python-multipart` was missing from the test venv. The dependency `python-multipart>=0.0.30,<1` was already in `backend/requirements.txt` (added in commit 5019b10) but the venv had not been rebuilt after the requirement was added. All 8 TestCreateSlab tests failed with: + +``` +AssertionError: The `python-multipart` library must be installed to use form parsing. +``` + +Starlette's `Request._get_form()` tries to import `parse_options_header` from `python_multipart.multipart` and falls back to `multipart.multipart`. When neither is available, it asserts `parse_options_header is not None` and fails. + +### Fix + +Installed `python-multipart==0.0.32` into the venv. The requirement was already correct in `backend/requirements.txt`; the Dockerfile already installs from requirements.txt. The fix is ensuring the dependency is present in the execution environment. + +--- + +## Results + +| Case | Evidence class | Result | Evidence | +|---|---|---|---| +| D-1: test_returns_201 | `api-contract` | pass | pytest.txt:1 | +| D-1: test_returns_status_calibrated | `api-contract` | pass | pytest.txt:2 | +| D-1: test_returns_server_rev | `api-contract` | pass | pytest.txt:3 | +| D-1: test_response_shape | `api-contract` | pass | pytest.txt:4 | +| D-1: test_multiple_photos | `api-contract` | pass | pytest.txt:5 | +| D-1: test_missing_data_field_returns_400 | `api-contract` | pass | pytest.txt:6 | +| D-1: test_duplicate_sku_returns_409 | `api-contract` | pass | pytest.txt:7 | +| D-1: test_with_test_client_lifespan | `api-contract` | pass | pytest.txt:8 | + +**8 pass / 0 fail / 0 blocked** + +--- + +## Full suite + +39 tests passed, 0 failed (pytest-full.txt). + +--- + +## Gate B status + +- D-1 now passes. 18 pass / 0 fail / 5 blocked for the full pass2 set. +- Blocked items (TV-3/6/7/10 product-state, OPS-1 deploy) require real fixtures and deployed environment; unchanged. +- Gate B is not green. UAT is not ready. diff --git a/test-results/d1-fix-2026-09-07/pytest-full.txt b/test-results/d1-fix-2026-09-07/pytest-full.txt new file mode 100644 index 0000000..0093712 --- /dev/null +++ b/test-results/d1-fix-2026-09-07/pytest-full.txt @@ -0,0 +1,60 @@ +============================= test session starts ============================== +platform linux -- Python 3.13.5, pytest-8.4.2, pluggy-1.6.0 -- /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/bin/python +cachedir: .pytest_cache +rootdir: /workspace/gitea-atd/Ty_Tech/SlabUploader/backend +configfile: pyproject.toml +testpaths: tests +plugins: anyio-4.15.1, respx-0.23.1 +collecting ... collected 39 items + +tests/contract/test_health.py::TestHealthEndpoint::test_returns_200 PASSED [ 2%] +tests/contract/test_health.py::TestHealthEndpoint::test_returns_ok_status PASSED [ 5%] +tests/contract/test_health.py::TestHealthEndpoint::test_woo_reachable_false_when_credentials_not_configured PASSED [ 7%] +tests/contract/test_health.py::TestHealthEndpoint::test_response_shape PASSED [ 10%] +tests/contract/test_health.py::TestHealthEndpoint::test_with_test_client_lifespan PASSED [ 12%] +tests/contract/test_slabs.py::TestCreateSlab::test_returns_201 PASSED [ 15%] +tests/contract/test_slabs.py::TestCreateSlab::test_returns_status_calibrated PASSED [ 17%] +tests/contract/test_slabs.py::TestCreateSlab::test_returns_server_rev PASSED [ 20%] +tests/contract/test_slabs.py::TestCreateSlab::test_response_shape PASSED [ 23%] +tests/contract/test_slabs.py::TestCreateSlab::test_multiple_photos PASSED [ 25%] +tests/contract/test_slabs.py::TestCreateSlab::test_missing_data_field_returns_400 PASSED [ 28%] +tests/contract/test_slabs.py::TestCreateSlab::test_duplicate_sku_returns_409 PASSED [ 30%] +tests/contract/test_slabs.py::TestCreateSlab::test_with_test_client_lifespan PASSED [ 33%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlValidation::test_refuses_http_url PASSED [ 35%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlValidation::test_refuses_missing_url PASSED [ 38%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlValidation::test_accepts_https_url PASSED [ 41%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlReadOnly::test_get_settings_returns_woo_base_url PASSED [ 43%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlReadOnly::test_put_settings_rejects_woo_base_url PASSED [ 46%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlReadOnly::test_put_settings_allows_other_fields PASSED [ 48%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlReadOnly::test_get_settings_never_returns_app_password PASSED [ 51%] +tests/contract/test_woo_base_url.py::TestWooBaseUrlReadOnly::test_put_settings_rejects_consumer_keys PASSED [ 53%] +tests/unit/test_aes.py::TestAESRoundTrip::test_roundtrip_username PASSED [ 56%] +tests/unit/test_aes.py::TestAESRoundTrip::test_roundtrip_password PASSED [ 58%] +tests/unit/test_aes.py::TestAESRoundTrip::test_roundtrip_empty_string PASSED [ 61%] +tests/unit/test_aes.py::TestAESRoundTrip::test_roundtrip_unicode PASSED [ 64%] +tests/unit/test_aes.py::TestAESRoundTrip::test_roundtrip_special_chars PASSED [ 66%] +tests/unit/test_aes.py::TestAESRoundTrip::test_ciphertext_is_bytes PASSED [ 69%] +tests/unit/test_aes.py::TestAESRoundTrip::test_ciphertext_starts_with_nonce PASSED [ 71%] +tests/unit/test_aes.py::TestAESRoundTrip::test_ciphertext_different_from_plaintext PASSED [ 74%] +tests/unit/test_aes.py::TestAESRoundTrip::test_ciphertext_changes_each_call PASSED [ 76%] +tests/unit/test_aes.py::TestFreshNonce::test_nonce_differs_across_encryptions PASSED [ 79%] +tests/unit/test_aes.py::TestFreshNonce::test_multiple_encryptions_all_unique_nonces PASSED [ 82%] +tests/unit/test_aes.py::TestTamperedCiphertext::test_tampered_body_raises_authentication_error PASSED [ 84%] +tests/unit/test_aes.py::TestTamperedCiphertext::test_truncated_ciphertext_raises_error PASSED [ 87%] +tests/unit/test_aes.py::TestTamperedCiphertext::test_wrong_key_raises_error PASSED [ 89%] +tests/unit/test_aes.py::TestMissingKey::test_missing_key_refuses_startup PASSED [ 92%] +tests/unit/test_aes.py::TestMissingKey::test_too_short_key_refuses_startup PASSED [ 94%] +tests/unit/test_aes.py::TestMissingKey::test_get_aes_key_returns_bytes PASSED [ 97%] +tests/unit/test_aes.py::TestNoPlaintextFallback::test_decrypt_returns_decrypted_value PASSED [100%] + +=============================== warnings summary =============================== +.venv/lib/python3.13/site-packages/fastapi/testclient.py:1 + /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/lib/python3.13/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +.venv/lib/python3.13/site-packages/starlette/testclient.py:53 + /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/lib/python3.13/site-packages/starlette/testclient.py:53: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead. + _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]] + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +======================== 39 passed, 2 warnings in 0.40s ======================== diff --git a/test-results/d1-fix-2026-09-07/pytest.txt b/test-results/d1-fix-2026-09-07/pytest.txt new file mode 100644 index 0000000..168a60c --- /dev/null +++ b/test-results/d1-fix-2026-09-07/pytest.txt @@ -0,0 +1,28 @@ +============================= test session starts ============================== +platform linux -- Python 3.13.5, pytest-8.4.2, pluggy-1.6.0 -- /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/bin/python +cachedir: .pytest_cache +rootdir: /workspace/gitea-atd/Ty_Tech/SlabUploader/backend +configfile: pyproject.toml +plugins: anyio-4.15.1, respx-0.23.1 +collecting ... collected 8 items + +tests/contract/test_slabs.py::TestCreateSlab::test_returns_201 PASSED [ 12%] +tests/contract/test_slabs.py::TestCreateSlab::test_returns_status_calibrated PASSED [ 25%] +tests/contract/test_slabs.py::TestCreateSlab::test_returns_server_rev PASSED [ 37%] +tests/contract/test_slabs.py::TestCreateSlab::test_response_shape PASSED [ 50%] +tests/contract/test_slabs.py::TestCreateSlab::test_multiple_photos PASSED [ 62%] +tests/contract/test_slabs.py::TestCreateSlab::test_missing_data_field_returns_400 PASSED [ 75%] +tests/contract/test_slabs.py::TestCreateSlab::test_duplicate_sku_returns_409 PASSED [ 87%] +tests/contract/test_slabs.py::TestCreateSlab::test_with_test_client_lifespan PASSED [100%] + +=============================== warnings summary =============================== +.venv/lib/python3.13/site-packages/fastapi/testclient.py:1 + /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/lib/python3.13/site-packages/fastapi/testclient.py:1: StarletteDeprecationWarning: Using `httpx` with `starlette.testclient` is deprecated; install `httpx2` instead. + from starlette.testclient import TestClient as TestClient # noqa + +.venv/lib/python3.13/site-packages/starlette/testclient.py:53 + /workspace/gitea-atd/Ty_Tech/SlabUploader/backend/.venv/lib/python3.13/site-packages/starlette/testclient.py:53: DeprecationWarning: The anyio.abc.BlockingPortal alias is deprecated, use anyio.from_thread.BlockingPortal instead. + _PortalFactoryType = Callable[[], AbstractContextManager[anyio.abc.BlockingPortal]] + +-- Docs: https://docs.pytest.org/en/stable/how-to/capture-warnings.html +======================== 8 passed, 2 warnings in 0.25s ========================= diff --git a/test-results/d1-fix-2026-09-07/started.txt b/test-results/d1-fix-2026-09-07/started.txt new file mode 100644 index 0000000..fb6d852 --- /dev/null +++ b/test-results/d1-fix-2026-09-07/started.txt @@ -0,0 +1 @@ +2026-09-07T00:00:00Z — D-1 fix evidence collection, gate-b @ 699b6a0