mirror of
https://github.com/nimbold/Firelink.git
synced 2026-09-01 21:48:00 +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'
|
||||
working-directory: src-tauri
|
||||
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
|
||||
if: runner.os == 'Windows'
|
||||
working-directory: src-tauri
|
||||
|
||||
@@ -14,8 +14,8 @@
|
||||
},
|
||||
"ffmpeg": {
|
||||
"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",
|
||||
"sha256": "410c82fc0a7d713fd83412138271b8559faa8cf8a74a75eaf541dfca75ea4590"
|
||||
"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": "827762aba79aa27b397a00607b5e2afe717d4d83264aa753085fb7335ea932f0"
|
||||
},
|
||||
"aria2c": {
|
||||
"version": "1.37.0",
|
||||
@@ -36,8 +36,8 @@
|
||||
},
|
||||
"ffmpeg": {
|
||||
"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",
|
||||
"sha256": "1135ba005de82cecce7ceba9d653c47374af8a657a0eae93571214c6a5169850"
|
||||
"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": "2b5156f14340e2abf83888a925b9fd8bd17270368680b7a5107d8eb4c7169c57"
|
||||
},
|
||||
"aria2c": {
|
||||
"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,
|
||||
atomic-storage, canonical-cache, and web-seed normalization targets, while
|
||||
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