use super::*; #[expect( clippy::too_many_lines, reason = "segment-plan regression fixture keeps response bodies, ranges, and request assertions adjacent" )] #[test] fn execute_runtime_uses_segment_plan_after_bootstrap_partial_response() { let downloader = SequencedHttpDownloader::new(vec![ Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "4".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 0-3/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"1234".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 0, end_inclusive: 3, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "4".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 4-7/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"5678".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 4, end_inclusive: 7, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "2".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 8-9/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"90".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 8, end_inclusive: 9, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), ]); let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-explicit-segments-test"); let _ = fs::create_dir_all(&temp_dir); let config_path = temp_dir.join("aria2.conf"); fs::write( &config_path, "max-tries=3\nsplit=3\nmin-split-size=4\npiece-length=4\n", ) .expect("config should write"); let report = execute_runtime_with_downloader( Invocation::Run { config_path: Some(config_path.clone()), uris: vec!["http://example.com/segment-plan.bin".to_owned()], }, &downloader, ) .expect("runtime should execute via explicit segments"); assert_eq!(report.completed_download_count, 1); assert_eq!(report.first_status.as_deref(), Some("complete")); assert_eq!(report.first_completed_length, Some(10)); let recorded = downloader.recorded_requests(); let [bootstrap, first_followup, second_followup] = recorded.as_slice() else { panic!("expected exactly three recorded requests"); }; assert_eq!(bootstrap.request.range, None); let mut followup_ranges = [first_followup, second_followup] .iter() .map(|task| task.request.range) .collect::>(); followup_ranges.sort_by_key(|range| range.as_ref().map(|range| range.start)); assert_eq!( followup_ranges, vec![ Some(aria2_rust_pro_protocol::RangeSpec { start: 4, end_inclusive: Some(9), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }), Some(aria2_rust_pro_protocol::RangeSpec { start: 8, end_inclusive: Some(9), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }), ] ); let _ = fs::remove_file(config_path); } #[expect( clippy::too_many_lines, reason = "initial range-probe regression keeps bootstrap and follow-up range fixtures adjacent" )] #[test] fn execute_runtime_uses_initial_range_probe_when_split_budget_allows_parallel_segments() { let downloader = SequencedHttpDownloader::new(vec![ Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "4".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 0-3/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"1234".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 0, end_inclusive: 3, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "4".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 4-7/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"5678".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 4, end_inclusive: 7, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "2".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 8-9/10".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"90".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 8, end_inclusive: 9, total_size: Some(10), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), ]); let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-segment-probe-test"); let _ = fs::create_dir_all(&temp_dir); let config_path = temp_dir.join("aria2.conf"); fs::write( &config_path, "split=3\nmax-connection-per-server=3\nmin-split-size=4\npiece-length=4\n", ) .expect("config should write"); let report = execute_runtime_with_downloader( Invocation::Run { config_path: Some(config_path.clone()), uris: vec!["http://example.com/probe-plan.bin".to_owned()], }, &downloader, ) .expect("runtime should execute via initial range probe"); assert_eq!(report.completed_download_count, 1); assert_eq!(report.first_status.as_deref(), Some("complete")); assert_eq!(report.first_completed_length, Some(10)); let recorded = downloader.recorded_requests(); let [probe, first_followup, second_followup] = recorded.as_slice() else { panic!("expected exactly three recorded requests"); }; assert_eq!( probe.request.range, Some(aria2_rust_pro_protocol::RangeSpec { start: 0, end_inclusive: Some(3), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }) ); let mut followup_ranges = [first_followup, second_followup] .iter() .map(|task| task.request.range) .collect::>(); followup_ranges.sort_by_key(|range| range.as_ref().map(|range| range.start)); assert_eq!( followup_ranges, vec![ Some(aria2_rust_pro_protocol::RangeSpec { start: 4, end_inclusive: Some(7), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }), Some(aria2_rust_pro_protocol::RangeSpec { start: 8, end_inclusive: Some(9), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }), ] ); let _ = fs::remove_file(config_path); } #[expect( clippy::too_many_lines, reason = "tiny-tail regression keeps the coalesced probe fixture next to the expected request ranges" )] #[test] fn execute_runtime_coalesces_tiny_followup_tail_after_initial_probe() { let downloader = SequencedHttpDownloader::new(vec![ Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "32".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 0-31/36".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"1234567890abcdefghijklmnopqrstuv".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 0, end_inclusive: 31, total_size: Some(36), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), Ok(HttpResponseModel { status: 206, reason: "Partial Content".to_owned(), version: HttpVersion::Http11, headers: HttpResponseHeaders { headers: vec![ aria2_rust_pro_protocol::HttpHeader { name: "content-length".to_owned(), value: "4".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, aria2_rust_pro_protocol::HttpHeader { name: "content-range".to_owned(), value: "bytes 32-35/36".to_owned(), kind: aria2_rust_pro_protocol::HeaderKind::Response, }, ], }, body: ResponseBody::Inline(b"ghij".to_vec()), content_range: Some(aria2_rust_pro_protocol::ContentRangeSpec { unit: aria2_rust_pro_protocol::RangeUnit::Bytes, start: 32, end_inclusive: 35, total_size: Some(36), unsatisfied: false, }), partial_content: true, checksum: None, redirected_from: None, }), ]); let temp_dir = std::env::temp_dir().join("aria2-rust-pro-cli-tiny-tail-coalesce-test"); let _ = fs::create_dir_all(&temp_dir); let config_path = temp_dir.join("aria2.conf"); fs::write( &config_path, "split=4\nmax-connection-per-server=4\nmin-split-size=4\npiece-length=4\n", ) .expect("config should write"); let report = execute_runtime_with_downloader( Invocation::Run { config_path: Some(config_path.clone()), uris: vec!["http://example.com/coalesced-tail.bin".to_owned()], }, &downloader, ) .expect("runtime should coalesce tiny follow-up tail after probe"); assert_eq!(report.completed_download_count, 1); assert_eq!(report.first_status.as_deref(), Some("complete")); assert_eq!(report.first_completed_length, Some(36)); let recorded = downloader.recorded_requests(); let [probe, first_followup] = recorded.as_slice() else { panic!("expected exactly two recorded requests"); }; assert_eq!( probe.request.range, Some(aria2_rust_pro_protocol::RangeSpec { start: 0, end_inclusive: Some(31), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }) ); assert_eq!( first_followup.request.range, Some(aria2_rust_pro_protocol::RangeSpec { start: 32, end_inclusive: Some(35), unit: aria2_rust_pro_protocol::RangeUnit::Bytes, }) ); let _ = fs::remove_file(config_path); }