wireframe 0.3.0

Simplify building servers and clients for custom binary protocols.
Documentation
//! Step definitions for fragment metadata behavioural tests.

use std::time::Duration;

use rstest_bdd_macros::{given, then, when};
use wireframe::fragment::{FragmentHeader, FragmentIndex, MessageId};

use crate::fixtures::fragment::{FragmentWorld, TestResult};

#[given("a fragment series for message {message:u64}")]
fn given_series(fragment_world: &mut FragmentWorld, message: u64) {
    fragment_world.start_series(message);
}

#[given("the series expects fragment index {index:u32}")]
fn given_series_expectation(fragment_world: &mut FragmentWorld, index: u32) -> TestResult {
    fragment_world.force_next_index(index)?;
    Ok(())
}

#[when("fragment {index:u32} arrives marked non-final")]
fn when_fragment_non_final(fragment_world: &mut FragmentWorld, index: u32) -> TestResult {
    fragment_world.accept_fragment(index, false)?;
    Ok(())
}

#[when("fragment {index:u32} arrives marked final")]
fn when_fragment_final(fragment_world: &mut FragmentWorld, index: u32) -> TestResult {
    fragment_world.accept_fragment(index, true)?;
    Ok(())
}

#[when("fragment {index:u32} from message {message:u64} arrives marked non-final")]
fn when_fragment_other_message(
    fragment_world: &mut FragmentWorld,
    index: u32,
    message: u64,
) -> TestResult {
    fragment_world.accept_fragment_from(message, index, false)?;
    Ok(())
}

#[then("the fragment completes the message")]
fn then_fragment_completes(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_completion()?;
    Ok(())
}

#[then("the fragment is rejected as out-of-order")]
fn then_fragment_out_of_order(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_index_mismatch()?;
    Ok(())
}

#[then("the fragment is rejected for the wrong message")]
fn then_fragment_wrong_message(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_message_mismatch()?;
    Ok(())
}

#[then("the fragment is rejected for index overflow")]
fn then_fragment_overflow(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_index_overflow()?;
    Ok(())
}

#[then("the fragment is rejected because the series is complete")]
fn then_fragment_complete(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_series_complete_error()?;
    Ok(())
}

#[given("a fragmenter capped at {max_payload:usize} bytes per fragment")]
fn given_fragmenter(fragment_world: &mut FragmentWorld, max_payload: usize) -> TestResult {
    fragment_world.configure_fragmenter(max_payload)?;
    Ok(())
}

#[when("the fragmenter splits a payload of {len:usize} bytes")]
fn when_fragmenter_splits(fragment_world: &mut FragmentWorld, len: usize) -> TestResult {
    fragment_world.fragment_payload(len)?;
    Ok(())
}

#[then("the fragmenter produces {expected:usize} fragments")]
fn then_fragment_count(fragment_world: &mut FragmentWorld, expected: usize) -> TestResult {
    fragment_world.assert_fragment_count(expected)?;
    Ok(())
}

#[then("fragment {index:usize} carries {len:usize} bytes")]
fn then_fragment_payload_len(
    fragment_world: &mut FragmentWorld,
    index: usize,
    len: usize,
) -> TestResult {
    fragment_world.assert_fragment_payload_len(index, len)?;
    Ok(())
}

#[then("fragment {index:usize} is marked final")]
fn then_fragment_final(fragment_world: &mut FragmentWorld, index: usize) -> TestResult {
    fragment_world.assert_fragment_final_flag(index, true)?;
    Ok(())
}

#[then("fragment {index:usize} is marked non-final")]
fn then_fragment_non_final(fragment_world: &mut FragmentWorld, index: usize) -> TestResult {
    fragment_world.assert_fragment_final_flag(index, false)?;
    Ok(())
}

#[then("the fragments use message id {message_id:u64}")]
fn then_fragment_message_id(fragment_world: &mut FragmentWorld, message_id: u64) -> TestResult {
    fragment_world.assert_message_id(message_id)?;
    Ok(())
}

#[given(
    "a reassembler allowing {max_bytes:usize} bytes with a {timeout_secs:u64}-second reassembly \
     timeout"
)]
fn given_reassembler(
    fragment_world: &mut FragmentWorld,
    max_bytes: usize,
    timeout_secs: u64,
) -> TestResult {
    fragment_world.configure_reassembler(max_bytes, timeout_secs)?;
    Ok(())
}

#[when(
    "fragment {index:u32} for message {message:u64} with {len:usize} bytes arrives marked \
     non-final"
)]
fn when_reassembler_fragment_non_final(
    fragment_world: &mut FragmentWorld,
    index: u32,
    message: u64,
    len: usize,
) -> TestResult {
    let header = FragmentHeader::new(MessageId::new(message), FragmentIndex::new(index), false);
    fragment_world.push_fragment(header, len)?;
    Ok(())
}

#[when(
    "fragment {index:u32} for message {message:u64} with {len:usize} bytes arrives marked final"
)]
#[when("fragment {index:u32} for message {message:u64} with {len:usize} byte arrives marked final")]
fn when_reassembler_fragment_final(
    fragment_world: &mut FragmentWorld,
    index: u32,
    message: u64,
    len: usize,
) -> TestResult {
    let header = FragmentHeader::new(MessageId::new(message), FragmentIndex::new(index), true);
    fragment_world.push_fragment(header, len)?;
    Ok(())
}

#[when("time advances by {seconds:u64} seconds for reassembly")]
fn when_time_advances(fragment_world: &mut FragmentWorld, seconds: u64) -> TestResult {
    fragment_world.advance_time(Duration::from_secs(seconds))?;
    Ok(())
}

#[when("expired reassembly buffers are purged")]
fn when_reassembly_purged(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.purge_reassembly()?;
    Ok(())
}

#[then("the reassembler outputs a payload of {expected:usize} bytes")]
fn then_reassembled_len(fragment_world: &mut FragmentWorld, expected: usize) -> TestResult {
    fragment_world.assert_reassembled_len(expected)?;
    Ok(())
}

#[then("no message has been reassembled yet")]
fn then_no_reassembled_message(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_no_reassembly()?;
    Ok(())
}

#[then("the reassembler reports a message-too-large error")]
fn then_reassembly_over_limit(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_reassembly_over_limit()?;
    Ok(())
}

#[then("the reassembler reports an out-of-order fragment error")]
fn then_reassembly_out_of_order(fragment_world: &mut FragmentWorld) -> TestResult {
    fragment_world.assert_reassembly_out_of_order()?;
    Ok(())
}

fn assert_buffered_messages(fragment_world: &mut FragmentWorld, expected: usize) -> TestResult {
    fragment_world.assert_buffered_messages(expected)?;
    Ok(())
}

#[then("the reassembler is buffering {expected:usize} message")]
fn then_buffered_message(fragment_world: &mut FragmentWorld, expected: usize) -> TestResult {
    assert_buffered_messages(fragment_world, expected)
}

#[then("the reassembler is buffering {expected:usize} messages")]
fn then_buffered_messages(fragment_world: &mut FragmentWorld, expected: usize) -> TestResult {
    assert_buffered_messages(fragment_world, expected)
}

#[then("message {message:u64} is evicted")]
fn then_message_evicted(fragment_world: &mut FragmentWorld, message: u64) -> TestResult {
    fragment_world.assert_evicted_message(message)?;
    Ok(())
}