Files
lanspread/PEER_CLI_SCENARIOS.md
T
ddidderr 8b3aefd2db test(peer-cli): document full manual scenario pass
Record the May 18 manual Docker pass over PEER_CLI_SCENARIOS.md so the
scenario matrix has current evidence for every row. The run log now covers the
clean direct-connect and aggregation rerun, exact diff checks for downloaded
files, custom renamed-RAR fixtures for conflict and version-skew cases, and the
latest-only transfer behavior for S15-S17.

S12 remains verified by unit tests instead of a CLI race because the raw serving
gates are below the peer-cli command surface. The run log names the exact tests
that cover the non-catalog, missing-sentinel, active-operation, and local-path
serving gates.

Test Plan:
- just peer-cli-image
- RUSTC_WRAPPER= just peer-cli-build
- manual Docker peer-cli runs for S1-S17 using JSONL stdin commands
- diff -r transferred game directories against source fixture directories
- just fmt
- RUSTC_WRAPPER= just test
- RUSTC_WRAPPER= just clippy
- git diff --check

Refs: PEER_CLI_SCENARIOS.md
2026-05-18 22:43:15 +02:00

14 KiB

Peer CLI P2P Scenarios

This matrix tracks the headless peer-to-peer contract exercised through lanspread-peer-cli. It intentionally avoids the GUI and uses direct connect for deterministic local runs; mDNS/macvlan remains an environment smoke path.

Scenario Matrix

ID Scenario Setup Expected result
S1 Startup scan Start one peer with fixture-alpha. Peer emits local-peer-ready and local-library-changed; catalog fixture games are downloaded=true, installed=false, availability=Ready.
S2 Direct connect handshake Start alpha and bravo, send alpha connect to bravo's ready address. Both peers record one remote peer, no self-peer entry appears, and each peer receives the other's library.
S3 Remote aggregation Empty client connects to alpha and bravo. list-games shows remote-only games once; shared ggoo has peer_count=2, unique games have peer_count=1.
S4 Single-source download, no install Empty client connected to bravo downloads bfbc2 with install=false. Client emits got-game-files, download-begin, download-finished, then local bfbc2 is downloaded=true, installed=false; root files exist and local/ does not.
S5 Auto-install download Empty client connected to bravo downloads cnctw with default install. Download finishes, install begins and finishes, and local cnctw is downloaded=true, installed=true with local/fixture-payload.txt.
S6 Manual install and uninstall After S4, client sends install bfbc2, then uninstall bfbc2. Install marks bfbc2 installed and creates local/; uninstall removes local/ while preserving downloaded root files.
S7 Duplicate-source majority download Empty client connects to alpha and bravo, then downloads shared ggoo. Metadata from both peers validates by majority/plurality, download completes once, and installed state matches the install flag.
S8 Ambiguous metadata rejection Two peers advertise the same game/version with conflicting file sizes. Download fails with a download-failed event; no committed version.ini is left for the target game.
S9 Missing game Client asks for a game none of its peers can serve. CLI reports a deterministic command failure and emits no-peers-have-game; no local files are created.
S10 Shutdown and goodbye cleanup Alpha and bravo are connected, then bravo shuts down. Alpha receives peer loss/removal and remote games from bravo disappear.
S11 Same identity reconnect Bravo restarts with the same state dir but a new port, then alpha connects to the new address. Alpha has one bravo peer entry with the updated address, not duplicate identities.
S12 Transfer serving gates A peer has a non-catalog, missing-sentinel, active-operation, or local/ path request. The serving peer declines metadata/data; covered by unit tests where timing is too small for a stable CLI race test.
S13 Exact transferred-file equality Repeat small and large downloads, then compare every transferred regular file against its source with SHA-256 manifests. Source and receiver manifests match exactly for each transferred file; no extra or missing files appear in the downloaded game root.
S14 Large multi-peer chunked download fixture-alpha/alienswarm contains a renamed RAR .eti larger than 100 MB. A second peer downloads it, then a third peer downloads alienswarm from both peers. The third peer's downloaded files match the source by SHA-256; download-chunk-finished events show the large .eti chunks coming from both peers with byte counts balanced within one chunk.
S15 Three-way version skew Three peers advertise the same catalog game ID. Peer A has version.ini=20250101, peer B has version.ini=20250201, and peer C has version.ini=20250301; each version has distinguishable file contents. An empty client connects to all three and downloads the game with install=false. list-games shows one row for the game with peer_count=3 and eti_game_version=20250301. The got-game-files descriptor set and transfer source are peer C's newest version only; no chunks come from A or B. The receiver's version.ini and SHA-256 manifest match C exactly.
S16 Latest-version fanout with stale peers present Peer A has an older version of a game. Peers B and C both advertise the same newest version with matching file manifests; use a large file when proving chunk split. The aggregated row still counts all ready peers, but eligible transfer peers are only B and C. Large-file chunks may split between B and C; peer A contributes no manifest majority vote and no file chunks.
S17 Latest-version conflict rejection Peer A has an older version. Peers B and C both advertise the newest version, but their latest-version file sizes conflict. Validation considers only the latest-version peers, so A cannot rescue the majority. The download fails with download-failed, and no committed target version.ini remains.

