fix(torrents): preserve per-file web-seed ownership

- use Aria2 per-file URI state for safe Torrent seed lifecycle updates
- preserve embedded and legacy web-seed baselines during explicit changes
- harden atomic replacement and retained cache cleanup across platforms
- execute Windows Torrent cache and web-seed regressions in CI
This commit is contained in:
NimBold
2026-08-19 12:11:09 +03:30
parent 78e9c9b80f
commit 9418aa5564
5 changed files with 652 additions and 111 deletions
+42 -4
View File
@@ -1,11 +1,14 @@
use firelink_lib::atomic_write_replace;
use std::fs;
use tempfile::tempdir;
#[cfg(target_os = "macos")]
use tempfile::tempdir_in;
#[tokio::test]
async fn atomic_replacement_replaces_existing_file_repeatedly() {
let directory = tempdir().expect("temporary directory should be created");
let destination = directory.path().join("download.torrent");
let root = fs::canonicalize(directory.path()).expect("temporary directory should canonicalize");
let destination = root.join("download.torrent");
for value in [
b"reserved".as_slice(),
@@ -29,8 +32,9 @@ async fn atomic_replacement_rejects_symbolic_link_destinations() {
use std::os::unix::fs::symlink;
let directory = tempdir().expect("temporary directory should be created");
let target = directory.path().join("target");
let destination = directory.path().join("download.torrent");
let root = fs::canonicalize(directory.path()).expect("temporary directory should canonicalize");
let target = root.join("target");
let destination = root.join("download.torrent");
fs::write(&target, b"protected").expect("target should be written");
symlink(&target, &destination).expect("symbolic link should be created");
@@ -43,10 +47,44 @@ async fn atomic_replacement_rejects_symbolic_link_destinations() {
);
}
#[cfg(unix)]
#[tokio::test]
async fn atomic_replacement_rejects_symbolic_link_parent_components() {
use std::os::unix::fs::symlink;
let directory = tempdir().expect("temporary directory should be created");
let root = fs::canonicalize(directory.path()).expect("temporary directory should canonicalize");
let target_directory = root.join("target");
let linked_directory = root.join("linked");
let target_child = target_directory.join("child");
std::fs::create_dir(&target_directory).expect("target directory should be created");
std::fs::create_dir(&target_child).expect("target child directory should be created");
symlink(&target_directory, &linked_directory).expect("symbolic link should be created");
let destination = linked_directory.join("child/download.torrent");
assert!(atomic_write_replace(&destination, b"replacement")
.await
.is_err());
assert!(!target_child.join("download.torrent").exists());
}
#[cfg(target_os = "macos")]
#[tokio::test]
async fn atomic_replacement_accepts_macos_system_path_aliases() {
let directory = tempdir_in("/tmp").expect("temporary directory should be created");
let destination = directory.path().join("download.torrent");
atomic_write_replace(&destination, b"replacement")
.await
.expect("the fixed macOS /tmp alias should be accepted");
assert_eq!(fs::read(&destination).unwrap(), b"replacement");
}
#[tokio::test]
async fn atomic_replacement_recovers_after_non_regular_destination_failure() {
let directory = tempdir().expect("temporary directory should be created");
let destination = directory.path().join("download.torrent");
let root = fs::canonicalize(directory.path()).expect("temporary directory should canonicalize");
let destination = root.join("download.torrent");
fs::create_dir(&destination).expect("non-regular destination should be created");
assert!(atomic_write_replace(&destination, b"replacement")