use std::sync::LazyLock;
use crate::bytes::{ByteBuffer, BytePool};
use crate::flow::config::{FakeBodyMode, FakeHeaderConfig, FieldType, FieldTypeHolder, FlowConfig};
use crate::utils::unix_timestamp_ms;
static TEST_POOL: LazyLock<BytePool> = LazyLock::new(|| BytePool::new(32, 256, 32, 4, 16));
#[test]
fn test_fake_body_empty_returns_zero() {
let mode = FakeBodyMode::Empty;
assert_eq!(mode.get_length(1500, 100, false), 0);
assert_eq!(mode.get_length(1500, 100, true), 0);
assert_eq!(mode.get_length(0, 0, false), 0);
}
#[test]
fn test_fake_body_constant_basic() {
let mode = FakeBodyMode::Constant {
packet_length: 500,
};
assert_eq!(mode.get_length(1500, 100, false), 400);
assert_eq!(mode.get_length(1500, 100, true), 400);
}
#[test]
fn test_fake_body_constant_exact_fit() {
let mode = FakeBodyMode::Constant {
packet_length: 100,
};
assert_eq!(mode.get_length(1500, 100, false), 0);
}
#[test]
fn test_fake_body_constant_returns_zero_when_taken_exceeds() {
let mode = FakeBodyMode::Constant {
packet_length: 50,
};
assert_eq!(mode.get_length(1500, 100, false), 0);
}
#[test]
fn test_fake_body_random_service_only_skips_non_service() {
let mode = FakeBodyMode::Random {
min_length: 10,
max_length: 200,
service: true,
};
assert_eq!(mode.get_length(1500, 100, false), 0, "service-only mode should skip non-service packets");
}
#[test]
fn test_fake_body_random_non_service_pads() {
let mode = FakeBodyMode::Random {
min_length: 10,
max_length: 200,
service: false,
};
for _ in 0..20 {
let len = mode.get_length(1500, 100, false);
assert!((10..200).contains(&len), "length {len} should be in [10, 200)");
}
}
#[test]
fn test_fake_body_random_service_only_pads_service() {
let mode = FakeBodyMode::Random {
min_length: 10,
max_length: 200,
service: true,
};
for _ in 0..20 {
let len = mode.get_length(1500, 100, true);
assert!((10..200).contains(&len), "length {len} should be in [10, 200)");
}
}
#[test]
fn test_fake_body_random_tight_space() {
let mode = FakeBodyMode::Random {
min_length: 50,
max_length: 200,
service: false,
};
let len = mode.get_length(60, 50, false);
assert_eq!(len, 10, "should return effective_max when space is tight");
}
#[test]
fn test_field_type_constant() {
let mut field: FieldType<u32> = FieldType::Constant {
value: 42,
};
for _ in 0..10 {
assert_eq!(field.apply(), 42);
}
}
#[test]
fn test_field_type_incremental() {
let mut field: FieldType<u16> = FieldType::Incremental {
value: 0,
};
assert_eq!(field.apply(), 1);
assert_eq!(field.apply(), 2);
assert_eq!(field.apply(), 3);
}
#[test]
fn test_field_type_incremental_from_offset() {
let mut field: FieldType<u8> = FieldType::Incremental {
value: 250,
};
assert_eq!(field.apply(), 251);
assert_eq!(field.apply(), 252);
}
#[test]
fn test_field_type_random_runs() {
let mut field: FieldType<u64> = FieldType::Random;
for _ in 0..20 {
let _ = field.apply();
}
}
#[test]
fn test_field_type_volatile_never_changes() {
let mut field: FieldType<u32> = FieldType::Volatile {
value: 99,
change_probability: 2.0,
};
for _ in 0..50 {
assert_eq!(field.apply(), 99);
}
}
#[test]
fn test_field_type_switching_before_timeout() {
let far_future = unix_timestamp_ms() + 1_000_000;
let mut field: FieldType<u32> = FieldType::Switching {
value: 7,
next_switch: far_future,
switch_timeout: 60_000,
};
assert_eq!(field.apply(), 7, "should return current value before timeout");
}
#[test]
fn test_field_type_switching_after_timeout() {
let mut field: FieldType<u32> = FieldType::Switching {
value: 7,
next_switch: 0, switch_timeout: 60_000,
};
let _ = field.apply();
}
#[test]
fn test_fake_header_config_len() {
let config = FakeHeaderConfig {
pattern: vec![
FieldTypeHolder::U8(FieldType::Constant {
value: 0,
}),
FieldTypeHolder::U16(FieldType::Constant {
value: 0,
}),
FieldTypeHolder::U32(FieldType::Constant {
value: 0,
}),
FieldTypeHolder::U64(FieldType::Constant {
value: 0,
}),
],
};
assert_eq!(config.len(), 1 + 2 + 4 + 8, "total header length should be 15 bytes");
}
#[test]
fn test_fake_header_config_len_empty() {
let config = FakeHeaderConfig {
pattern: vec![],
};
assert_eq!(config.is_empty(), true, "empty pattern should be considered empty");
}
#[test]
fn test_fake_header_config_fill_constants() {
let mut config = FakeHeaderConfig {
pattern: vec![
FieldTypeHolder::U8(FieldType::Constant {
value: 0xAB,
}),
FieldTypeHolder::U16(FieldType::Constant {
value: 0x1234,
}),
FieldTypeHolder::U32(FieldType::Constant {
value: 0xDEAD_BEEF,
}),
],
};
let total_len = config.len(); let buffer = TEST_POOL.allocate(Some(total_len));
config.fill(buffer.clone());
let data = buffer.slice();
assert_eq!(data[0], 0xAB, "u8 field should be written at offset 0");
assert_eq!(&data[1..3], &0x1234u16.to_be_bytes(), "u16 field should be big-endian at offset 1");
assert_eq!(&data[3..7], &0xDEAD_BEEFu32.to_be_bytes(), "u32 field should be big-endian at offset 3");
}
#[test]
fn test_fake_header_config_fill_incremental() {
let mut config = FakeHeaderConfig {
pattern: vec![FieldTypeHolder::U8(FieldType::Incremental {
value: 0,
})],
};
let buffer = TEST_POOL.allocate(Some(1));
config.fill(buffer.clone());
assert_eq!(buffer.slice()[0], 1);
config.fill(buffer.clone());
assert_eq!(buffer.slice()[0], 2);
}
#[test]
fn test_flow_config_assert_empty_body_ok() {
let config = FlowConfig::new(FakeBodyMode::Empty, FakeHeaderConfig::new(vec![]));
assert!(config.assert(1500).is_ok());
}
#[test]
fn test_flow_config_assert_constant_body_ok() {
let config = FlowConfig::new(
FakeBodyMode::Constant {
packet_length: 1000,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_ok());
}
#[test]
fn test_flow_config_assert_constant_body_exceeds_max() {
let config = FlowConfig::new(
FakeBodyMode::Constant {
packet_length: 2000,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_err());
}
#[test]
fn test_flow_config_assert_random_body_ok() {
let config = FlowConfig::new(
FakeBodyMode::Random {
min_length: 10,
max_length: 200,
service: false,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_ok());
}
#[test]
fn test_flow_config_assert_random_body_min_exceeds_max() {
let config = FlowConfig::new(
FakeBodyMode::Random {
min_length: 300,
max_length: 100,
service: false,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_err());
}
#[test]
fn test_flow_config_assert_header_exceeds_max() {
let large_pattern: Vec<FieldTypeHolder> = (0..200)
.map(|_| {
FieldTypeHolder::U64(FieldType::Constant {
value: 0,
})
})
.collect();
let config = FlowConfig::new(FakeBodyMode::Empty, FakeHeaderConfig::new(large_pattern));
assert!(config.assert(1500).is_err());
}
#[test]
fn test_flow_config_assert_constant_body_at_max() {
let config = FlowConfig::new(
FakeBodyMode::Constant {
packet_length: 1500,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_ok());
}
#[test]
fn test_flow_config_assert_random_body_equal_bounds() {
let config = FlowConfig::new(
FakeBodyMode::Random {
min_length: 100,
max_length: 100,
service: false,
},
FakeHeaderConfig::new(vec![]),
);
assert!(config.assert(1500).is_ok());
}
#[test]
fn test_field_type_switching_no_thrash_after_first_switch() {
let switch_timeout_ms: u64 = 500;
let mut field: FieldType<u8> = FieldType::Switching {
value: 0u8,
next_switch: unix_timestamp_ms() - 1,
switch_timeout: switch_timeout_ms,
};
let first = field.apply();
let second = field.apply();
assert_eq!(first, second, "field switched twice within a single millisecond — next_switch reset is broken");
}
#[test]
fn test_field_type_switching_switches_again_after_timeout() {
let switch_timeout_ms: u64 = 50; let mut field: FieldType<u8> = FieldType::Switching {
value: 0u8,
next_switch: unix_timestamp_ms() - 1,
switch_timeout: switch_timeout_ms,
};
let _ = field.apply();
std::thread::sleep(std::time::Duration::from_millis(switch_timeout_ms + 10));
let _after = field.apply();
let immediate = field.apply();
let immediate2 = field.apply();
assert_eq!(immediate, immediate2, "double switch within 1ms — timer reset broken after second trigger");
}