a8edcd7450
Merge the S18-S36 scenario ideas into the official peer-cli scenario matrix and add a Docker-backed runner that now exercises S1-S36 with concrete file proofs. The runner creates temporary fixtures under .lanspread-peer-cli, drives JSONL peer containers, checks transferred roots with diff and SHA-256 manifests, and covers startup, discovery, transfer, failure, mutation, concurrency, mesh, lifecycle, and catalog edge cases. The scenarios exposed a few harness/runtime boundary gaps that would otherwise make the contract ambiguous. The peer CLI now rejects self-connects, rejects commands for game IDs outside the receiver catalog, filters unknown remote games from its command/event surface, and reports duplicate active same-game commands as operation-in-progress errors. The peer core also refuses non-catalog download commands before transfer, and PeerGameDB has a unit check that address changes preserve identity and library state. S12 and S28 remain unit-level invariants because the CLI cannot stably race raw serve-gate requests or rebind a live listener without restart. The runner treats those scenarios as covered by just test and checks the expected unit test names appear in the output. Test Plan: - just fmt - python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py - RUSTC_WRAPPER= just test - RUSTC_WRAPPER= just clippy - RUSTC_WRAPPER= just peer-cli-build - just peer-cli-image - python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py - git diff --check Refs: PEER_CLI_SCENARIOS.md S1-S36
282 lines
24 KiB
Markdown
282 lines
24 KiB
Markdown
# 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. |
|
|
| S18 | Mid-download source drop with redundancy | Client downloads a large shared game from two ready peers, then one source is killed after the download has begun. | Failed chunks are retried against the surviving source; the download finishes, no `download-failed` is emitted, and the receiver's files match the source by diff or SHA-256. |
|
|
| S19 | Mid-download sole-source drop | Client downloads a large game from one source, then that source is killed after the download has begun. | The download emits `download-failed`; no committed target `version.ini` remains; any partial payload is not advertised as ready; active operation state clears so a retry is possible. |
|
|
| S20 | Receiver write failure | Client downloads a large game into a constrained `/games` filesystem. | The download fails deterministically, no committed `version.ini` is advertised, and active operation state clears so the peer can retry later. |
|
|
| S21 | Add-game propagation | Two connected peers are running; one peer gains a new catalog game root through a completed download or an external drop. | The other peer receives a library update without reconnecting, and `list-games` shows the new remote game under the existing peer. |
|
|
| S22 | Remove-game propagation | Two connected peers are running; one peer loses a previously advertised game root. | The other peer receives a library update without dropping the peer, and `list-games` no longer shows that remote game. |
|
|
| S23 | Version bump propagation | Two connected peers are running; one peer's ready game root gets a newer `version.ini`. | The other peer receives a library update without reconnecting, and the aggregated row reflects the newer `eti_game_version`. |
|
|
| S24 | Two clients pull from one source | Two empty clients connect to the same source and download the same large game concurrently. | Both downloads finish, both receivers match the source by diff or SHA-256, and the source remains responsive. |
|
|
| S25 | One client downloads two games concurrently | One client connected to a source issues two different `download` commands without waiting for the first to finish. | Both operations may run in parallel; both eventually finish, each game reaches the requested install state, and each transferred root matches its source. |
|
|
| S26 | Same-game duplicate download rejection | A client starts downloading a game, then issues a second `download` command for the same game while the first operation is active. | The second request is rejected deterministically as an operation-in-progress condition; the first download is not corrupted and still reaches its documented final state. |
|
|
| S27 | Self-connect rejection | A peer sends `connect` to its own advertised listener address. | The command fails cleanly, no self-peer entry is created, and the peer remains responsive. |
|
|
| S28 | Address change without identity change | A known peer is rediscovered with the same peer ID and a different listener address while its library is still known. | The peer record updates in place to the new address, the existing library stays attached to that peer ID, and no duplicate peer entry appears. This is covered with a deterministic unit-level check until the CLI can rebind a live listener without restart. |
|
|
| S29 | Empty-library peer participates | A peer with no games connects into the mesh. | Other peers list it as a peer with zero games; it can receive a download, advertise the new game without restart, and become a source. |
|
|
| S30 | 5+ peer mesh aggregation | Five peers advertise partially overlapping catalog games with a mix of unique games, shared games, and differing versions; a sixth client connects to all five. | The client shows one row per game ID, correct ready-source `peer_count`, latest `eti_game_version`, no duplicates, and no self entries. |
|
|
| S31 | Bootstrapped peer becomes source in same session | An empty client downloads a game from a source, the original source shuts down, then a fresh third peer downloads the same game from the bootstrapped client. | The third peer's files match the original source by diff or SHA-256, proving downloaded files become servable without restart. |
|
|
| S32 | Reinstall after uninstall | A downloaded game is installed, uninstalled, then installed again without another download. | `local/` is recreated from preserved root files, no transfer events occur during reinstall, and the game returns to `installed=true`. |
|
|
| S33 | Install after external root mutation | A downloaded game root is externally mutated before `install` is issued. | The CLI fixture installer installs from the current root bytes. The resulting `local/fixture-payload.txt` must match the mutated archive bytes exactly. |
|
|
| S34 | Many-small-files game without `.eti` | A catalog game root contains `version.ini` plus many small regular files and no archive. | Download with `install=false` transfers every file, chunk events are coherent for small files, and source/receiver manifests match exactly. |
|
|
| S35 | Unknown game ID from remote peer | A remote peer advertises a game ID that is not in the receiver's catalog. | The receiver does not list the unknown game as downloadable, download attempts fail deterministically, and no local files are created. |
|
|
| S36 | Latest singleton beats stale majority | Five peers advertise one game; one peer has `20260501`, four peers have `20250101`. | `list-games` reports `eti_game_version=20260501`; all descriptors and chunks come from the singleton latest peer; stale peers contribute zero bytes. |
|
|
|
|
## 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.
|
|
|
|
## Extended Failure And Mutation Contracts
|
|
|
|
Use S18-S36 to pin down operational behavior that is awkward to prove with the
|
|
GUI:
|
|
|
|
- A failed download must not commit the root `version.ini` sentinel. Partial
|
|
payload files may remain, but they must not be advertised as a ready local
|
|
game and must not leave an active operation stuck.
|
|
- Source failure during a redundant download should retry failed chunks against
|
|
another validated source for the same latest-version file.
|
|
- Live local library changes are observable by connected peers through library
|
|
deltas; reconnect is not required for add, remove, or version-bump cases.
|
|
- Same-game operations are single-flight. A duplicate download request while a
|
|
game is already active is rejected instead of starting another writer.
|
|
- Unknown remote game IDs are filtered by the receiver's current catalog and
|
|
are not downloadable.
|
|
|
|
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 Automated Docker Matrix Pass
|
|
|
|
- Runner: `python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
|
|
passed S1-S36 against the current `lanspread-peer-cli:dev` image.
|
|
- S1-S17 rerun highlights: startup, direct connect, aggregation, download,
|
|
install/uninstall, duplicate-source, ambiguous metadata, missing game,
|
|
shutdown cleanup, identity reconnect, serve gates, exact equality, large
|
|
multi-peer chunking, and latest-version selection/conflict all passed. Exact
|
|
transfer scenarios used `diff -r`/SHA-256 manifest checks; S14 chunk totals
|
|
were `58,721,049` and `67,108,864` bytes, balanced within one `32 MiB` chunk.
|
|
- S18-S36 rerun highlights: source-drop, disk-full, live mutation, concurrency,
|
|
duplicate-operation rejection, self-connect rejection, empty-peer sourcing,
|
|
5-peer aggregation, bootstrapped sourcing, reinstall, external mutation,
|
|
many-small-files, unknown catalog filtering, and stale-majority/latest
|
|
singleton cases all passed. File-copy scenarios used diff/manifests or `cmp`
|
|
for the mutated install payload.
|
|
|
|
### 2026-05-18 - Extended Scenario Docker Pass
|
|
|
|
- Runner: `python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py`
|
|
passed for S18-S36 after rebuilding `lanspread-peer-cli:dev` with
|
|
`just peer-cli-image`.
|
|
- S18 redundant source drop: one `alienswarm` source was killed after
|
|
`download-begin`; the client emitted `download-finished`, no
|
|
`download-failed`, and `diff -r`/SHA-256 manifest comparison matched the
|
|
surviving source. Recorded large-file chunk bytes from the surviving source:
|
|
`58,721,049`.
|
|
- S19 sole-source drop: killing the only source after `download-begin` emitted
|
|
`download-failed`; the receiver had no committed `alienswarm/version.ini`, no
|
|
ready local row, and no active operation left.
|
|
- S20 receiver write failure: a client with `/games` constrained to a `32m`
|
|
tmpfs emitted `download-failed`; `/games/alienswarm/version.ini` was absent
|
|
inside the container and active operations were empty.
|
|
- S21-S23 live mutation propagation: a connected peer observed `cod5` added,
|
|
`cod5` removed, and `cnc4` bumped from `20250101` to `20260501` without
|
|
reconnecting or dropping the peer.
|
|
- S24-S25 concurrency: two clients downloaded `alienswarm` from one source at
|
|
the same time and both diffed cleanly; one client downloaded `bfbc2` and
|
|
`cnctw` concurrently and both roots diffed cleanly.
|
|
- S26 duplicate same-game download: the second `alienswarm` download command
|
|
returned `operation already in progress for game alienswarm`; the first
|
|
download still finished and diffed cleanly.
|
|
- S27 self-connect rejection: connecting a peer to its own listener returned
|
|
`cannot connect peer to itself ...`; `list-peers` stayed empty and the peer
|
|
stayed responsive.
|
|
- S28 address-change invariant: `just test` passed and included
|
|
`peer_db::tests::address_update_preserves_peer_identity_and_library`.
|
|
- S29 empty-library peer: an observer first saw the empty peer with zero games;
|
|
after that peer downloaded `alienswarm`, the downloaded root diffed cleanly
|
|
and the observer's peer snapshot for that same peer contained `alienswarm`.
|
|
- S30 5-peer aggregation: a sixth client connected to five peers and aggregated
|
|
six game IDs with expected `peer_count` and latest versions, with no duplicate
|
|
game rows and no self-peer entry.
|
|
- S31 bootstrapped source: after the original source was killed, a third peer
|
|
downloaded `alienswarm` from the bootstrapped client and diffed cleanly
|
|
against the original fixture.
|
|
- S32 reinstall: reinstall after uninstall recreated `local/`, reported
|
|
`installed=true`, and produced no transfer chunk events during reinstall.
|
|
- S33 external root mutation: after mutating the downloaded `bfbc2.eti` inside
|
|
the client container, `install` wrote `local/fixture-payload.txt` that matched
|
|
the mutated archive exactly by `cmp`.
|
|
- S34 many-small-files transfer: a `bf1942` fixture with 20 small regular files
|
|
and no `.eti` downloaded with `install=false`; 21 file chunks were observed
|
|
including `version.ini`, and the receiver diffed cleanly against the source.
|
|
- S35 unknown game ID: a source advertised `mystery-game` via `--fixture`; the
|
|
receiver filtered it out of `list-games`, `download mystery-game` returned
|
|
`game mystery-game is not in the local catalog`, and no local files were
|
|
created.
|
|
- S36 latest singleton: with one peer on `20260501` and four peers on
|
|
`20250101`, the client reported `peer_count=5` and latest `20260501`; only
|
|
the singleton latest peer sent chunks and the final root diffed cleanly.
|
|
|
|
### 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`.
|