test-03 reproduction

Public source retained with the related article. Generated results, dependency directories, runtime storage, secrets, and oversized binary artifacts are intentionally excluded.

README

# TEST-03 mock-fortress experiment

This fixture supports the planned article *A Mock Fortress Tests a Call
Script*. It compares two tests for the same checkout quote under two controlled
changes:

- a behavior-preserving reorder of independent policy calls; and
- a production serializer defect that maps `tax_cents` from shipping.

The experiment asks what each test can reject. It does not claim that mocks,
ordered expectations, or unit tests are categorically harmful.

## Run it

Docker is the only host dependency.

```sh
./run.sh all
```

Each counterfactual can also run independently:

```sh
./run.sh clean
./run.sh reorder
./run.sh serializer
```

The runner builds a digest-pinned image, executes every expected pass and
failure, and returns non-zero if the crossed result is not reproduced. It
retains the exact command, expectation, exit code, duration, and console output
under `results/`.

## Expected matrix

| Mode | Mock fortress | Collaboration test | Observable JSON |
| --- | --- | --- | --- |
| Clean | pass | pass | expected quote |
| Policies reordered | fail | pass | byte-for-byte equal to clean |
| Serializer defect | pass | fail | `tax_cents` changes from `1800` to `500` |

The mock-fortress test constructs `BuildCheckoutQuote` directly with five
Mockery doubles. Every expectation is globally ordered. Its serializer double
returns the expected JSON instead of executing `JsonQuoteSerializer`.

The collaboration test registers the production bindings in Laravel's real
container, replaces only `ProductCatalogue`, resolves `BuildCheckoutQuote`, and
asserts the exact JSON produced by the real policies and serializer.

## Double map

| Double in fortress | Classification | Collaboration-test decision |
| --- | --- | --- |
| `ProductCatalogue` | external HTTP gateway | keep a fixed implementation of the production port |
| `DiscountPolicy` | owned deterministic policy | use the real implementation |
| `TaxPolicy` | owned deterministic policy | use the real implementation |
| `ShippingPolicy` | owned deterministic policy | use the real implementation |
| `QuoteSerializer` | owned output collaborator | use the real implementation |
| Laravel container | application composition | resolve through the real container |

The retained catalogue substitute returns only a product price. It neither
reimplements the policies nor supplies the final quote. The real HTTP adapter
still needs separate contract coverage; TEST-04 owns that question.

## Why both counterfactuals matter

The reorder is deliberately harmless. Tax and shipping depend on the same
already-discounted subtotal and destination, not on each other. `run.sh`
compares the complete clean and reordered output before accepting the green
collaboration test.

The serializer defect is deliberately observable. The quote total remains
`11300`, but the serialized `tax_cents` field changes from `1800` to `500`.
The fortress cannot see that production defect because its serializer never
runs.

Strict ordering can be correct when the order itself is a protocol, such as
commit-before-acknowledge. This fixture's independent policy calculations do
not have that contract.

## 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, Mockery 1.6.12, 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 for each command.
They are reproduction metadata, not a benchmark of mocks versus real
collaborators.

Retained files