Version-Skew Contract

Use S15-S17 to pin down what "newer" means when several peers have the same game ID:

  • Version comparison uses the eight-digit version.ini string, so use sortable YYYYMMDD values in manual fixtures.
  • list-games aggregates by game ID. The game appears once; peer_count counts all ready peers with that ID, including peers that only have older versions.
  • The aggregated eti_game_version must be the newest ready version.
  • The descriptor set emitted to the download path, file-size validation, and transfer planning are latest-only. Older-version peers may be queried by a generic detail request, but their descriptors must not supply download descriptors, majority votes, or chunks once a newer version exists.
  • If exactly one peer has the latest version, that peer is the only transfer source. If several peers tie on the latest version, validation and chunk fanout happen among that latest-version set only.
  • Capture proof with the list-games row, got-game-files descriptors, download-chunk-finished source addresses, and source/receiver SHA-256 manifests.

For a manual run, prefer a catalog game ID already served by the fixture lab, such as cnc4, then create temporary just peer-cli-run game roots with different version.ini contents. The existing alpha/bravo/charlie fixtures cover duplicate-source and shared-game cases, but not the three-version skew until a dedicated fixture or temporary games root is prepared.

Run Log

2026-05-18 - Full Matrix Manual Docker Pass

  • Build/setup: just peer-cli-image passed. Local just peer-cli-build needed RUSTC_WRAPPER= because the host kache wrapper failed with a read-only filesystem error; RUSTC_WRAPPER= just peer-cli-build passed.
  • Temporary skew/conflict fixtures were created under the ignored .lanspread-peer-cli/full-fixtures/ tree using rar a -idq -m0 against /dev/urandom payloads and then renaming the archives to .eti. find .lanspread-peer-cli/full-fixtures -name '*.eti' -exec unrar t -idq {} \; passed.
  • S1 startup scan: just peer-cli-alpha emitted cli-started, local-library-changed, and local-peer-ready; alienswarm, bf1942, and ggoo were downloaded=true, installed=false, availability=Ready.
  • S2 clean direct connect: with only alpha and bravo running, alpha connected to bravo at 10.66.0.3:42776; wait-peers returned peer_count=1, and list-peers showed exactly one bravo peer with four games.
  • S3 clean remote aggregation: an empty clean-s3-client saw exactly alpha and bravo. list-games showed ggoo peer_count=2; alienswarm, bf1942, bfbc2, cnc4, and cnctw each had peer_count=1.
  • S4 single-source no-install: full-empty-client downloaded bfbc2 from bravo with install=false. Events included got-game-files, download-begin, download-finished, and local installed=false. Host verification: diff -r crates/lanspread-peer-cli/fixtures/fixture-bravo/bfbc2 .lanspread-peer-cli/full-empty-client/games/bfbc2 passed and local/ was absent.
  • S5 auto-install: full-empty-client downloaded cnctw with default install. Events included download finish, install-begin, and install-finished; local/fixture-payload.txt existed. Host verification diffed the downloaded files against fixture-bravo/cnctw excluding local/ and .lanspread.json.
  • S6 manual install/uninstall: after S4, install bfbc2 created local/ and marked installed=true; uninstall bfbc2 removed local/ and preserved the downloaded root files. Host verification diffed the preserved files against fixture-bravo/bfbc2 excluding .lanspread.json.
  • S7 duplicate-source download: full-empty-client downloaded shared ggoo from alpha/bravo with install=false. Chunk events used alpha for version.ini and bravo for ggoo.eti; host diff -r matched both fixture-alpha/ggoo and fixture-bravo/ggoo.
  • S8 ambiguous metadata rejection: full-s8-a and full-s8-b both advertised ggoo version 20260101 but with different .eti sizes (1,048,746 and 2,097,323 bytes). The client saw peer_count=2, then download ggoo emitted download-failed; no target ggoo/version.ini was committed.
  • S9 missing game: download does-not-exist emitted no-peers-have-game and returned a command error; .lanspread-peer-cli/full-empty-client/games had no does-not-exist directory.
  • S10 shutdown cleanup: alpha saw bravo before shutdown with one remote peer and bravo-only remote games. After bravo shutdown, alpha emitted peer-lost; list-peers returned [] and list-games returned an empty remote list.
  • S11 same identity reconnect: restarting bravo reused peer ID 019e347d901e70c19adf5b9fd313fce4 at new address 10.66.0.3:41764. Alpha list-peers showed exactly one bravo entry at the new address.
  • S12 transfer serving gates: this remains covered by unit tests because the CLI cannot stably race raw transfer requests against non-catalog, missing sentinel, active-operation, and local/ path states. RUSTC_WRAPPER= just test passed, including local_download_available_gates_on_catalog_operation_and_sentinel, get_game_response_respects_serve_gates, file_transfer_dispatch_respects_serve_gates, and local_relative_paths_are_never_transferable.
  • S13 exact transferred-file equality: the S4 small transfer and S14 large transfer both passed host diff -r against the original source game directories, proving exact file equality beyond event flow.
  • S14 large multi-peer chunked download: full-empty-client first downloaded alienswarm from alpha and diffed cleanly against fixture-alpha/alienswarm. A fresh full-s14-client then saw alienswarm peer_count=2 and downloaded from both alpha and full-empty-client. Large .eti chunk totals were 67,108,864 bytes from alpha and 58,721,049 bytes from the staged peer, balanced within one 32 MiB chunk. Final host diff -r against fixture-alpha/alienswarm passed.
  • S15 three-way version skew: peers A/B/C advertised cnc4 versions 20250101, 20250201, and 20250301. The client saw one row with peer_count=3 and eti_game_version=20250301; all chunks came only from C at 10.66.0.4:60290. Host diff -r against C passed.
  • S16 latest-version fanout with stale peer present: A advertised stale 20250101; B/C both advertised latest 20250301 with a 134,217,906 byte .eti. The client saw peer_count=3; chunks came only from B/C (67,108,873 and 67,109,042 bytes respectively), with stale A contributing zero. Host diff -r matched both B and C.
  • S17 latest-version conflict rejection: A advertised stale 20250101; B/C both advertised latest 20250301 but with conflicting .eti sizes (1,048,748 and 2,097,325 bytes). The client saw peer_count=3 and latest 20250301, then download cnc4 emitted download-failed; no target cnc4/version.ini was committed.
  • Gates after manual runs: just fmt, RUSTC_WRAPPER= just test, and RUSTC_WRAPPER= just clippy passed.

