mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-02 05:57:59 +00:00
fix(ci): cover Windows headless production contracts
- execute queue, retry, normalization, and credential-boundary contracts without the broken Tauri mock runtime\n- refresh target-specific FFmpeg source locks after provider URL and digest drift\n- document the remaining compile-only Windows GUI test limitation
This commit is contained in:
@@ -98,6 +98,10 @@ jobs:
|
|||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
working-directory: src-tauri
|
working-directory: src-tauri
|
||||||
run: cargo test --lib --no-run --target ${{ matrix.target }}
|
run: cargo test --lib --no-run --target ${{ matrix.target }}
|
||||||
|
- name: Run Windows headless production contracts
|
||||||
|
if: runner.os == 'Windows'
|
||||||
|
working-directory: src-tauri
|
||||||
|
run: cargo test --test production_contract --target ${{ matrix.target }} -- --nocapture
|
||||||
- name: Verify Windows atomic Torrent storage
|
- name: Verify Windows atomic Torrent storage
|
||||||
if: runner.os == 'Windows'
|
if: runner.os == 'Windows'
|
||||||
working-directory: src-tauri
|
working-directory: src-tauri
|
||||||
|
|||||||
@@ -14,8 +14,8 @@
|
|||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"version": "8.1.2-44-g7c533d0f86",
|
"version": "8.1.2-44-g7c533d0f86",
|
||||||
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-08-20-13-45/ffmpeg-n8.1.2-44-g7c533d0f86-win64-gpl-8.1.zip",
|
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-08-21-13-40/ffmpeg-n8.1.2-44-g7c533d0f86-win64-gpl-8.1.zip",
|
||||||
"sha256": "410c82fc0a7d713fd83412138271b8559faa8cf8a74a75eaf541dfca75ea4590"
|
"sha256": "827762aba79aa27b397a00607b5e2afe717d4d83264aa753085fb7335ea932f0"
|
||||||
},
|
},
|
||||||
"aria2c": {
|
"aria2c": {
|
||||||
"version": "1.37.0",
|
"version": "1.37.0",
|
||||||
@@ -36,8 +36,8 @@
|
|||||||
},
|
},
|
||||||
"ffmpeg": {
|
"ffmpeg": {
|
||||||
"version": "8.1.2-44-g7c533d0f86",
|
"version": "8.1.2-44-g7c533d0f86",
|
||||||
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-08-20-13-45/ffmpeg-n8.1.2-44-g7c533d0f86-linux64-gpl-8.1.tar.xz",
|
"url": "https://github.com/BtbN/FFmpeg-Builds/releases/download/autobuild-2026-08-21-13-40/ffmpeg-n8.1.2-44-g7c533d0f86-linux64-gpl-8.1.tar.xz",
|
||||||
"sha256": "1135ba005de82cecce7ceba9d653c47374af8a657a0eae93571214c6a5169850"
|
"sha256": "2b5156f14340e2abf83888a925b9fd8bd17270368680b7a5107d8eb4c7169c57"
|
||||||
},
|
},
|
||||||
"aria2c": {
|
"aria2c": {
|
||||||
"version": "1.37.0",
|
"version": "1.37.0",
|
||||||
|
|||||||
@@ -54,4 +54,7 @@ Native CI runs this failure-path smoke after staging the target-specific
|
|||||||
bundled engines on macOS, Windows, and Linux. Windows executes the Torrent RPC,
|
bundled engines on macOS, Windows, and Linux. Windows executes the Torrent RPC,
|
||||||
atomic-storage, canonical-cache, and web-seed normalization targets, while
|
atomic-storage, canonical-cache, and web-seed normalization targets, while
|
||||||
compiling (but not executing) the queue-manager and library test binaries;
|
compiling (but not executing) the queue-manager and library test binaries;
|
||||||
the Tauri mock harness exits before running on the Windows runner.
|
the Tauri mock harness exits before running on the Windows runner. The
|
||||||
|
headless `production_contract` target still executes queue admission,
|
||||||
|
normalization, credential-boundary, and retry contracts on Windows without
|
||||||
|
constructing a Tauri mock application.
|
||||||
|
|||||||
@@ -0,0 +1,118 @@
|
|||||||
|
use firelink_lib::ipc::{TorrentFile, TorrentWebSeed};
|
||||||
|
use firelink_lib::queue::{
|
||||||
|
clamp_download_connections, normalize_aria2_disk_cache, normalize_torrent_bind_address,
|
||||||
|
normalize_torrent_dht_message_timeout, normalize_torrent_file_allocation,
|
||||||
|
normalize_torrent_web_seeds, EnqueueItem, QueueManager, SpawnPayload,
|
||||||
|
};
|
||||||
|
use firelink_lib::retry::{backoff_for, is_permanent_network_error, network_error_class};
|
||||||
|
use std::time::Duration;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn headless_queue_contracts_reject_unsafe_inputs_and_preserve_bounds() {
|
||||||
|
assert_eq!(clamp_download_connections(0), 1);
|
||||||
|
assert_eq!(clamp_download_connections(99), 16);
|
||||||
|
assert_eq!(normalize_aria2_disk_cache(Some(" 32m ")).unwrap(), "32M");
|
||||||
|
assert!(normalize_aria2_disk_cache(Some("0K")).is_err());
|
||||||
|
assert_eq!(
|
||||||
|
normalize_torrent_bind_address(Some(" 2001:db8::1 ")).unwrap(),
|
||||||
|
Some("2001:db8::1".to_string())
|
||||||
|
);
|
||||||
|
assert!(normalize_torrent_bind_address(Some("not-an-ip")).is_err());
|
||||||
|
assert_eq!(normalize_torrent_dht_message_timeout(60).unwrap(), 60);
|
||||||
|
assert!(normalize_torrent_dht_message_timeout(61).is_err());
|
||||||
|
assert_eq!(normalize_torrent_file_allocation(None).unwrap(), "prealloc");
|
||||||
|
assert!(normalize_torrent_file_allocation(Some("sparse")).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn headless_torrent_web_seed_contract_deduplicates_and_expands_per_file() {
|
||||||
|
let files = vec![
|
||||||
|
TorrentFile {
|
||||||
|
index: 1,
|
||||||
|
path: "folder/one.bin".to_string(),
|
||||||
|
length: 10,
|
||||||
|
},
|
||||||
|
TorrentFile {
|
||||||
|
index: 2,
|
||||||
|
path: "two.bin".to_string(),
|
||||||
|
length: 20,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let seeds = vec![
|
||||||
|
TorrentWebSeed {
|
||||||
|
file_index: 1,
|
||||||
|
uri: "https://mirror.example/base".to_string(),
|
||||||
|
},
|
||||||
|
TorrentWebSeed {
|
||||||
|
file_index: 1,
|
||||||
|
uri: "https://mirror.example/base".to_string(),
|
||||||
|
},
|
||||||
|
];
|
||||||
|
let normalized = normalize_torrent_web_seeds(Some(&seeds), &files).unwrap();
|
||||||
|
assert_eq!(normalized.len(), 1);
|
||||||
|
let expanded = firelink_lib::queue::expand_torrent_web_seeds(&normalized, &files).unwrap();
|
||||||
|
assert_eq!(
|
||||||
|
expanded,
|
||||||
|
vec![(1, "https://mirror.example/base/folder/one.bin".to_string())]
|
||||||
|
);
|
||||||
|
|
||||||
|
let credentialed = [TorrentWebSeed {
|
||||||
|
file_index: 1,
|
||||||
|
uri: "https://user:pass@mirror.example/file".to_string(),
|
||||||
|
}];
|
||||||
|
assert!(normalize_torrent_web_seeds(Some(&credentialed), &files).is_err());
|
||||||
|
let unknown_file = [TorrentWebSeed {
|
||||||
|
file_index: 3,
|
||||||
|
uri: "https://mirror.example/file".to_string(),
|
||||||
|
}];
|
||||||
|
assert!(normalize_torrent_web_seeds(Some(&unknown_file), &files).is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn headless_enqueue_contract_strips_torrent_credentials_before_task_creation() {
|
||||||
|
let item = EnqueueItem {
|
||||||
|
id: "torrent".to_string(),
|
||||||
|
queue_id: "main".to_string(),
|
||||||
|
url: "https://example.test/file.torrent".to_string(),
|
||||||
|
destination: "/tmp".to_string(),
|
||||||
|
filename: "file.torrent".to_string(),
|
||||||
|
username: Some("user".to_string()),
|
||||||
|
password: Some("secret".to_string()),
|
||||||
|
headers: Some("Cookie: session=secret".to_string()),
|
||||||
|
cookies: Some("session=secret".to_string()),
|
||||||
|
is_torrent: Some(true),
|
||||||
|
..EnqueueItem::default()
|
||||||
|
};
|
||||||
|
let task = item.into_task();
|
||||||
|
assert!(task.payload.username.is_none());
|
||||||
|
assert!(task.payload.password.is_none());
|
||||||
|
assert!(task.payload.headers.is_none());
|
||||||
|
assert!(task.payload.cookies.is_none());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn headless_queue_lifecycle_eligibility_and_retry_contracts_hold() {
|
||||||
|
assert!(QueueManager::<tauri::Wry>::aria2_allocation_phase_eligible(
|
||||||
|
&SpawnPayload::default()
|
||||||
|
));
|
||||||
|
assert!(
|
||||||
|
!QueueManager::<tauri::Wry>::aria2_allocation_phase_eligible(&SpawnPayload {
|
||||||
|
is_media: true,
|
||||||
|
..SpawnPayload::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert!(
|
||||||
|
!QueueManager::<tauri::Wry>::aria2_allocation_phase_eligible(&SpawnPayload {
|
||||||
|
is_torrent: true,
|
||||||
|
torrent_file_allocation: Some("none".to_string()),
|
||||||
|
..SpawnPayload::default()
|
||||||
|
})
|
||||||
|
);
|
||||||
|
assert_eq!(backoff_for(0), Duration::from_secs(2));
|
||||||
|
assert_eq!(backoff_for(usize::MAX), Duration::from_secs(10));
|
||||||
|
assert_eq!(
|
||||||
|
network_error_class("HTTP/1.1 503 Service Unavailable"),
|
||||||
|
"http"
|
||||||
|
);
|
||||||
|
assert!(is_permanent_network_error("HTTP 403 Forbidden"));
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user