mod common;
#[tokio::test]
async fn test_happy_path() {
transfer_test! {
length: 8,
steps: [Read(8)],
expect: Succeeds,
}
}
#[tokio::test]
async fn test_source_transient_then_resume() {
transfer_test! {
length: 8,
steps: [
Read(3), SourceTransient("simulated stream error"), Read(5), ], expect: Succeeds,
}
}
#[tokio::test]
async fn test_source_permanent_error() {
transfer_test! {
length: 0,
steps: [GetReaderPermanent("not found")],
expect: PermanentError,
}
}
#[tokio::test]
async fn test_stream_permanent_mid_transfer() {
transfer_test! {
length: 100,
steps: [
Read(50),
SourcePermanent("resource gone"),
],
expect: PermanentError,
}
}
#[tokio::test]
async fn test_writer_transient_error() {
transfer_test! {
length: 100,
steps: [
Read(50), WriterTransient("disk busy"), Read(50), ], expect: Succeeds,
}
}
#[tokio::test]
async fn test_writer_permanent_error() {
transfer_test! {
length: 100,
steps: [
Read(50),
WriterPermanent("disk full"),
],
expect: PermanentError,
}
}
#[tokio::test]
async fn test_offset_mismatch_truncates() {
transfer_test! {
length: 10,
steps: [
Read(4), SourceTransient("simulated"), Restart, Read(10), ], expect: Succeeds,
}
}
#[tokio::test]
async fn test_retry_exhaustion() {
transfer_test! {
length: 100,
max_retries: 2,
steps: [
SourceTransient("always failing"), SourceTransient("always failing"), SourceTransient("always failing"), ],
expect: PermanentErrorContaining("exhausted"),
}
}
#[tokio::test]
async fn test_zero_byte_transfer() {
transfer_test! {
length: 0,
steps: [Read(0)],
expect: Succeeds,
}
}
#[tokio::test]
async fn test_get_reader_transient_then_success() {
transfer_test! {
length: 4,
steps: [
GetReaderTransient("connection refused"), Read(4), ],
expect: Succeeds,
}
}
#[tokio::test]
async fn test_partial_write_resume() {
transfer_test! {
length: 100,
steps: [
Read(40),
WriterTransient("disk busy"),
Read(60),
],
expect: Succeeds,
}
}
#[tokio::test]
async fn test_writer_error_with_multi_chunk_source() {
transfer_test! {
length: 120,
source_chunk_size: 40,
steps: [
Read(80), WriterTransient("disk busy"), Read(40), ], expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "steps read 9 bytes total but length is 10")]
async fn test_dsl_rejects_under_read() {
transfer_test! {
length: 10,
steps: [Read(9)],
expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "exceeds data length")]
async fn test_dsl_rejects_over_read() {
transfer_test! {
length: 10,
steps: [Read(11)],
expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "cannot appear mid-attempt")]
async fn test_dsl_rejects_get_reader_transient_mid_attempt() {
transfer_test! {
length: 10,
steps: [Read(5), GetReaderTransient("err")],
expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "cannot appear mid-attempt")]
async fn test_dsl_rejects_get_reader_permanent_mid_attempt() {
transfer_test! {
length: 10,
steps: [Read(5), GetReaderPermanent("err")],
expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "must follow an error")]
async fn test_dsl_rejects_restart_mid_attempt() {
transfer_test! {
length: 10,
steps: [Read(5), Restart],
expect: Succeeds,
}
}
#[tokio::test]
#[should_panic(expected = "multiple writer errors")]
async fn test_dsl_rejects_multiple_writer_errors() {
transfer_test! {
length: 100,
steps: [
Read(30),
WriterTransient("first"),
Read(30),
WriterTransient("second"),
Read(40),
],
expect: Succeeds,
}
}