test(peer-cli): cover solid streamed installs

NEXT_STEPS item 3 needed solid archive handling to be a deliberate
contract instead of an incidental RAR header attribute. Add a tiny real
solid RAR fixture and S41 to the extended peer-cli scenarios so the
Docker harness proves this path end to end.

The scenario verifies the source archive with container-bundled
`unrar lt`, streams the install with the injected provider, and then
asserts the receiver is installed local-only without a root archive or
root `version.ini`. It also compares local payload SHA-256 hashes against
`unrar p` output and checks the streamed byte count matches the extracted
entries. This keeps the existing one metadata pass plus one sequential
payload pass contract covered for solid archives.

Test Plan:
- just fmt
- just test
- python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py
- python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S41 --build-image
- python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S41
- git diff --check
- git diff --cached --check

Refs: NEXT_STEPS.md item 3
This commit is contained in:
2026-06-07 22:00:21 +02:00
parent c313f7c9ae
commit 0e970dcec7
5 changed files with 138 additions and 11 deletions
+35
View File
@@ -48,6 +48,7 @@ for deterministic local runs; mDNS/macvlan remains an environment smoke path.
| S38 | First-play launch-setting stamping | `fixture-persona/css` ships a real RAR `.eti` whose tree buries a CRLF `SmartSteamEmu.ini` with a stub `PersonaName` line under `engine/bin/win64/steam_settings/`, plus a stub `account_name.txt` and `language.txt` under `profiles/local/`. A peer installs `css` (with `--unrar`), then sends `play css` with a username and language, then `play css` again. | After install the marker `games/css/launch_settings_applied` is absent and the stub files are intact under `local/`. The first `play` returns `already_applied=false` with `account_name_written`, `language_written`, and `persona_name_written` all true; the deep `SmartSteamEmu.ini` `PersonaName` value becomes the username with its `\r\n` ending and sibling lines preserved, `account_name.txt` becomes the username, `language.txt` becomes the passed language, and the marker now exists. A second `play` returns `already_applied=true`, rewrites nothing, and leaves the files untouched even if their values were reset externally. |
| S39 | Streamed install without keeping archive payload | Empty client connects to `fixture-bravo`, then sends `stream-install cnctw`. The source has real RAR `.eti` payload entries under `bin/` and `data/`; the receiver uses the container-bundled `unrar` stream provider. | Client emits `got-game-files`, `download-begin`, streamed `download-chunk-finished`, `download-finished`, `install-begin`, and `install-finished`. Local `cnctw` is `downloaded=false`, `installed=true`, `availability=LocalOnly`; root `version.ini` and `.eti` are absent; `local/bin/cnctw-payload.bin` and `local/data/cnctw-assets.dat` match `unrar p` output by SHA-256. |
| S40 | Streamed install receiver is not a peer source | After S39, a third peer connects only to the streamed-install receiver. | The third peer may see the receiver's local-only summary in peer snapshots, but `list-games` remote aggregation does not expose `cnctw` as downloadable, `peer_count` remains zero/absent, and attempting `download cnctw` fails with no local files created. |
| S41 | Solid archive streamed install | Empty client connects to a peer serving `fixture-solid/cnctw`, whose `.eti` is a real solid RAR archive. The receiver uses the container-bundled `unrar` stream provider. | The fixture is verified as solid with `unrar lt`; streamed install finishes with `downloaded=false`, `installed=true`, `availability=LocalOnly`; root archive and `version.ini` are absent; streamed byte count equals the extracted solid entries; local payload SHA-256 hashes match `unrar p` output. |
## Version-Skew Contract
@@ -112,8 +113,42 @@ Use S38 to pin down how launcher settings are stamped into an installed game:
crate's `launch_settings` unit tests cover the rewrite, line-ending, and
marker logic deterministically.
## Streamed Install Archive Contract
Use S39-S41 to pin down low-disk streamed installs:
- The stream provider performs one archive metadata pass and one payload pass
per `.eti`, then frames entry boundaries for the receiver.
- Non-solid and solid archives both install into `local/` without committing a
root archive or root `version.ini`, so the receiver is installed but not a
downloadable source.
- S41 verifies the fixture is actually solid inside the source container, so
solid handling stays covered by the same Docker harness as the existing
streamed-install scenarios.
## Run Log
### 2026-06-07 - Solid Streamed Install Coverage (S41)
- Code under test added `fixture-solid/cnctw`, a real solid RAR `.eti`, plus
S41 in `run_extended_scenarios.py`.
- Gates before Docker: `just fmt`, `git diff --check`, and
`python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
passed.
- Runner:
`python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S41 --build-image`
passed against the rebuilt `lanspread-peer-cli:dev` image.
- S41 verified the source archive with `unrar lt -cfg-` inside the source
container; the archive reported `Details: RAR 5, solid`.
- The streamed install finished with `downloaded=false`, `installed=true`,
`availability=LocalOnly`, no root `version.ini`, and no root `cnctw.eti`.
- The client received `118` streamed file bytes, matching the extracted solid
entries. Payload SHA-256 hashes matched `unrar p` output:
`88764c9a6c9b5b846b4323cf7725cb7fd70766ddd7fba4168332804a839fa193`
(`bin/cnctw-solid-payload.bin`) and
`44afc308269b2381b7c707a056dd8d9d393274108ac4d880237fa6772c861d7a`
(`data/cnctw-solid-assets.dat`).
### 2026-06-07 - Streamed Install Prototype (S39-S40)
- Code under test added `stream-install` to `lanspread-peer-cli`, a peer