use std::time::Duration;
use azure_core::http::{
headers::{HeaderName, HeaderValue, Headers},
StatusCode,
};
use crate::constants::SubStatusCode;
use super::FaultInjectionErrorType;
#[derive(Clone, Debug)]
pub struct CustomResponse {
pub status_code: StatusCode,
pub headers: Headers,
pub body: Vec<u8>,
}
pub struct CustomResponseBuilder {
status_code: StatusCode,
headers: Headers,
body: Vec<u8>,
}
impl CustomResponseBuilder {
pub fn new(status_code: StatusCode) -> Self {
Self {
status_code,
headers: Headers::new(),
body: Vec::new(),
}
}
pub fn with_header(
mut self,
name: impl Into<HeaderName>,
value: impl Into<HeaderValue>,
) -> Self {
self.headers.insert(name, value);
self
}
pub fn with_sub_status(self, code: impl Into<SubStatusCode>) -> Self {
let code = code.into();
self.with_header(crate::constants::SUB_STATUS, code.to_string())
}
pub fn with_body(mut self, body: impl Into<Vec<u8>>) -> Self {
self.body = body.into();
self
}
pub fn build(self) -> CustomResponse {
CustomResponse {
status_code: self.status_code,
headers: self.headers,
body: self.body,
}
}
}
#[derive(Clone, Debug)]
pub struct FaultInjectionResult {
pub error_type: Option<FaultInjectionErrorType>,
pub custom_response: Option<CustomResponse>,
pub delay: Duration,
probability: f32,
}
impl FaultInjectionResult {
pub fn probability(&self) -> f32 {
self.probability
}
}
pub struct FaultInjectionResultBuilder {
error_type: Option<FaultInjectionErrorType>,
custom_response: Option<CustomResponse>,
delay: Duration,
probability: f32,
}
impl FaultInjectionResultBuilder {
pub fn new() -> Self {
Self {
error_type: None,
custom_response: None,
delay: Duration::ZERO,
probability: 1.0,
}
}
pub fn with_error(mut self, error_type: FaultInjectionErrorType) -> Self {
self.error_type = Some(error_type);
self
}
pub fn with_custom_response(mut self, response: CustomResponse) -> Self {
self.custom_response = Some(response);
self
}
pub fn with_delay(mut self, delay: Duration) -> Self {
self.delay = delay;
self
}
pub fn with_probability(mut self, probability: f32) -> Self {
self.probability = probability.clamp(0.0, 1.0);
self
}
pub fn build(self) -> FaultInjectionResult {
FaultInjectionResult {
error_type: self.error_type,
custom_response: self.custom_response,
delay: self.delay,
probability: self.probability,
}
}
}
impl Default for FaultInjectionResultBuilder {
fn default() -> Self {
Self::new()
}
}
#[cfg(test)]
mod tests {
use super::{CustomResponse, FaultInjectionResultBuilder};
use crate::fault_injection::FaultInjectionErrorType;
use azure_core::http::{headers::Headers, StatusCode};
use std::time::Duration;
#[test]
fn builder_default_values() {
let error = FaultInjectionResultBuilder::new()
.with_error(FaultInjectionErrorType::Timeout)
.build();
assert_eq!(error.error_type.unwrap(), FaultInjectionErrorType::Timeout);
assert_eq!(error.delay, Duration::ZERO);
assert!((error.probability() - 1.0).abs() < f32::EPSILON);
}
#[test]
fn builder_probability_clamped_above() {
let error = FaultInjectionResultBuilder::new()
.with_error(FaultInjectionErrorType::ServiceUnavailable)
.with_probability(1.5)
.build();
assert!((error.probability() - 1.0).abs() < f32::EPSILON);
}
#[test]
fn builder_probability_clamped_below() {
let error = FaultInjectionResultBuilder::new()
.with_error(FaultInjectionErrorType::ServiceUnavailable)
.with_probability(-0.5)
.build();
assert!(error.probability().abs() < f32::EPSILON);
}
#[test]
fn builder_with_custom_response() {
let body = b"{\"test\": true}".to_vec();
let result = FaultInjectionResultBuilder::new()
.with_custom_response(CustomResponse {
status_code: StatusCode::Ok,
headers: Headers::new(),
body: body.clone(),
})
.build();
assert!(result.error_type.is_none());
let custom = result.custom_response.unwrap();
assert_eq!(custom.status_code, StatusCode::Ok);
assert_eq!(custom.body, body);
}
}