test(peer-cli): expand streamed install edge coverage

NEXT_STEPS item 6 called for the remaining streamed-install edge cases to
be covered in the peer-cli matrix. Add S43-S47 for already-installed
rejection, corrupt archive rollback, sender disconnect, receiver cancel,
and sorted multi-archive streaming.

The receiver-cancel scenario needs the harness to drive the same runtime
path as the GUI, so `lanspread-peer-cli` now accepts a narrow
`cancel-download` command that forwards to `PeerCommand::CancelDownload`.
A parser test covers the new JSONL command shape.

Add `fixture-multi/cnctw`, a tiny two-archive RAR fixture. S47 uses it to
prove streamed installs process root `.eti` archives in sorted order and
commit only extracted `local/` payloads, not the root archives or
`version.ini` sentinel.

Test Plan:
- just fmt
- python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py
- python3 crates/lanspread-peer-cli/scripts/run_extended_scenarios.py S43 S44 S45 S46 S47 --build-image
- just test
- just clippy
- git diff --check
- git diff --cached --check

Refs: NEXT_STEPS.md item 6
This commit is contained in:
2026-06-07 22:26:49 +02:00
parent 88bfaeb04a
commit 9288fda037
8 changed files with 286 additions and 10 deletions
+20
View File
@@ -36,6 +36,9 @@ pub enum CliCommand {
StreamInstall {
game_id: String,
},
CancelDownload {
game_id: String,
},
Install {
game_id: String,
},
@@ -67,6 +70,7 @@ impl CliCommand {
Self::SetGameDir { .. } => "set-game-dir",
Self::Download { .. } => "download",
Self::StreamInstall { .. } => "stream-install",
Self::CancelDownload { .. } => "cancel-download",
Self::Install { .. } => "install",
Self::Uninstall { .. } => "uninstall",
Self::Play { .. } => "play",
@@ -108,6 +112,9 @@ pub fn parse_command_value(value: &Value) -> eyre::Result<CommandEnvelope> {
"stream-install" => CliCommand::StreamInstall {
game_id: game_id(object)?,
},
"cancel-download" => CliCommand::CancelDownload {
game_id: game_id(object)?,
},
"install" => CliCommand::Install {
game_id: game_id(object)?,
},
@@ -364,6 +371,19 @@ mod tests {
);
}
#[test]
fn parses_cancel_download_command() {
let parsed = parse_command_line(r#"{"cmd":"cancel-download","game_id":"cnctw"}"#)
.expect("command should parse");
assert_eq!(
parsed.command,
CliCommand::CancelDownload {
game_id: "cnctw".to_string(),
}
);
}
#[tokio::test]
async fn fixture_unpacker_creates_install_payload() {
let temp = TempDir::new("lanspread-peer-cli-fixture");
+7
View File
@@ -267,6 +267,13 @@ async fn handle_command(
})?;
Ok(json!({"queued": true, "game_id": game_id}))
}
CliCommand::CancelDownload { game_id } => {
ensure_catalog_game(shared, game_id).await?;
sender.send(PeerCommand::CancelDownload {
id: game_id.clone(),
})?;
Ok(json!({"queued": true, "game_id": game_id}))
}
CliCommand::Install { game_id } => {
ensure_catalog_game(shared, game_id).await?;
ensure_no_active_operation(shared, game_id).await?;