feat(14-01): record vendor calls with summer parity:upstream and replay them offline

- WriteUpstream masks vars, hashes long base64 JSON strings and refuses unmasked Authorization/X-Api-Key
- multipart requests recorded as ordered parts; the fake compares parts and hashed payloads
- loopback CONNECT recording proxy with a local ECDSA parity CA, script and forward modes
- parity:upstream command, README and parity docs
This commit is contained in:
Jakub Zych
2026-10-03 19:55:42 +02:00
parent e6a67134d1
commit ee0004fb65
11 changed files with 1647 additions and 15 deletions

View File

@@ -10,7 +10,7 @@ Besides building and scaffolding, the `summer` tool carries two groups of utilit
## API parity commands
When you port an existing backend to SummerCMS, its real responses are the contract your port must meet. The parity commands wrap [tide](../../modules/tide/README.md): they record fixtures from the reference backend and replay them against the port. Every address they listen on or connect to must be a loopback address, and captured secrets go to a variables file with mode 0600, outside the committed fixtures.
When you port an existing backend to SummerCMS, its real responses are the contract your port must meet. The parity commands wrap [tide](../../modules/tide/README.md): they record fixtures from the reference backend and replay them against the port. Every address they listen on or connect to must be a loopback address (the one exception is `parity:upstream --mode forward`, which sends each recorded vendor call once to the real vendor), and captured secrets go to a variables file with mode 0600, outside the committed fixtures.
| Command | Flags | Purpose |
|---------|-------|---------|
@@ -18,6 +18,7 @@ When you port an existing backend to SummerCMS, its real responses are the contr
| `summer parity:record` | `--spec`, `--target`, `--output`, `--rules`, `--vars`, `--update`; `--manifest`, `--fixtures`, `--next-batch`, `--resume`, `--allow-incomplete`, `--require-recorded` | Sends the requests of a YAML spec to a target and records the responses as a fixture, or records the missing cases of a route manifest in batches of at most 15. |
| `summer parity:replay` | `--fixtures`, `--target`, `--vars`, `--manifest`, `--self-check`, `--require-recorded` | Replays recorded fixtures against a backend and reports the differences after masking IDs and timestamps. |
| `summer parity:broadcasts` | `--flow`, `--target`, `--vars`, `--listen` (default `127.0.0.1:8424`), `--out`, `--name`, `--step`, `--ids`, `--rules`, `--api-key`, `--settle` (default `500ms`), `--pending` | Runs a flow against the reference backend with a fake Centrifugo server and records the realtime publications it sends into a golden file. |
| `summer parity:upstream` | `--listen` (default `127.0.0.1:8425`), `--ca-dir`, `--out`, `--mode` (default `script`; or `forward`), `--script`, `--vars` | Runs a loopback HTTPS recording proxy with a locally generated CA. The reference backend sends its vendor API calls through it; each call is answered from the `--script` file (or forwarded once to the vendor in `forward` mode) and written, with credentials masked, into an upstream sidecar (`<fixture>.upstream.yaml`) when the command is interrupted. |
A typical port records once against the reference backend and replays against the Go backend on every change: