From 3d09e32ccded0ef80f668848d67e666e92d0ea10 Mon Sep 17 00:00:00 2001 From: Cabbagec Date: Thu, 23 Jul 2026 10:20:08 +0000 Subject: [PATCH] test: cover collision and permission failures --- e2e/README.md | 4 +++- e2e/scripts/complex-matrix.sh | 40 ++++++++++++++++++++++++++++++++++- 2 files changed, 42 insertions(+), 2 deletions(-) diff --git a/e2e/README.md b/e2e/README.md index 7dda515..e2b3b93 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -45,7 +45,9 @@ source staging, and command replay after a target client disconnect. The final hostile cases change the source selection and make an absent target appear after confirmation; each must fail before commit with its exact precondition reason. qBittorrent is also checked for zero downloaded bytes at the replayed -target. +target. Finally, a wrong-size pre-existing target and an unwritable target root +must fail without committing or losing the source; the permission fixture +restores the target root's original mode even on an aborted run. The scheduler pause/resume endpoints exist only on the loopback test adapter; normal control and Telegram orchestration always use the automatic scheduler. diff --git a/e2e/scripts/complex-matrix.sh b/e2e/scripts/complex-matrix.sh index 7d99861..78b8cc4 100755 --- a/e2e/scripts/complex-matrix.sh +++ b/e2e/scripts/complex-matrix.sh @@ -97,6 +97,15 @@ assert_qb() { --selected "$selected" >/dev/null } +archive2_download_mode= +restore_archive2_download_mode() { + if [[ -n "$archive2_download_mode" ]]; then + compose_node archive-2 exec -T qbittorrent \ + chmod "$archive2_download_mode" /downloads >/dev/null 2>&1 || true + fi +} +trap restore_archive2_download_mode EXIT + for node in cache-1 cache-2 archive-1 archive-2; do delete_torrent "$node" "$legacy_bundle_hash" delete_torrent "$node" "$bundle_hash" @@ -377,6 +386,35 @@ control_scenario expect-failure \ assert_qb cache-1 "$cancel_hash" '0' delete_torrent archive-2 "$cancel_hash" +target_collision_job=$(control_scenario create-paused \ + --operation archive \ + --source-client cache-1 \ + --target-client archive-2 \ + --info-hash "$cancel_hash") +compose_node archive-2 exec -T --user 1001:1001 qbittorrent /bin/sh -c \ + 'printf "wrong-size\n" > /downloads/fixture.bin' +control_scenario expect-failure \ + --job-id "$target_collision_job" \ + --contains 'pre-existing destination size does not match' +assert_qb cache-1 "$cancel_hash" '0' +compose_node archive-2 exec -T qbittorrent rm -f /downloads/fixture.bin + +permission_job=$(control_scenario create-paused \ + --operation archive \ + --source-client cache-1 \ + --target-client archive-2 \ + --info-hash "$cancel_hash") +archive2_download_mode=$(compose_node archive-2 exec -T qbittorrent \ + stat -c '%a' /downloads) +compose_node archive-2 exec -T qbittorrent chmod 0555 /downloads +control_scenario expect-failure \ + --job-id "$permission_job" \ + --contains 'Permission denied' +restore_archive2_download_mode +archive2_download_mode= +assert_qb cache-1 "$cancel_hash" '0' + printf '%s\n' \ 'complex matrix passed: selective merges, union eviction, hostile retention,' \ - 'queued/partial cancellation, restart/replay, and hostile state changes' + 'queued/partial cancellation, restart/replay, hostile state changes,' \ + 'target collisions, and permission denial'