2026-05-17 - Exact Transfer And Large Multi-Peer Chunking

  • Fixture update: fixture-alpha/alienswarm/alienswarm.eti was rebuilt with rar a -idq -m0 from three random 40 MiB payload files, then renamed to .eti. Final archive size: 125,829,913 bytes. unrar t -idq passed.
  • Gates before manual runs: just fmt, just test, just peer-cli-build, just clippy, and just peer-cli-image passed.
  • S13 small exact transfer: deep-small-client downloaded bfbc2 from fixture-bravo with install=false. SHA-256 manifests matched exactly: bfbc2/bfbc2.eti f7accef0833f29481acdeaac58261bc4fc23ebb58b7197049024d354f60daabc; bfbc2/version.ini f3d94f70edcebbbc7d8ce38fdf076412fb95114ce1ecf071b26c9c2f93586372.
  • S13 large exact transfer: deep-stage-b downloaded alienswarm from fixture-alpha with install=false. SHA-256 manifests matched exactly: alienswarm/alienswarm.eti 8a4fb1fd458e731affb175134b7b99efc8d8a5eda80e978ba81f721d01aecc43; alienswarm/notes.txt 3832bcb7057a4453981e975d2d2d528bfd9a26671423352f4a8527362d5b9810; alienswarm/version.ini 8dfdc51d4dbfb06015b41a85a5f5d47f44144139e4a12db2b17eb040773082a3.
  • S14 multi-peer setup: deep-stage-c connected to alpha (10.66.0.3:53514) and deep-stage-b (10.66.0.2:58491). list-games showed alienswarm with peer_count=2 before the download.
  • S14 chunk-source evidence for alienswarm/alienswarm.eti: deep-stage-c received chunks from deep-stage-b at offsets 0 and 67,108,864 (67,108,864 bytes total) and from alpha at offsets 33,554,432 and 100,663,296 (58,721,049 bytes total). The source-byte difference was 8,387,815 bytes, below one 32 MiB chunk.
  • S14 final exactness: deep-stage-c's alienswarm SHA-256 manifest matched fixture-alpha exactly for alienswarm.eti, notes.txt, and version.ini.