1use crate::behavioral_economics::BehavioralEconomicsEngine;
5use crate::stateful_handler::StatefulResponseHandler;
6use crate::{
7 CustomFixtureLoader, Error, FailureInjector, ProxyHandler, RealityContinuumEngine,
8 RecordReplayHandler, RequestFingerprint, ResponsePriority, ResponseSource, Result,
9};
10use async_trait::async_trait;
13use axum::http::{HeaderMap, Method, StatusCode, Uri};
14use std::collections::HashMap;
15use std::sync::Arc;
16use tokio::sync::RwLock;
17
18#[derive(Debug, Clone)]
20pub struct RouteFaultResponse {
21 pub status_code: u16,
23 pub error_message: String,
25 pub fault_type: String,
27}
28
29#[async_trait]
33pub trait RouteChaosInjectorTrait: Send + Sync {
34 async fn inject_latency(&self, method: &Method, uri: &Uri) -> Result<()>;
36
37 fn get_fault_response(&self, method: &Method, uri: &Uri) -> Option<RouteFaultResponse>;
39}
40
41#[async_trait]
43pub trait BehavioralScenarioReplay: Send + Sync {
44 async fn try_replay(
46 &self,
47 method: &Method,
48 uri: &Uri,
49 headers: &HeaderMap,
50 body: Option<&[u8]>,
51 session_id: Option<&str>,
52 ) -> Result<Option<BehavioralReplayResponse>>;
53}
54
55#[derive(Debug, Clone)]
57pub struct BehavioralReplayResponse {
58 pub status_code: u16,
60 pub headers: HashMap<String, String>,
62 pub body: Vec<u8>,
64 pub timing_ms: Option<u64>,
66 pub content_type: String,
68}
69
70pub struct PriorityHttpHandler {
72 custom_fixture_loader: Option<Arc<CustomFixtureLoader>>,
74 record_replay: RecordReplayHandler,
76 behavioral_scenario_replay: Option<Arc<dyn BehavioralScenarioReplay + Send + Sync>>,
78 stateful_handler: Option<Arc<StatefulResponseHandler>>,
80 route_chaos_injector: Option<Arc<dyn RouteChaosInjectorTrait>>,
83 failure_injector: Option<FailureInjector>,
85 proxy_handler: Option<ProxyHandler>,
87 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
89 openapi_spec: Option<crate::openapi::spec::OpenApiSpec>,
91 continuum_engine: Option<Arc<RealityContinuumEngine>>,
93 behavioral_economics_engine: Option<Arc<RwLock<BehavioralEconomicsEngine>>>,
95 request_metrics: Arc<RwLock<HashMap<String, (u64, u64, std::time::Instant)>>>,
97}
98
99pub trait MockGenerator {
101 fn generate_mock_response(
103 &self,
104 fingerprint: &RequestFingerprint,
105 headers: &HeaderMap,
106 body: Option<&[u8]>,
107 ) -> Result<Option<MockResponse>>;
108}
109
110#[derive(Debug, Clone)]
112pub struct MockResponse {
113 pub status_code: u16,
115 pub headers: HashMap<String, String>,
117 pub body: String,
119 pub content_type: String,
121}
122
123impl PriorityHttpHandler {
124 pub fn new(
126 record_replay: RecordReplayHandler,
127 failure_injector: Option<FailureInjector>,
128 proxy_handler: Option<ProxyHandler>,
129 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
130 ) -> Self {
131 Self {
132 custom_fixture_loader: None,
133 record_replay,
134 behavioral_scenario_replay: None,
135 stateful_handler: None,
136 route_chaos_injector: None,
137 failure_injector,
138 proxy_handler,
139 mock_generator,
140 openapi_spec: None,
141 continuum_engine: None,
142 behavioral_economics_engine: None,
143 request_metrics: Arc::new(RwLock::new(HashMap::new())),
144 }
145 }
146
147 pub fn new_with_openapi(
149 record_replay: RecordReplayHandler,
150 failure_injector: Option<FailureInjector>,
151 proxy_handler: Option<ProxyHandler>,
152 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
153 openapi_spec: Option<crate::openapi::spec::OpenApiSpec>,
154 ) -> Self {
155 Self {
156 custom_fixture_loader: None,
157 record_replay,
158 behavioral_scenario_replay: None,
159 stateful_handler: None,
160 route_chaos_injector: None,
161 failure_injector,
162 proxy_handler,
163 mock_generator,
164 openapi_spec,
165 continuum_engine: None,
166 behavioral_economics_engine: None,
167 request_metrics: Arc::new(RwLock::new(HashMap::new())),
168 }
169 }
170
171 pub fn with_custom_fixture_loader(mut self, loader: Arc<CustomFixtureLoader>) -> Self {
173 self.custom_fixture_loader = Some(loader);
174 self
175 }
176
177 pub fn with_stateful_handler(mut self, handler: Arc<StatefulResponseHandler>) -> Self {
179 self.stateful_handler = Some(handler);
180 self
181 }
182
183 pub fn with_route_chaos_injector(mut self, injector: Arc<dyn RouteChaosInjectorTrait>) -> Self {
185 self.route_chaos_injector = Some(injector);
186 self
187 }
188
189 pub fn with_continuum_engine(mut self, engine: Arc<RealityContinuumEngine>) -> Self {
191 self.continuum_engine = Some(engine);
192 self
193 }
194
195 pub fn with_behavioral_economics_engine(
197 mut self,
198 engine: Arc<RwLock<BehavioralEconomicsEngine>>,
199 ) -> Self {
200 self.behavioral_economics_engine = Some(engine);
201 self
202 }
203
204 pub fn with_behavioral_scenario_replay(
206 mut self,
207 replay_engine: Arc<dyn BehavioralScenarioReplay + Send + Sync>,
208 ) -> Self {
209 self.behavioral_scenario_replay = Some(replay_engine);
210 self
211 }
212
213 pub async fn process_request(
215 &self,
216 method: &Method,
217 uri: &Uri,
218 headers: &HeaderMap,
219 body: Option<&[u8]>,
220 ) -> Result<PriorityResponse> {
221 let normalized_path = crate::CustomFixtureLoader::normalize_path(uri.path());
224 let normalized_uri_str = if let Some(query) = uri.query() {
225 format!("{}?{}", normalized_path, query)
226 } else {
227 normalized_path
228 };
229 let normalized_uri =
230 normalized_uri_str.parse::<axum::http::Uri>().unwrap_or_else(|_| uri.clone());
231
232 let fingerprint = RequestFingerprint::new(method.clone(), &normalized_uri, headers, body);
233
234 if let Some(ref custom_loader) = self.custom_fixture_loader {
236 if let Some(custom_fixture) = custom_loader.load_fixture(&fingerprint) {
237 if custom_fixture.delay_ms > 0 {
239 tokio::time::sleep(tokio::time::Duration::from_millis(custom_fixture.delay_ms))
240 .await;
241 }
242
243 let response_body = if custom_fixture.response.is_string() {
245 custom_fixture.response.as_str().unwrap().to_string()
246 } else {
247 serde_json::to_string(&custom_fixture.response).map_err(|e| {
248 Error::generic(format!(
249 "Failed to serialize custom fixture response: {}",
250 e
251 ))
252 })?
253 };
254
255 let content_type = custom_fixture
257 .headers
258 .get("content-type")
259 .cloned()
260 .unwrap_or_else(|| "application/json".to_string());
261
262 return Ok(PriorityResponse {
263 source: ResponseSource::new(
264 ResponsePriority::Replay,
265 "custom_fixture".to_string(),
266 )
267 .with_metadata("fixture_path".to_string(), custom_fixture.path.clone()),
268 status_code: custom_fixture.status,
269 headers: custom_fixture.headers.clone(),
270 body: response_body.into_bytes(),
271 content_type,
272 });
273 }
274 }
275
276 if let Some(recorded_request) =
278 self.record_replay.replay_handler().load_fixture(&fingerprint).await?
279 {
280 let content_type = recorded_request
281 .response_headers
282 .get("content-type")
283 .unwrap_or(&"application/json".to_string())
284 .clone();
285
286 return Ok(PriorityResponse {
287 source: ResponseSource::new(ResponsePriority::Replay, "replay".to_string())
288 .with_metadata("fixture_path".to_string(), "recorded".to_string()),
289 status_code: recorded_request.status_code,
290 headers: recorded_request.response_headers,
291 body: recorded_request.response_body.into_bytes(),
292 content_type,
293 });
294 }
295
296 if let Some(ref scenario_replay) = self.behavioral_scenario_replay {
298 let session_id = headers
300 .get("x-session-id")
301 .or_else(|| headers.get("session-id"))
302 .and_then(|v| v.to_str().ok())
303 .map(|s| s.to_string());
304
305 if let Ok(Some(replay_response)) = scenario_replay
306 .try_replay(method, uri, headers, body, session_id.as_deref())
307 .await
308 {
309 if let Some(timing_ms) = replay_response.timing_ms {
311 tokio::time::sleep(tokio::time::Duration::from_millis(timing_ms)).await;
312 }
313 return Ok(PriorityResponse {
314 source: ResponseSource::new(
315 ResponsePriority::Replay,
316 "behavioral_scenario".to_string(),
317 )
318 .with_metadata("replay_type".to_string(), "scenario".to_string()),
319 status_code: replay_response.status_code,
320 headers: replay_response.headers,
321 body: replay_response.body,
322 content_type: replay_response.content_type,
323 });
324 }
325 }
326
327 if let Some(ref stateful_handler) = self.stateful_handler {
329 if let Some(stateful_response) =
330 stateful_handler.process_request(method, uri, headers, body).await?
331 {
332 return Ok(PriorityResponse {
333 source: ResponseSource::new(ResponsePriority::Stateful, "stateful".to_string())
334 .with_metadata("state".to_string(), stateful_response.state)
335 .with_metadata("resource_id".to_string(), stateful_response.resource_id),
336 status_code: stateful_response.status_code,
337 headers: stateful_response.headers,
338 body: stateful_response.body.into_bytes(),
339 content_type: stateful_response.content_type,
340 });
341 }
342 }
343
344 if let Some(ref route_chaos) = self.route_chaos_injector {
346 if let Err(e) = route_chaos.inject_latency(method, uri).await {
348 tracing::warn!("Failed to inject per-route latency: {}", e);
349 }
350
351 if let Some(fault_response) = route_chaos.get_fault_response(method, uri) {
353 let error_response = serde_json::json!({
354 "error": fault_response.error_message,
355 "injected_failure": true,
356 "fault_type": fault_response.fault_type,
357 "timestamp": chrono::Utc::now().to_rfc3339()
358 });
359
360 return Ok(PriorityResponse {
361 source: ResponseSource::new(
362 ResponsePriority::Fail,
363 "route_fault_injection".to_string(),
364 )
365 .with_metadata("fault_type".to_string(), fault_response.fault_type)
366 .with_metadata("error_message".to_string(), fault_response.error_message),
367 status_code: fault_response.status_code,
368 headers: HashMap::new(),
369 body: serde_json::to_string(&error_response)?.into_bytes(),
370 content_type: "application/json".to_string(),
371 });
372 }
373 }
374
375 if let Some(ref failure_injector) = self.failure_injector {
377 let tags = if let Some(ref spec) = self.openapi_spec {
378 fingerprint.openapi_tags(spec).unwrap_or_else(|| fingerprint.tags())
379 } else {
380 fingerprint.tags()
381 };
382 if let Some((status_code, error_message)) = failure_injector.process_request(&tags) {
383 let error_response = serde_json::json!({
384 "error": error_message,
385 "injected_failure": true,
386 "timestamp": chrono::Utc::now().to_rfc3339()
387 });
388
389 return Ok(PriorityResponse {
390 source: ResponseSource::new(
391 ResponsePriority::Fail,
392 "failure_injection".to_string(),
393 )
394 .with_metadata("error_message".to_string(), error_message),
395 status_code,
396 headers: HashMap::new(),
397 body: serde_json::to_string(&error_response)?.into_bytes(),
398 content_type: "application/json".to_string(),
399 });
400 }
401 }
402
403 let should_blend = if let Some(ref continuum_engine) = self.continuum_engine {
405 continuum_engine.is_enabled().await
406 } else {
407 false
408 };
409
410 if let Some(ref proxy_handler) = self.proxy_handler {
412 let migration_mode = if proxy_handler.config.migration_enabled {
414 proxy_handler.config.get_effective_migration_mode(uri.path())
415 } else {
416 None
417 };
418
419 if let Some(crate::proxy::config::MigrationMode::Mock) = migration_mode {
421 } else if proxy_handler.config.should_proxy_with_condition(method, uri, headers, body) {
423 let is_shadow = proxy_handler.config.should_shadow(uri.path());
425
426 if should_blend {
428 let proxy_future = proxy_handler.proxy_request(method, uri, headers, body);
430 let mock_result = if let Some(ref mock_generator) = self.mock_generator {
431 mock_generator.generate_mock_response(&fingerprint, headers, body)
432 } else {
433 Ok(None)
434 };
435
436 let proxy_result = proxy_future.await;
438
439 match (proxy_result, mock_result) {
441 (Ok(proxy_response), Ok(Some(mock_response))) => {
442 if let Some(ref continuum_engine) = self.continuum_engine {
444 let blend_ratio =
445 continuum_engine.get_blend_ratio(uri.path()).await;
446 let blender = continuum_engine.blender();
447
448 let mock_body_str = &mock_response.body;
450 let real_body_bytes =
451 proxy_response.body.clone().unwrap_or_default();
452 let real_body_str = String::from_utf8_lossy(&real_body_bytes);
453
454 let mock_json: serde_json::Value =
455 serde_json::from_str(mock_body_str)
456 .unwrap_or_else(|_| serde_json::json!({}));
457 let real_json: serde_json::Value =
458 serde_json::from_str(&real_body_str)
459 .unwrap_or_else(|_| serde_json::json!({}));
460
461 let blended_json =
463 blender.blend_responses(&mock_json, &real_json, blend_ratio);
464 let blended_body = serde_json::to_string(&blended_json)
465 .unwrap_or_else(|_| real_body_str.to_string());
466
467 let blended_status = blender.blend_status_code(
469 mock_response.status_code,
470 proxy_response.status_code,
471 blend_ratio,
472 );
473
474 let mut proxy_headers = HashMap::new();
476 for (key, value) in proxy_response.headers.iter() {
477 if let Ok(value_str) = value.to_str() {
478 proxy_headers.insert(
479 key.as_str().to_string(),
480 value_str.to_string(),
481 );
482 }
483 }
484 let blended_headers = blender.blend_headers(
485 &mock_response.headers,
486 &proxy_headers,
487 blend_ratio,
488 );
489
490 let content_type = blended_headers
491 .get("content-type")
492 .cloned()
493 .or_else(|| {
494 proxy_response
495 .headers
496 .get("content-type")
497 .and_then(|v| v.to_str().ok())
498 .map(|s| s.to_string())
499 })
500 .unwrap_or_else(|| "application/json".to_string());
501
502 tracing::info!(
503 path = %uri.path(),
504 blend_ratio = blend_ratio,
505 "Reality Continuum: blended mock and real responses"
506 );
507
508 let mut source = ResponseSource::new(
509 ResponsePriority::Proxy,
510 "continuum".to_string(),
511 )
512 .with_metadata("blend_ratio".to_string(), blend_ratio.to_string())
513 .with_metadata(
514 "upstream_url".to_string(),
515 proxy_handler.config.get_upstream_url(uri.path()),
516 );
517
518 if let Some(mode) = migration_mode {
519 source = source.with_metadata(
520 "migration_mode".to_string(),
521 format!("{:?}", mode),
522 );
523 }
524
525 return Ok(PriorityResponse {
526 source,
527 status_code: blended_status,
528 headers: blended_headers,
529 body: blended_body.into_bytes(),
530 content_type,
531 });
532 }
533 }
534 (Ok(proxy_response), Ok(None)) => {
535 tracing::debug!(
537 path = %uri.path(),
538 "Continuum: mock generation failed, using real response"
539 );
540 }
542 (Ok(proxy_response), Err(_)) => {
543 tracing::debug!(
545 path = %uri.path(),
546 "Continuum: mock generation failed, using real response"
547 );
548 }
550 (Err(e), Ok(Some(mock_response))) => {
551 tracing::debug!(
553 path = %uri.path(),
554 error = %e,
555 "Continuum: proxy failed, using mock response"
556 );
557 let mut source = ResponseSource::new(
559 ResponsePriority::Mock,
560 "continuum_fallback".to_string(),
561 )
562 .with_metadata("generated_from".to_string(), "openapi_spec".to_string())
563 .with_metadata(
564 "fallback_reason".to_string(),
565 "proxy_failed".to_string(),
566 );
567
568 if let Some(mode) = migration_mode {
569 source = source.with_metadata(
570 "migration_mode".to_string(),
571 format!("{:?}", mode),
572 );
573 }
574
575 return Ok(PriorityResponse {
576 source,
577 status_code: mock_response.status_code,
578 headers: mock_response.headers,
579 body: mock_response.body.into_bytes(),
580 content_type: mock_response.content_type,
581 });
582 }
583 (Err(e), _) => {
584 tracing::warn!(
586 path = %uri.path(),
587 error = %e,
588 "Continuum: both proxy and mock failed"
589 );
590 if let Some(crate::proxy::config::MigrationMode::Real) = migration_mode
592 {
593 return Err(Error::generic(format!(
594 "Proxy request failed in real mode: {}",
595 e
596 )));
597 }
598 }
600 }
601 }
602
603 match proxy_handler.proxy_request(method, uri, headers, body).await {
605 Ok(proxy_response) => {
606 let mut response_headers = HashMap::new();
607 for (key, value) in proxy_response.headers.iter() {
608 let key_str = key.as_str();
609 if let Ok(value_str) = value.to_str() {
610 response_headers.insert(key_str.to_string(), value_str.to_string());
611 }
612 }
613
614 let content_type = response_headers
615 .get("content-type")
616 .unwrap_or(&"application/json".to_string())
617 .clone();
618
619 if is_shadow {
621 if let Some(ref mock_generator) = self.mock_generator {
622 if let Ok(Some(mock_response)) = mock_generator
623 .generate_mock_response(&fingerprint, headers, body)
624 {
625 tracing::info!(
627 path = %uri.path(),
628 real_status = proxy_response.status_code,
629 mock_status = mock_response.status_code,
630 "Shadow mode: comparing real and mock responses"
631 );
632
633 let real_body_bytes =
635 proxy_response.body.clone().unwrap_or_default();
636 let real_body = String::from_utf8_lossy(&real_body_bytes);
637 let mock_body = &mock_response.body;
638
639 if real_body != *mock_body {
640 tracing::warn!(
641 path = %uri.path(),
642 "Shadow mode: real and mock responses differ"
643 );
644 }
645 }
646 }
647 }
648
649 let mut source = ResponseSource::new(
650 ResponsePriority::Proxy,
651 if is_shadow {
652 "shadow".to_string()
653 } else {
654 "proxy".to_string()
655 },
656 )
657 .with_metadata(
658 "upstream_url".to_string(),
659 proxy_handler.config.get_upstream_url(uri.path()),
660 );
661
662 if let Some(mode) = migration_mode {
663 source = source
664 .with_metadata("migration_mode".to_string(), format!("{:?}", mode));
665 }
666
667 return Ok(PriorityResponse {
668 source,
669 status_code: proxy_response.status_code,
670 headers: response_headers,
671 body: proxy_response.body.unwrap_or_default(),
672 content_type,
673 });
674 }
675 Err(e) => {
676 tracing::warn!("Proxy request failed: {}", e);
677 if let Some(crate::proxy::config::MigrationMode::Real) = migration_mode {
679 return Err(Error::generic(format!(
680 "Proxy request failed in real mode: {}",
681 e
682 )));
683 }
684 }
686 }
687 }
688 }
689
690 if let Some(ref mock_generator) = self.mock_generator {
692 let migration_mode = if let Some(ref proxy_handler) = self.proxy_handler {
694 if proxy_handler.config.migration_enabled {
695 proxy_handler.config.get_effective_migration_mode(uri.path())
696 } else {
697 None
698 }
699 } else {
700 None
701 };
702
703 if let Some(mock_response) =
704 mock_generator.generate_mock_response(&fingerprint, headers, body)?
705 {
706 let mut source = ResponseSource::new(ResponsePriority::Mock, "mock".to_string())
707 .with_metadata("generated_from".to_string(), "openapi_spec".to_string());
708
709 if let Some(mode) = migration_mode {
710 source =
711 source.with_metadata("migration_mode".to_string(), format!("{:?}", mode));
712 }
713
714 return Ok(PriorityResponse {
715 source,
716 status_code: mock_response.status_code,
717 headers: mock_response.headers,
718 body: mock_response.body.into_bytes(),
719 content_type: mock_response.content_type,
720 });
721 }
722 }
723
724 if self.record_replay.record_handler().should_record(method) {
726 let default_response = serde_json::json!({
728 "message": "Request recorded for future replay",
729 "timestamp": chrono::Utc::now().to_rfc3339(),
730 "fingerprint": fingerprint.to_hash()
731 });
732
733 let response_body = serde_json::to_string(&default_response)?;
734 let status_code = 200;
735
736 self.record_replay
738 .record_handler()
739 .record_request(&fingerprint, status_code, headers, &response_body, None)
740 .await?;
741
742 return Ok(PriorityResponse {
743 source: ResponseSource::new(ResponsePriority::Record, "record".to_string())
744 .with_metadata("recorded".to_string(), "true".to_string()),
745 status_code,
746 headers: HashMap::new(),
747 body: response_body.into_bytes(),
748 content_type: "application/json".to_string(),
749 });
750 }
751
752 Err(Error::generic("No handler could process the request".to_string()))
754 }
755
756 async fn apply_behavioral_economics(
761 &self,
762 response: PriorityResponse,
763 method: &Method,
764 uri: &Uri,
765 latency_ms: Option<u64>,
766 ) -> Result<PriorityResponse> {
767 if let Some(ref engine) = self.behavioral_economics_engine {
768 let engine = engine.read().await;
769 let evaluator = engine.condition_evaluator();
770
771 {
773 let mut eval = evaluator.write().await;
774 if let Some(latency) = latency_ms {
775 eval.update_latency(uri.path(), latency);
776 }
777
778 let endpoint = uri.path().to_string();
780 let mut metrics = self.request_metrics.write().await;
781 let now = std::time::Instant::now();
782
783 let (request_count, error_count, last_request_time) =
785 metrics.entry(endpoint.clone()).or_insert_with(|| (0, 0, now));
786
787 *request_count += 1;
789
790 if response.status_code >= 400 {
792 *error_count += 1;
793 }
794
795 let error_rate = if *request_count > 0 {
797 *error_count as f64 / *request_count as f64
798 } else {
799 0.0
800 };
801 eval.update_error_rate(&endpoint, error_rate);
802
803 let time_elapsed = now.duration_since(*last_request_time).as_secs_f64();
805 if time_elapsed > 0.0 {
806 let rps = *request_count as f64 / time_elapsed.max(1.0);
807 eval.update_load(rps);
808 }
809
810 if time_elapsed > 60.0 {
812 *request_count = 1;
813 *error_count = if response.status_code >= 400 { 1 } else { 0 };
814 *last_request_time = now;
815 } else {
816 *last_request_time = now;
817 }
818 }
819
820 let executed_actions = engine.evaluate().await?;
822
823 if !executed_actions.is_empty() {
825 tracing::debug!(
826 "Behavioral economics engine executed {} actions",
827 executed_actions.len()
828 );
829 }
833 }
834
835 Ok(response)
836 }
837}
838
839#[derive(Debug, Clone)]
841pub struct PriorityResponse {
842 pub source: ResponseSource,
844 pub status_code: u16,
846 pub headers: HashMap<String, String>,
848 pub body: Vec<u8>,
850 pub content_type: String,
852}
853
854impl PriorityResponse {
855 pub fn to_axum_response(self) -> axum::response::Response {
857 let mut response = axum::response::Response::new(axum::body::Body::from(self.body));
858 *response.status_mut() = StatusCode::from_u16(self.status_code).unwrap_or(StatusCode::OK);
859
860 for (key, value) in self.headers {
862 if let (Ok(header_name), Ok(header_value)) =
863 (key.parse::<axum::http::HeaderName>(), value.parse::<axum::http::HeaderValue>())
864 {
865 response.headers_mut().insert(header_name, header_value);
866 }
867 }
868
869 if !response.headers().contains_key("content-type") {
871 if let Ok(header_value) = self.content_type.parse::<axum::http::HeaderValue>() {
872 response.headers_mut().insert("content-type", header_value);
873 }
874 }
875
876 response
877 }
878}
879
880pub struct SimpleMockGenerator {
882 pub default_status: u16,
884 pub default_body: String,
886}
887
888impl SimpleMockGenerator {
889 pub fn new(default_status: u16, default_body: String) -> Self {
891 Self {
892 default_status,
893 default_body,
894 }
895 }
896}
897
898impl MockGenerator for SimpleMockGenerator {
899 fn generate_mock_response(
900 &self,
901 _fingerprint: &RequestFingerprint,
902 _headers: &HeaderMap,
903 _body: Option<&[u8]>,
904 ) -> Result<Option<MockResponse>> {
905 Ok(Some(MockResponse {
906 status_code: self.default_status,
907 headers: HashMap::new(),
908 body: self.default_body.clone(),
909 content_type: "application/json".to_string(),
910 }))
911 }
912}
913
914#[cfg(test)]
915mod tests {
916 use super::*;
917 use tempfile::TempDir;
918
919 struct MockRouteChaosInjector;
921
922 #[async_trait]
923 impl RouteChaosInjectorTrait for MockRouteChaosInjector {
924 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
925 Ok(())
926 }
927
928 fn get_fault_response(&self, _method: &Method, _uri: &Uri) -> Option<RouteFaultResponse> {
929 Some(RouteFaultResponse {
930 status_code: 503,
931 error_message: "Service unavailable".to_string(),
932 fault_type: "test_fault".to_string(),
933 })
934 }
935 }
936
937 struct MockBehavioralScenarioReplay;
938
939 #[async_trait]
940 impl BehavioralScenarioReplay for MockBehavioralScenarioReplay {
941 async fn try_replay(
942 &self,
943 _method: &Method,
944 _uri: &Uri,
945 _headers: &HeaderMap,
946 _body: Option<&[u8]>,
947 _session_id: Option<&str>,
948 ) -> Result<Option<BehavioralReplayResponse>> {
949 Ok(Some(BehavioralReplayResponse {
950 status_code: 200,
951 headers: HashMap::new(),
952 body: b"scenario response".to_vec(),
953 timing_ms: Some(100),
954 content_type: "application/json".to_string(),
955 }))
956 }
957 }
958
959 #[tokio::test]
960 async fn test_priority_chain() {
961 let temp_dir = TempDir::new().unwrap();
962 let fixtures_dir = temp_dir.path().to_path_buf();
963
964 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
965 let mock_generator =
966 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock response"}"#.to_string()));
967
968 let handler = PriorityHttpHandler::new_with_openapi(
969 record_replay,
970 None, None, Some(mock_generator),
973 None, );
975
976 let method = Method::GET;
977 let uri = Uri::from_static("/api/test");
978 let headers = HeaderMap::new();
979
980 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
981
982 assert_eq!(response.status_code, 200);
983 assert_eq!(response.source.source_type, "mock");
984 }
985
986 #[tokio::test]
987 async fn test_builder_methods() {
988 let temp_dir = TempDir::new().unwrap();
989 let fixtures_dir = temp_dir.path().to_path_buf();
990 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
991 let mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
992
993 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
994
995 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
997 let handler = handler.with_custom_fixture_loader(custom_loader);
998 assert!(handler.custom_fixture_loader.is_some());
999
1000 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1002 let handler = handler.with_stateful_handler(stateful_handler);
1003 assert!(handler.stateful_handler.is_some());
1004
1005 let route_chaos = Arc::new(MockRouteChaosInjector);
1007 let handler = handler.with_route_chaos_injector(route_chaos);
1008 assert!(handler.route_chaos_injector.is_some());
1009
1010 let continuum_engine = Arc::new(RealityContinuumEngine::new(
1012 crate::reality_continuum::config::ContinuumConfig::default(),
1013 ));
1014 let handler = handler.with_continuum_engine(continuum_engine);
1015 assert!(handler.continuum_engine.is_some());
1016
1017 let behavioral_engine = Arc::new(RwLock::new(
1019 BehavioralEconomicsEngine::new(
1020 crate::behavioral_economics::config::BehavioralEconomicsConfig::default(),
1021 )
1022 .unwrap(),
1023 ));
1024 let handler = handler.with_behavioral_economics_engine(behavioral_engine);
1025 assert!(handler.behavioral_economics_engine.is_some());
1026
1027 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1029 let handler = handler.with_behavioral_scenario_replay(scenario_replay);
1030 assert!(handler.behavioral_scenario_replay.is_some());
1031 }
1032
1033 #[tokio::test]
1034 async fn test_custom_fixture_priority() {
1035 let temp_dir = TempDir::new().unwrap();
1036 let fixtures_dir = temp_dir.path().to_path_buf();
1037 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1038 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
1039
1040 let fixture_path = temp_dir.path().join("custom_fixture.json");
1042 std::fs::write(
1043 &fixture_path,
1044 r#"{"status": 201, "response": {"message": "custom"}, "headers": {"x-custom": "value"}}"#,
1045 )
1046 .unwrap();
1047
1048 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1049 .with_custom_fixture_loader(custom_loader);
1050
1051 let method = Method::GET;
1052 let uri = Uri::from_static("/api/test");
1053 let headers = HeaderMap::new();
1054
1055 let _handler = handler; }
1059
1060 #[tokio::test]
1061 async fn test_route_chaos_injection() {
1062 let temp_dir = TempDir::new().unwrap();
1063 let fixtures_dir = temp_dir.path().to_path_buf();
1064 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1065 let route_chaos = Arc::new(MockRouteChaosInjector);
1066
1067 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1068 .with_route_chaos_injector(route_chaos);
1069
1070 let method = Method::GET;
1071 let uri = Uri::from_static("/api/test");
1072 let headers = HeaderMap::new();
1073
1074 let response = handler.process_request(&method, &uri, &headers, None).await;
1075
1076 if let Ok(resp) = response {
1078 assert_eq!(resp.status_code, 503);
1079 assert_eq!(resp.source.source_type, "route_fault_injection");
1080 }
1081 }
1082
1083 #[tokio::test]
1084 async fn test_behavioral_scenario_replay() {
1085 let temp_dir = TempDir::new().unwrap();
1086 let fixtures_dir = temp_dir.path().to_path_buf();
1087 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1088 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1089
1090 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1091 .with_behavioral_scenario_replay(scenario_replay);
1092
1093 let method = Method::GET;
1094 let uri = Uri::from_static("/api/test");
1095 let mut headers = HeaderMap::new();
1096 headers.insert("x-session-id", "test-session".parse().unwrap());
1097
1098 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1099
1100 assert_eq!(response.status_code, 200);
1101 assert_eq!(response.source.source_type, "behavioral_scenario");
1102 assert_eq!(response.body, b"scenario response");
1103 }
1104
1105 #[tokio::test]
1106 async fn test_priority_response_to_axum() {
1107 let response = PriorityResponse {
1108 source: ResponseSource::new(ResponsePriority::Mock, "test".to_string()),
1109 status_code: 201,
1110 headers: {
1111 let mut h = HashMap::new();
1112 h.insert("x-custom".to_string(), "value".to_string());
1113 h
1114 },
1115 body: b"test body".to_vec(),
1116 content_type: "application/json".to_string(),
1117 };
1118
1119 let axum_response = response.to_axum_response();
1120 assert_eq!(axum_response.status(), StatusCode::CREATED);
1121 }
1122
1123 #[tokio::test]
1124 async fn test_simple_mock_generator() {
1125 let generator = SimpleMockGenerator::new(404, r#"{"error": "not found"}"#.to_string());
1126 let fingerprint = RequestFingerprint::new(
1127 Method::GET,
1128 &Uri::from_static("/api/test"),
1129 &HeaderMap::new(),
1130 None,
1131 );
1132
1133 let response =
1134 generator.generate_mock_response(&fingerprint, &HeaderMap::new(), None).unwrap();
1135
1136 assert!(response.is_some());
1137 let mock_response = response.unwrap();
1138 assert_eq!(mock_response.status_code, 404);
1139 assert_eq!(mock_response.body, r#"{"error": "not found"}"#);
1140 }
1141
1142 #[tokio::test]
1143 async fn test_new_vs_new_with_openapi() {
1144 let temp_dir = TempDir::new().unwrap();
1145 let fixtures_dir = temp_dir.path().to_path_buf();
1146 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1147 let mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1148
1149 let record_replay1 = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1151 let mock_generator1 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1152 let handler1 = PriorityHttpHandler::new(record_replay1, None, None, Some(mock_generator1));
1153 assert!(handler1.openapi_spec.is_none());
1154
1155 let record_replay2 = RecordReplayHandler::new(fixtures_dir, true, true, false);
1157 let mock_generator2 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1158 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1159 r#"openapi: 3.0.0
1160info:
1161 title: Test API
1162 version: 1.0.0
1163paths:
1164 /test:
1165 get:
1166 responses:
1167 '200':
1168 description: OK
1169"#,
1170 Some("yaml"),
1171 )
1172 .unwrap();
1173 let handler2 = PriorityHttpHandler::new_with_openapi(
1174 record_replay2,
1175 None,
1176 None,
1177 Some(mock_generator2),
1178 Some(openapi_spec),
1179 );
1180 assert!(handler2.openapi_spec.is_some());
1181 }
1182
1183 #[tokio::test]
1184 async fn test_custom_fixture_with_delay() {
1185 let temp_dir = TempDir::new().unwrap();
1186 let fixtures_dir = temp_dir.path().to_path_buf();
1187 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1188
1189 let fixture_content = r#"{
1191 "method": "GET",
1192 "path": "/api/test",
1193 "status": 200,
1194 "response": {"message": "delayed response"},
1195 "delay_ms": 10
1196}"#;
1197 let fixture_file = fixtures_dir.join("test.json");
1198 std::fs::write(&fixture_file, fixture_content).unwrap();
1199
1200 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1201 custom_loader.load_fixtures().await.unwrap();
1202
1203 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1204 .with_custom_fixture_loader(Arc::new(custom_loader));
1205
1206 let method = Method::GET;
1207 let uri = Uri::from_static("/api/test");
1208 let headers = HeaderMap::new();
1209
1210 let start = std::time::Instant::now();
1211 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1212 let elapsed = start.elapsed();
1213
1214 assert_eq!(response.status_code, 200);
1215 assert_eq!(response.source.source_type, "custom_fixture");
1216 assert!(elapsed.as_millis() >= 10); }
1218
1219 #[tokio::test]
1220 async fn test_custom_fixture_with_non_string_response() {
1221 let temp_dir = TempDir::new().unwrap();
1222 let fixtures_dir = temp_dir.path().to_path_buf();
1223 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1224
1225 let fixture_content = r#"{
1227 "method": "GET",
1228 "path": "/api/test",
1229 "status": 201,
1230 "response": {"id": 123, "name": "test"},
1231 "headers": {"content-type": "application/json"}
1232}"#;
1233 let fixture_file = fixtures_dir.join("test.json");
1234 std::fs::write(&fixture_file, fixture_content).unwrap();
1235
1236 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1237 custom_loader.load_fixtures().await.unwrap();
1238
1239 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1240 .with_custom_fixture_loader(Arc::new(custom_loader));
1241
1242 let method = Method::GET;
1243 let uri = Uri::from_static("/api/test");
1244 let headers = HeaderMap::new();
1245
1246 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1247
1248 assert_eq!(response.status_code, 201);
1249 assert_eq!(response.source.source_type, "custom_fixture");
1250 assert!(response.body.len() > 0);
1251 let body_str = String::from_utf8_lossy(&response.body);
1252 assert!(body_str.contains("id"));
1253 }
1254
1255 #[tokio::test]
1256 async fn test_custom_fixture_with_custom_content_type() {
1257 let temp_dir = TempDir::new().unwrap();
1258 let fixtures_dir = temp_dir.path().to_path_buf();
1259 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1260
1261 let fixture_content = r#"{
1263 "method": "GET",
1264 "path": "/api/test",
1265 "status": 200,
1266 "response": "text response",
1267 "headers": {"content-type": "text/plain"}
1268}"#;
1269 let fixture_file = fixtures_dir.join("test.json");
1270 std::fs::write(&fixture_file, fixture_content).unwrap();
1271
1272 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1273 custom_loader.load_fixtures().await.unwrap();
1274
1275 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1276 .with_custom_fixture_loader(Arc::new(custom_loader));
1277
1278 let method = Method::GET;
1279 let uri = Uri::from_static("/api/test");
1280 let headers = HeaderMap::new();
1281
1282 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1283
1284 assert_eq!(response.status_code, 200);
1285 assert_eq!(response.content_type, "text/plain");
1286 }
1287
1288 #[tokio::test]
1289 async fn test_stateful_handler_path() {
1290 let temp_dir = TempDir::new().unwrap();
1291 let fixtures_dir = temp_dir.path().to_path_buf();
1292 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1293
1294 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1296
1297 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1300 .with_stateful_handler(stateful_handler);
1301
1302 let method = Method::GET;
1303 let uri = Uri::from_static("/api/test");
1304 let headers = HeaderMap::new();
1305
1306 let _response = handler.process_request(&method, &uri, &headers, None).await;
1309 }
1311
1312 #[tokio::test]
1313 async fn test_route_chaos_latency_injection() {
1314 let temp_dir = TempDir::new().unwrap();
1315 let fixtures_dir = temp_dir.path().to_path_buf();
1316 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1317
1318 struct LatencyInjector;
1320 #[async_trait]
1321 impl RouteChaosInjectorTrait for LatencyInjector {
1322 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1323 tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
1324 Ok(())
1325 }
1326 fn get_fault_response(
1327 &self,
1328 _method: &Method,
1329 _uri: &Uri,
1330 ) -> Option<RouteFaultResponse> {
1331 None
1332 }
1333 }
1334
1335 let route_chaos = Arc::new(LatencyInjector);
1336 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1337 .with_route_chaos_injector(route_chaos);
1338
1339 let method = Method::GET;
1340 let uri = Uri::from_static("/api/test");
1341 let headers = HeaderMap::new();
1342
1343 let start = std::time::Instant::now();
1344 let _response = handler.process_request(&method, &uri, &headers, None).await;
1345 let elapsed = start.elapsed();
1346
1347 assert!(elapsed.as_millis() >= 20);
1349 }
1350
1351 #[tokio::test]
1352 async fn test_failure_injection_path() {
1353 let temp_dir = TempDir::new().unwrap();
1354 let fixtures_dir = temp_dir.path().to_path_buf();
1355 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1356
1357 let mut failure_config = crate::failure_injection::FailureConfig::default();
1359 failure_config.global_error_rate = 1.0; failure_config.default_status_codes = vec![500]; let failure_injector = FailureInjector::new(Some(failure_config), true);
1363
1364 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1365 r#"openapi: 3.0.0
1366info:
1367 title: Test API
1368 version: 1.0.0
1369paths:
1370 /api/test:
1371 get:
1372 tags: [test]
1373 responses:
1374 '200':
1375 description: OK
1376"#,
1377 Some("yaml"),
1378 )
1379 .unwrap();
1380
1381 let handler = PriorityHttpHandler::new_with_openapi(
1382 record_replay,
1383 Some(failure_injector),
1384 None,
1385 None,
1386 Some(openapi_spec),
1387 );
1388
1389 let method = Method::GET;
1390 let uri = Uri::from_static("/api/test");
1391 let headers = HeaderMap::new();
1392
1393 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1394
1395 assert_eq!(response.status_code, 500);
1396 assert_eq!(response.source.source_type, "failure_injection");
1397 let body_str = String::from_utf8_lossy(&response.body);
1398 assert!(body_str.contains("Injected failure")); }
1400
1401 #[tokio::test]
1402 async fn test_record_handler_path() {
1403 let temp_dir = TempDir::new().unwrap();
1404 let fixtures_dir = temp_dir.path().to_path_buf();
1405 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, true);
1408
1409 let mock_generator =
1411 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1412 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
1413
1414 let method = Method::POST; let uri = Uri::from_static("/api/test");
1416 let headers = HeaderMap::new();
1417
1418 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1421
1422 assert_eq!(response.status_code, 200);
1423 assert_eq!(response.source.source_type, "mock");
1425 }
1426
1427 #[tokio::test]
1428 async fn test_behavioral_economics_engine_path() {
1429 let temp_dir = TempDir::new().unwrap();
1430 let fixtures_dir = temp_dir.path().to_path_buf();
1431 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1432 let mock_generator =
1433 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1434
1435 let be_config = crate::behavioral_economics::config::BehavioralEconomicsConfig::default();
1436 let be_engine = Arc::new(RwLock::new(BehavioralEconomicsEngine::new(be_config).unwrap()));
1437
1438 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1439 .with_behavioral_economics_engine(be_engine);
1440
1441 let method = Method::GET;
1442 let uri = Uri::from_static("/api/test");
1443 let headers = HeaderMap::new();
1444
1445 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1446
1447 assert_eq!(response.status_code, 200);
1449 }
1450
1451 #[tokio::test]
1452 async fn test_replay_handler_with_recorded_fixture() {
1453 let temp_dir = TempDir::new().unwrap();
1454 let fixtures_dir = temp_dir.path().to_path_buf();
1455 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1457
1458 let method = Method::GET;
1459 let uri = Uri::from_static("/api/test");
1460 let mut headers = HeaderMap::new();
1461 headers.insert("content-type", "application/json".parse().unwrap());
1462
1463 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1465 record_replay
1466 .record_handler()
1467 .record_request(
1468 &fingerprint,
1469 200,
1470 &headers,
1471 r#"{"message": "recorded response"}"#,
1472 None,
1473 )
1474 .await
1475 .unwrap();
1476
1477 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1479
1480 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1482
1483 assert_eq!(response.status_code, 200);
1484 assert_eq!(response.source.source_type, "replay");
1485 let body_str = String::from_utf8_lossy(&response.body);
1486 assert!(body_str.contains("recorded response"));
1487 }
1488
1489 #[tokio::test]
1490 async fn test_behavioral_scenario_replay_with_cookies() {
1491 let temp_dir = TempDir::new().unwrap();
1492 let fixtures_dir = temp_dir.path().to_path_buf();
1493 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1494
1495 struct CookieScenarioReplay;
1499 #[async_trait]
1500 impl BehavioralScenarioReplay for CookieScenarioReplay {
1501 async fn try_replay(
1502 &self,
1503 _method: &Method,
1504 _uri: &Uri,
1505 headers: &HeaderMap,
1506 _body: Option<&[u8]>,
1507 session_id: Option<&str>,
1508 ) -> Result<Option<BehavioralReplayResponse>> {
1509 if session_id == Some("header-session-123") {
1512 Ok(Some(BehavioralReplayResponse {
1513 status_code: 200,
1514 headers: HashMap::new(),
1515 body: b"header scenario response".to_vec(),
1516 timing_ms: None,
1517 content_type: "application/json".to_string(),
1518 }))
1519 } else {
1520 Ok(None)
1521 }
1522 }
1523 }
1524
1525 let scenario_replay = Arc::new(CookieScenarioReplay);
1526 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1527 .with_behavioral_scenario_replay(scenario_replay);
1528
1529 let method = Method::GET;
1530 let uri = Uri::from_static("/api/test");
1531 let mut headers = HeaderMap::new();
1532 headers.insert("session-id", "header-session-123".parse().unwrap());
1534
1535 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1536
1537 assert_eq!(response.status_code, 200);
1538 assert_eq!(response.source.source_type, "behavioral_scenario");
1539 let body_str = String::from_utf8_lossy(&response.body);
1540 assert!(body_str.contains("header scenario"));
1541 }
1542
1543 #[tokio::test]
1544 async fn test_route_chaos_latency_error_handling() {
1545 let temp_dir = TempDir::new().unwrap();
1546 let fixtures_dir = temp_dir.path().to_path_buf();
1547 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1548
1549 struct ErrorLatencyInjector;
1551 #[async_trait]
1552 impl RouteChaosInjectorTrait for ErrorLatencyInjector {
1553 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1554 Err(Error::generic("Latency injection failed".to_string()))
1555 }
1556 fn get_fault_response(
1557 &self,
1558 _method: &Method,
1559 _uri: &Uri,
1560 ) -> Option<RouteFaultResponse> {
1561 None
1562 }
1563 }
1564
1565 let route_chaos = Arc::new(ErrorLatencyInjector);
1566 let mock_generator =
1567 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1568 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1569 .with_route_chaos_injector(route_chaos);
1570
1571 let method = Method::GET;
1572 let uri = Uri::from_static("/api/test");
1573 let headers = HeaderMap::new();
1574
1575 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1577 assert_eq!(response.status_code, 200);
1578 }
1579
1580 #[tokio::test]
1581 async fn test_behavioral_scenario_replay_with_timing_delay() {
1582 let temp_dir = TempDir::new().unwrap();
1583 let fixtures_dir = temp_dir.path().to_path_buf();
1584 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1585
1586 struct TimingScenarioReplay;
1588 #[async_trait]
1589 impl BehavioralScenarioReplay for TimingScenarioReplay {
1590 async fn try_replay(
1591 &self,
1592 _method: &Method,
1593 _uri: &Uri,
1594 _headers: &HeaderMap,
1595 _body: Option<&[u8]>,
1596 _session_id: Option<&str>,
1597 ) -> Result<Option<BehavioralReplayResponse>> {
1598 Ok(Some(BehavioralReplayResponse {
1599 status_code: 200,
1600 headers: HashMap::new(),
1601 body: b"delayed response".to_vec(),
1602 timing_ms: Some(15), content_type: "application/json".to_string(),
1604 }))
1605 }
1606 }
1607
1608 let scenario_replay = Arc::new(TimingScenarioReplay);
1609 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1610 .with_behavioral_scenario_replay(scenario_replay);
1611
1612 let method = Method::GET;
1613 let uri = Uri::from_static("/api/test");
1614 let headers = HeaderMap::new();
1615
1616 let start = std::time::Instant::now();
1617 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1618 let elapsed = start.elapsed();
1619
1620 assert_eq!(response.status_code, 200);
1621 assert!(elapsed.as_millis() >= 15); }
1623
1624 #[tokio::test]
1625 async fn test_stateful_handler_with_response() {
1626 let temp_dir = TempDir::new().unwrap();
1627 let fixtures_dir = temp_dir.path().to_path_buf();
1628 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1629
1630 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1634 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1635 .with_stateful_handler(stateful_handler);
1636
1637 let method = Method::GET;
1638 let uri = Uri::from_static("/api/test");
1639 let headers = HeaderMap::new();
1640
1641 let _result = handler.process_request(&method, &uri, &headers, None).await;
1644 }
1646
1647 #[tokio::test]
1648 async fn test_replay_handler_content_type_extraction() {
1649 let temp_dir = TempDir::new().unwrap();
1650 let fixtures_dir = temp_dir.path().to_path_buf();
1651 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1652
1653 let method = Method::GET;
1654 let uri = Uri::from_static("/api/test");
1655 let mut headers = HeaderMap::new();
1656 headers.insert("content-type", "application/xml".parse().unwrap());
1657
1658 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1660 record_replay
1661 .record_handler()
1662 .record_request(&fingerprint, 200, &headers, r#"<xml>test</xml>"#, None)
1663 .await
1664 .unwrap();
1665
1666 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1668
1669 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1671 assert_eq!(response.content_type, "application/xml");
1672 }
1673
1674 #[tokio::test]
1675 async fn test_proxy_migration_mode_mock() {
1676 let temp_dir = TempDir::new().unwrap();
1677 let fixtures_dir = temp_dir.path().to_path_buf();
1678 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1679
1680 let mut proxy_config =
1682 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1683 proxy_config.migration_enabled = true;
1684 proxy_config.rules.push(crate::proxy::config::ProxyRule {
1685 path_pattern: "/api/*".to_string(),
1686 target_url: "http://localhost:8080".to_string(),
1687 enabled: true,
1688 pattern: "/api/*".to_string(),
1689 upstream_url: "http://localhost:8080".to_string(),
1690 migration_mode: crate::proxy::config::MigrationMode::Mock, migration_group: None,
1692 condition: None,
1693 });
1694
1695 let proxy_handler = ProxyHandler::new(proxy_config);
1696 let mock_generator =
1697 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1698
1699 let handler = PriorityHttpHandler::new(
1700 record_replay,
1701 None,
1702 Some(proxy_handler),
1703 Some(mock_generator),
1704 );
1705
1706 let method = Method::GET;
1707 let uri = Uri::from_static("/api/test");
1708 let headers = HeaderMap::new();
1709
1710 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1712 assert_eq!(response.status_code, 200);
1713 assert_eq!(response.source.source_type, "mock");
1714 }
1715
1716 #[tokio::test]
1717 async fn test_proxy_migration_mode_disabled() {
1718 let temp_dir = TempDir::new().unwrap();
1719 let fixtures_dir = temp_dir.path().to_path_buf();
1720 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1721
1722 let mut proxy_config =
1724 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1725 proxy_config.migration_enabled = false; proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
1729 let mock_generator =
1730 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1731
1732 let handler = PriorityHttpHandler::new(
1733 record_replay,
1734 None,
1735 Some(proxy_handler),
1736 Some(mock_generator),
1737 );
1738
1739 let method = Method::GET;
1740 let uri = Uri::from_static("/api/test");
1741 let headers = HeaderMap::new();
1742
1743 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1745 assert_eq!(response.status_code, 200);
1746 assert_eq!(response.source.source_type, "mock");
1747 }
1748
1749 #[tokio::test]
1750 async fn test_continuum_engine_enabled_check() {
1751 let temp_dir = TempDir::new().unwrap();
1752 let fixtures_dir = temp_dir.path().to_path_buf();
1753 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1754
1755 let continuum_config = crate::reality_continuum::config::ContinuumConfig::new();
1757 let continuum_engine = Arc::new(RealityContinuumEngine::new(continuum_config));
1758 let mock_generator =
1759 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1760
1761 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1762 .with_continuum_engine(continuum_engine);
1763
1764 let method = Method::GET;
1765 let uri = Uri::from_static("/api/test");
1766 let headers = HeaderMap::new();
1767
1768 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1770 assert_eq!(response.status_code, 200);
1771 }
1772
1773 #[tokio::test]
1774 async fn test_behavioral_scenario_replay_error_handling() {
1775 let temp_dir = TempDir::new().unwrap();
1776 let fixtures_dir = temp_dir.path().to_path_buf();
1777 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1778
1779 struct ErrorScenarioReplay;
1781 #[async_trait]
1782 impl BehavioralScenarioReplay for ErrorScenarioReplay {
1783 async fn try_replay(
1784 &self,
1785 _method: &Method,
1786 _uri: &Uri,
1787 _headers: &HeaderMap,
1788 _body: Option<&[u8]>,
1789 _session_id: Option<&str>,
1790 ) -> Result<Option<BehavioralReplayResponse>> {
1791 Err(Error::generic("Scenario replay error".to_string()))
1792 }
1793 }
1794
1795 let scenario_replay = Arc::new(ErrorScenarioReplay);
1796 let mock_generator =
1797 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1798 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1799 .with_behavioral_scenario_replay(scenario_replay);
1800
1801 let method = Method::GET;
1802 let uri = Uri::from_static("/api/test");
1803 let headers = HeaderMap::new();
1804
1805 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1807 assert_eq!(response.status_code, 200);
1808 assert_eq!(response.source.source_type, "mock");
1809 }
1810
1811 #[tokio::test]
1812 async fn test_behavioral_scenario_replay_with_session_id_header() {
1813 let temp_dir = TempDir::new().unwrap();
1814 let fixtures_dir = temp_dir.path().to_path_buf();
1815 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1816
1817 struct SessionScenarioReplay;
1819 #[async_trait]
1820 impl BehavioralScenarioReplay for SessionScenarioReplay {
1821 async fn try_replay(
1822 &self,
1823 _method: &Method,
1824 _uri: &Uri,
1825 _headers: &HeaderMap,
1826 _body: Option<&[u8]>,
1827 session_id: Option<&str>,
1828 ) -> Result<Option<BehavioralReplayResponse>> {
1829 if session_id == Some("header-session-456") {
1830 Ok(Some(BehavioralReplayResponse {
1831 status_code: 200,
1832 headers: HashMap::new(),
1833 body: b"header session response".to_vec(),
1834 timing_ms: None,
1835 content_type: "application/json".to_string(),
1836 }))
1837 } else {
1838 Ok(None)
1839 }
1840 }
1841 }
1842
1843 let scenario_replay = Arc::new(SessionScenarioReplay);
1844 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1845 .with_behavioral_scenario_replay(scenario_replay);
1846
1847 let method = Method::GET;
1848 let uri = Uri::from_static("/api/test");
1849 let mut headers = HeaderMap::new();
1850 headers.insert("x-session-id", "header-session-456".parse().unwrap());
1851
1852 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1853 assert_eq!(response.status_code, 200);
1854 assert_eq!(response.source.source_type, "behavioral_scenario");
1855 }
1856
1857 #[tokio::test]
1858 async fn test_stateful_handler_returns_response() {
1859 let temp_dir = TempDir::new().unwrap();
1860 let fixtures_dir = temp_dir.path().to_path_buf();
1861 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1862
1863 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1865
1866 let mut state_responses = HashMap::new();
1868 state_responses.insert(
1869 "initial".to_string(),
1870 crate::stateful_handler::StateResponse {
1871 status_code: 200,
1872 headers: HashMap::new(),
1873 body_template: r#"{"status": "initial", "order_id": "123"}"#.to_string(),
1874 content_type: "application/json".to_string(),
1875 },
1876 );
1877
1878 let config = crate::stateful_handler::StatefulConfig {
1879 resource_id_extract: crate::stateful_handler::ResourceIdExtract::PathParam {
1880 param: "order_id".to_string(),
1881 },
1882 resource_type: "order".to_string(),
1883 state_responses,
1884 transitions: vec![],
1885 };
1886
1887 stateful_handler.add_config("/api/orders/{order_id}".to_string(), config).await;
1888
1889 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1890 .with_stateful_handler(stateful_handler);
1891
1892 let method = Method::GET;
1893 let uri = Uri::from_static("/api/orders/123");
1894 let headers = HeaderMap::new();
1895
1896 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1898 assert_eq!(response.status_code, 200);
1899 assert_eq!(response.source.source_type, "stateful");
1900 assert_eq!(response.source.metadata.get("state"), Some(&"initial".to_string()));
1901 assert_eq!(response.source.metadata.get("resource_id"), Some(&"123".to_string()));
1902 }
1903
1904 #[tokio::test]
1905 async fn test_record_handler_path_with_no_other_handlers() {
1906 let temp_dir = TempDir::new().unwrap();
1907 let fixtures_dir = temp_dir.path().to_path_buf();
1908 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, false);
1910
1911 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1912
1913 let method = Method::GET; let uri = Uri::from_static("/api/test");
1915 let headers = HeaderMap::new();
1916
1917 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1919 assert_eq!(response.status_code, 200);
1920 assert_eq!(response.source.source_type, "record");
1921 let body_str = String::from_utf8_lossy(&response.body);
1922 assert!(body_str.contains("Request recorded"));
1923 }
1924
1925 #[tokio::test]
1926 async fn test_mock_generator_with_migration_mode() {
1927 let temp_dir = TempDir::new().unwrap();
1928 let fixtures_dir = temp_dir.path().to_path_buf();
1929 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1930
1931 let mut proxy_config =
1933 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1934 proxy_config.migration_enabled = true;
1935 proxy_config.rules.push(crate::proxy::config::ProxyRule {
1936 path_pattern: "/api/*".to_string(),
1937 target_url: "http://localhost:8080".to_string(),
1938 enabled: true,
1939 pattern: "/api/*".to_string(),
1940 upstream_url: "http://localhost:8080".to_string(),
1941 migration_mode: crate::proxy::config::MigrationMode::Mock,
1942 migration_group: None,
1943 condition: None,
1944 });
1945 proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
1948 let mock_generator = Box::new(SimpleMockGenerator::new(
1949 200,
1950 r#"{"message": "mock with migration"}"#.to_string(),
1951 ));
1952
1953 let handler = PriorityHttpHandler::new(
1954 record_replay,
1955 None,
1956 Some(proxy_handler),
1957 Some(mock_generator),
1958 );
1959
1960 let method = Method::GET;
1961 let uri = Uri::from_static("/api/test");
1962 let headers = HeaderMap::new();
1963
1964 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1966 assert_eq!(response.status_code, 200);
1967 assert_eq!(response.source.source_type, "mock");
1968 let body_str = String::from_utf8_lossy(&response.body);
1969 assert!(body_str.contains("mock with migration"));
1970 }
1971
1972 #[tokio::test]
1973 async fn test_no_handler_can_process_request() {
1974 let temp_dir = TempDir::new().unwrap();
1975 let fixtures_dir = temp_dir.path().to_path_buf();
1976 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, false, false);
1978 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1979
1980 let method = Method::GET;
1981 let uri = Uri::from_static("/api/test");
1982 let headers = HeaderMap::new();
1983
1984 let result = handler.process_request(&method, &uri, &headers, None).await;
1986 assert!(result.is_err());
1987 assert!(result.unwrap_err().to_string().contains("No handler could process"));
1988 }
1989
1990 #[tokio::test]
1991 async fn test_route_chaos_fault_injection() {
1992 let temp_dir = TempDir::new().unwrap();
1993 let fixtures_dir = temp_dir.path().to_path_buf();
1994 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1995
1996 struct FaultInjector;
1998 #[async_trait]
1999 impl RouteChaosInjectorTrait for FaultInjector {
2000 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
2001 Ok(())
2002 }
2003 fn get_fault_response(&self, method: &Method, uri: &Uri) -> Option<RouteFaultResponse> {
2004 if method == Method::GET && uri.path() == "/api/faulty" {
2005 Some(RouteFaultResponse {
2006 status_code: 503,
2007 error_message: "Service unavailable".to_string(),
2008 fault_type: "injected_fault".to_string(),
2009 })
2010 } else {
2011 None
2012 }
2013 }
2014 }
2015
2016 let route_chaos = Arc::new(FaultInjector);
2017 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
2018 .with_route_chaos_injector(route_chaos);
2019
2020 let method = Method::GET;
2021 let uri = Uri::from_static("/api/faulty");
2022 let headers = HeaderMap::new();
2023
2024 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2026 assert_eq!(response.status_code, 503);
2027 let body_str = String::from_utf8_lossy(&response.body);
2028 assert!(body_str.contains("Service unavailable"));
2029 assert!(body_str.contains("injected_failure"));
2030 }
2031}