test-04 reproduction
Public source retained with the related article. Generated results, dependency directories, runtime storage, secrets, and oversized binary artifacts are intentionally excluded.
README
# TEST-04 shared-contract experiment
This fixture supports the planned article *Shared Contract Tests Make
Substitutability Measurable*. It runs one unchanged consumer-shaped contract
against an HTTP catalogue adapter and an in-memory fake under two controlled
divergences:
- the fake returns zero instead of throwing `ProductNotFound` for an unknown
SKU; and
- the HTTP adapter translates the provider's `404` into the same zero value.
The experiment asks whether every implementation preserves two named
caller-visible behaviors. It does not claim that a finite suite proves complete
substitutability.
## Run it
Docker is the only host dependency.
```sh
./run.sh all
```
Each mode can also run independently:
```sh
./run.sh clean
./run.sh fake-defect
./run.sh adapter-defect
```
The runner builds a digest-pinned image, starts a real PHP HTTP provider inside
each test container, executes every expected pass and failure, and returns
non-zero if the matrix is not reproduced. It retains the exact command,
expectation, exit code, duration, and console output under `results/`.
## Expected matrix
| Mode | HTTP contract | Fake contract | Known-SKU consumer controls |
| --- | --- | --- | --- |
| Clean | pass | pass | pass |
| Fake missing-SKU divergence | pass | fail | pass |
| HTTP missing-SKU divergence | fail | pass | pass |
The shared `ProductCatalogueContract` asserts the exact integer price for a
known SKU and the `ProductNotFound` exception for an unknown SKU. Its concrete
HTTP and fake subclasses provide only an implementation factory; they do not
override examples or expected values.
The consumer controls calculate the known product's subtotal through both
implementations. They remain green under both divergences because neither asks
for a missing SKU. That is the intended negative control: a happy path cannot
establish failure semantics it never executes.
## Contract boundary
| Behavior | Evidence owner |
| --- | --- |
| known SKU returns exact integer cents | shared contract |
| unknown SKU throws `ProductNotFound` | shared contract |
| provider `404` maps to `ProductNotFound` | HTTP contract execution |
| malformed payload is rejected | HTTP-adapter-specific test, outside this fixture |
| retries, authentication, and timeouts | separate integration or resilience tests |
| fake seed data is deterministic | fake setup |
The local provider is an actual HTTP process reached through Laravel's
production `Illuminate\Http\Client\Factory`. It serves one known product and a
`404` for every other SKU. The fixture does not claim to exercise DNS, TLS,
remote availability, authentication, or deployment configuration.
## Toolchain and measurement boundary
The Dockerfile pins PHP and Composer images by version and digest.
`composer.lock` pins Laravel 13.20.0, PHPUnit 12.5.31, and the complete
dependency graph. The runner records package versions and Composer's advisory
result with each run.
Durations include starting a disposable Docker container and local provider
for each command. They are reproduction metadata, not a benchmark of HTTP
adapters against in-memory fakes.
Retained files
- .dockerignore
- bin/test-with-provider.sh
- bin/versions.php
- composer.json
- composer.lock
- Dockerfile
- phpunit.xml
- provider/router.php
- README.md
- run.sh
- src/Catalogue/HttpProductCatalogue.php
- src/Catalogue/InMemoryProductCatalogue.php
- src/Catalogue/ProductCatalogue.php
- src/Catalogue/ProductNotFound.php
- src/Checkout/QuoteSubtotal.php
- tests/HttpProductCatalogueContractTest.php
- tests/InMemoryProductCatalogueContractTest.php
- tests/KnownProductConsumerTest.php
- tests/ProductCatalogueContract.php