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 #[allow(dead_code, clippy::type_complexity)]
97 request_metrics: Arc<RwLock<HashMap<String, (u64, u64, std::time::Instant)>>>,
98}
99
100pub trait MockGenerator {
102 fn generate_mock_response(
104 &self,
105 fingerprint: &RequestFingerprint,
106 headers: &HeaderMap,
107 body: Option<&[u8]>,
108 ) -> Result<Option<MockResponse>>;
109}
110
111#[derive(Debug, Clone)]
113pub struct MockResponse {
114 pub status_code: u16,
116 pub headers: HashMap<String, String>,
118 pub body: String,
120 pub content_type: String,
122}
123
124impl PriorityHttpHandler {
125 pub fn new(
127 record_replay: RecordReplayHandler,
128 failure_injector: Option<FailureInjector>,
129 proxy_handler: Option<ProxyHandler>,
130 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
131 ) -> Self {
132 Self {
133 custom_fixture_loader: None,
134 record_replay,
135 behavioral_scenario_replay: None,
136 stateful_handler: None,
137 route_chaos_injector: None,
138 failure_injector,
139 proxy_handler,
140 mock_generator,
141 openapi_spec: None,
142 continuum_engine: None,
143 behavioral_economics_engine: None,
144 request_metrics: Arc::new(RwLock::new(HashMap::new())),
145 }
146 }
147
148 pub fn new_with_openapi(
150 record_replay: RecordReplayHandler,
151 failure_injector: Option<FailureInjector>,
152 proxy_handler: Option<ProxyHandler>,
153 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
154 openapi_spec: Option<crate::openapi::spec::OpenApiSpec>,
155 ) -> Self {
156 Self {
157 custom_fixture_loader: None,
158 record_replay,
159 behavioral_scenario_replay: None,
160 stateful_handler: None,
161 route_chaos_injector: None,
162 failure_injector,
163 proxy_handler,
164 mock_generator,
165 openapi_spec,
166 continuum_engine: None,
167 behavioral_economics_engine: None,
168 request_metrics: Arc::new(RwLock::new(HashMap::new())),
169 }
170 }
171
172 pub fn with_custom_fixture_loader(mut self, loader: Arc<CustomFixtureLoader>) -> Self {
174 self.custom_fixture_loader = Some(loader);
175 self
176 }
177
178 pub fn with_stateful_handler(mut self, handler: Arc<StatefulResponseHandler>) -> Self {
180 self.stateful_handler = Some(handler);
181 self
182 }
183
184 pub fn with_route_chaos_injector(mut self, injector: Arc<dyn RouteChaosInjectorTrait>) -> Self {
186 self.route_chaos_injector = Some(injector);
187 self
188 }
189
190 pub fn with_continuum_engine(mut self, engine: Arc<RealityContinuumEngine>) -> Self {
192 self.continuum_engine = Some(engine);
193 self
194 }
195
196 pub fn with_behavioral_economics_engine(
198 mut self,
199 engine: Arc<RwLock<BehavioralEconomicsEngine>>,
200 ) -> Self {
201 self.behavioral_economics_engine = Some(engine);
202 self
203 }
204
205 pub fn with_behavioral_scenario_replay(
207 mut self,
208 replay_engine: Arc<dyn BehavioralScenarioReplay + Send + Sync>,
209 ) -> Self {
210 self.behavioral_scenario_replay = Some(replay_engine);
211 self
212 }
213
214 pub async fn process_request(
216 &self,
217 method: &Method,
218 uri: &Uri,
219 headers: &HeaderMap,
220 body: Option<&[u8]>,
221 ) -> Result<PriorityResponse> {
222 let normalized_path = CustomFixtureLoader::normalize_path(uri.path());
225 let normalized_uri_str = if let Some(query) = uri.query() {
226 format!("{}?{}", normalized_path, query)
227 } else {
228 normalized_path
229 };
230 let normalized_uri = normalized_uri_str.parse::<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 #[allow(dead_code)]
761 async fn apply_behavioral_economics(
762 &self,
763 response: PriorityResponse,
764 _method: &Method,
765 uri: &Uri,
766 latency_ms: Option<u64>,
767 ) -> Result<PriorityResponse> {
768 if let Some(ref engine) = self.behavioral_economics_engine {
769 let engine = engine.read().await;
770 let evaluator = engine.condition_evaluator();
771
772 {
774 let mut eval = evaluator.write().await;
775 if let Some(latency) = latency_ms {
776 eval.update_latency(uri.path(), latency);
777 }
778
779 let endpoint = uri.path().to_string();
781 let mut metrics = self.request_metrics.write().await;
782 let now = std::time::Instant::now();
783
784 let (request_count, error_count, last_request_time) =
786 metrics.entry(endpoint.clone()).or_insert_with(|| (0, 0, now));
787
788 *request_count += 1;
790
791 if response.status_code >= 400 {
793 *error_count += 1;
794 }
795
796 let error_rate = if *request_count > 0 {
798 *error_count as f64 / *request_count as f64
799 } else {
800 0.0
801 };
802 eval.update_error_rate(&endpoint, error_rate);
803
804 let time_elapsed = now.duration_since(*last_request_time).as_secs_f64();
806 if time_elapsed > 0.0 {
807 let rps = *request_count as f64 / time_elapsed.max(1.0);
808 eval.update_load(rps);
809 }
810
811 if time_elapsed > 60.0 {
813 *request_count = 1;
814 *error_count = if response.status_code >= 400 { 1 } else { 0 };
815 *last_request_time = now;
816 } else {
817 *last_request_time = now;
818 }
819 }
820
821 let executed_actions = engine.evaluate().await?;
823
824 if !executed_actions.is_empty() {
826 tracing::debug!(
827 "Behavioral economics engine executed {} actions",
828 executed_actions.len()
829 );
830 }
834 }
835
836 Ok(response)
837 }
838}
839
840#[derive(Debug, Clone)]
842pub struct PriorityResponse {
843 pub source: ResponseSource,
845 pub status_code: u16,
847 pub headers: HashMap<String, String>,
849 pub body: Vec<u8>,
851 pub content_type: String,
853}
854
855impl PriorityResponse {
856 pub fn to_axum_response(self) -> axum::response::Response {
858 let mut response = axum::response::Response::new(axum::body::Body::from(self.body));
859 *response.status_mut() = StatusCode::from_u16(self.status_code).unwrap_or(StatusCode::OK);
860
861 for (key, value) in self.headers {
863 if let (Ok(header_name), Ok(header_value)) =
864 (key.parse::<axum::http::HeaderName>(), value.parse::<axum::http::HeaderValue>())
865 {
866 response.headers_mut().insert(header_name, header_value);
867 }
868 }
869
870 if !response.headers().contains_key("content-type") {
872 if let Ok(header_value) = self.content_type.parse::<axum::http::HeaderValue>() {
873 response.headers_mut().insert("content-type", header_value);
874 }
875 }
876
877 response
878 }
879}
880
881pub struct SimpleMockGenerator {
883 pub default_status: u16,
885 pub default_body: String,
887}
888
889impl SimpleMockGenerator {
890 pub fn new(default_status: u16, default_body: String) -> Self {
892 Self {
893 default_status,
894 default_body,
895 }
896 }
897}
898
899impl MockGenerator for SimpleMockGenerator {
900 fn generate_mock_response(
901 &self,
902 _fingerprint: &RequestFingerprint,
903 _headers: &HeaderMap,
904 _body: Option<&[u8]>,
905 ) -> Result<Option<MockResponse>> {
906 Ok(Some(MockResponse {
907 status_code: self.default_status,
908 headers: HashMap::new(),
909 body: self.default_body.clone(),
910 content_type: "application/json".to_string(),
911 }))
912 }
913}
914
915#[cfg(test)]
916mod tests {
917 use super::*;
918 use tempfile::TempDir;
919
920 struct MockRouteChaosInjector;
922
923 #[async_trait]
924 impl RouteChaosInjectorTrait for MockRouteChaosInjector {
925 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
926 Ok(())
927 }
928
929 fn get_fault_response(&self, _method: &Method, _uri: &Uri) -> Option<RouteFaultResponse> {
930 Some(RouteFaultResponse {
931 status_code: 503,
932 error_message: "Service unavailable".to_string(),
933 fault_type: "test_fault".to_string(),
934 })
935 }
936 }
937
938 struct MockBehavioralScenarioReplay;
939
940 #[async_trait]
941 impl BehavioralScenarioReplay for MockBehavioralScenarioReplay {
942 async fn try_replay(
943 &self,
944 _method: &Method,
945 _uri: &Uri,
946 _headers: &HeaderMap,
947 _body: Option<&[u8]>,
948 _session_id: Option<&str>,
949 ) -> Result<Option<BehavioralReplayResponse>> {
950 Ok(Some(BehavioralReplayResponse {
951 status_code: 200,
952 headers: HashMap::new(),
953 body: b"scenario response".to_vec(),
954 timing_ms: Some(100),
955 content_type: "application/json".to_string(),
956 }))
957 }
958 }
959
960 #[tokio::test]
961 async fn test_priority_chain() {
962 let temp_dir = TempDir::new().unwrap();
963 let fixtures_dir = temp_dir.path().to_path_buf();
964
965 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
966 let mock_generator =
967 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock response"}"#.to_string()));
968
969 let handler = PriorityHttpHandler::new_with_openapi(
970 record_replay,
971 None, None, Some(mock_generator),
974 None, );
976
977 let method = Method::GET;
978 let uri = Uri::from_static("/api/test");
979 let headers = HeaderMap::new();
980
981 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
982
983 assert_eq!(response.status_code, 200);
984 assert_eq!(response.source.source_type, "mock");
985 }
986
987 #[tokio::test]
988 async fn test_builder_methods() {
989 let temp_dir = TempDir::new().unwrap();
990 let fixtures_dir = temp_dir.path().to_path_buf();
991 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
992 let mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
993
994 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
995
996 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
998 let handler = handler.with_custom_fixture_loader(custom_loader);
999 assert!(handler.custom_fixture_loader.is_some());
1000
1001 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1003 let handler = handler.with_stateful_handler(stateful_handler);
1004 assert!(handler.stateful_handler.is_some());
1005
1006 let route_chaos = Arc::new(MockRouteChaosInjector);
1008 let handler = handler.with_route_chaos_injector(route_chaos);
1009 assert!(handler.route_chaos_injector.is_some());
1010
1011 let continuum_engine = Arc::new(RealityContinuumEngine::new(
1013 crate::reality_continuum::config::ContinuumConfig::default(),
1014 ));
1015 let handler = handler.with_continuum_engine(continuum_engine);
1016 assert!(handler.continuum_engine.is_some());
1017
1018 let behavioral_engine = Arc::new(RwLock::new(
1020 BehavioralEconomicsEngine::new(
1021 crate::behavioral_economics::config::BehavioralEconomicsConfig::default(),
1022 )
1023 .unwrap(),
1024 ));
1025 let handler = handler.with_behavioral_economics_engine(behavioral_engine);
1026 assert!(handler.behavioral_economics_engine.is_some());
1027
1028 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1030 let handler = handler.with_behavioral_scenario_replay(scenario_replay);
1031 assert!(handler.behavioral_scenario_replay.is_some());
1032 }
1033
1034 #[tokio::test]
1035 async fn test_custom_fixture_priority() {
1036 let temp_dir = TempDir::new().unwrap();
1037 let fixtures_dir = temp_dir.path().to_path_buf();
1038 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1039 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
1040
1041 let fixture_path = temp_dir.path().join("custom_fixture.json");
1043 std::fs::write(
1044 &fixture_path,
1045 r#"{"status": 201, "response": {"message": "custom"}, "headers": {"x-custom": "value"}}"#,
1046 )
1047 .unwrap();
1048
1049 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1050 .with_custom_fixture_loader(custom_loader);
1051
1052 let _method = Method::GET;
1053 let _uri = Uri::from_static("/api/test");
1054 let _headers = HeaderMap::new();
1055
1056 let _handler = handler; }
1060
1061 #[tokio::test]
1062 async fn test_route_chaos_injection() {
1063 let temp_dir = TempDir::new().unwrap();
1064 let fixtures_dir = temp_dir.path().to_path_buf();
1065 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1066 let route_chaos = Arc::new(MockRouteChaosInjector);
1067
1068 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1069 .with_route_chaos_injector(route_chaos);
1070
1071 let method = Method::GET;
1072 let uri = Uri::from_static("/api/test");
1073 let headers = HeaderMap::new();
1074
1075 let response = handler.process_request(&method, &uri, &headers, None).await;
1076
1077 if let Ok(resp) = response {
1079 assert_eq!(resp.status_code, 503);
1080 assert_eq!(resp.source.source_type, "route_fault_injection");
1081 }
1082 }
1083
1084 #[tokio::test]
1085 async fn test_behavioral_scenario_replay() {
1086 let temp_dir = TempDir::new().unwrap();
1087 let fixtures_dir = temp_dir.path().to_path_buf();
1088 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1089 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1090
1091 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1092 .with_behavioral_scenario_replay(scenario_replay);
1093
1094 let method = Method::GET;
1095 let uri = Uri::from_static("/api/test");
1096 let mut headers = HeaderMap::new();
1097 headers.insert("x-session-id", "test-session".parse().unwrap());
1098
1099 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1100
1101 assert_eq!(response.status_code, 200);
1102 assert_eq!(response.source.source_type, "behavioral_scenario");
1103 assert_eq!(response.body, b"scenario response");
1104 }
1105
1106 #[tokio::test]
1107 async fn test_priority_response_to_axum() {
1108 let response = PriorityResponse {
1109 source: ResponseSource::new(ResponsePriority::Mock, "test".to_string()),
1110 status_code: 201,
1111 headers: {
1112 let mut h = HashMap::new();
1113 h.insert("x-custom".to_string(), "value".to_string());
1114 h
1115 },
1116 body: b"test body".to_vec(),
1117 content_type: "application/json".to_string(),
1118 };
1119
1120 let axum_response = response.to_axum_response();
1121 assert_eq!(axum_response.status(), StatusCode::CREATED);
1122 }
1123
1124 #[tokio::test]
1125 async fn test_simple_mock_generator() {
1126 let generator = SimpleMockGenerator::new(404, r#"{"error": "not found"}"#.to_string());
1127 let fingerprint = RequestFingerprint::new(
1128 Method::GET,
1129 &Uri::from_static("/api/test"),
1130 &HeaderMap::new(),
1131 None,
1132 );
1133
1134 let response =
1135 generator.generate_mock_response(&fingerprint, &HeaderMap::new(), None).unwrap();
1136
1137 assert!(response.is_some());
1138 let mock_response = response.unwrap();
1139 assert_eq!(mock_response.status_code, 404);
1140 assert_eq!(mock_response.body, r#"{"error": "not found"}"#);
1141 }
1142
1143 #[tokio::test]
1144 async fn test_new_vs_new_with_openapi() {
1145 let temp_dir = TempDir::new().unwrap();
1146 let fixtures_dir = temp_dir.path().to_path_buf();
1147 let _record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1148 let _mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1149
1150 let record_replay1 = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1152 let mock_generator1 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1153 let handler1 = PriorityHttpHandler::new(record_replay1, None, None, Some(mock_generator1));
1154 assert!(handler1.openapi_spec.is_none());
1155
1156 let record_replay2 = RecordReplayHandler::new(fixtures_dir, true, true, false);
1158 let mock_generator2 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1159 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1160 r#"openapi: 3.0.0
1161info:
1162 title: Test API
1163 version: 1.0.0
1164paths:
1165 /test:
1166 get:
1167 responses:
1168 '200':
1169 description: OK
1170"#,
1171 Some("yaml"),
1172 )
1173 .unwrap();
1174 let handler2 = PriorityHttpHandler::new_with_openapi(
1175 record_replay2,
1176 None,
1177 None,
1178 Some(mock_generator2),
1179 Some(openapi_spec),
1180 );
1181 assert!(handler2.openapi_spec.is_some());
1182 }
1183
1184 #[tokio::test]
1185 async fn test_custom_fixture_with_delay() {
1186 let temp_dir = TempDir::new().unwrap();
1187 let fixtures_dir = temp_dir.path().to_path_buf();
1188 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1189
1190 let fixture_content = r#"{
1192 "method": "GET",
1193 "path": "/api/test",
1194 "status": 200,
1195 "response": {"message": "delayed response"},
1196 "delay_ms": 10
1197}"#;
1198 let fixture_file = fixtures_dir.join("test.json");
1199 std::fs::write(&fixture_file, fixture_content).unwrap();
1200
1201 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1202 custom_loader.load_fixtures().await.unwrap();
1203
1204 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1205 .with_custom_fixture_loader(Arc::new(custom_loader));
1206
1207 let method = Method::GET;
1208 let uri = Uri::from_static("/api/test");
1209 let headers = HeaderMap::new();
1210
1211 let start = std::time::Instant::now();
1212 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1213 let elapsed = start.elapsed();
1214
1215 assert_eq!(response.status_code, 200);
1216 assert_eq!(response.source.source_type, "custom_fixture");
1217 assert!(elapsed.as_millis() >= 10); }
1219
1220 #[tokio::test]
1221 async fn test_custom_fixture_with_non_string_response() {
1222 let temp_dir = TempDir::new().unwrap();
1223 let fixtures_dir = temp_dir.path().to_path_buf();
1224 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1225
1226 let fixture_content = r#"{
1228 "method": "GET",
1229 "path": "/api/test",
1230 "status": 201,
1231 "response": {"id": 123, "name": "test"},
1232 "headers": {"content-type": "application/json"}
1233}"#;
1234 let fixture_file = fixtures_dir.join("test.json");
1235 std::fs::write(&fixture_file, fixture_content).unwrap();
1236
1237 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1238 custom_loader.load_fixtures().await.unwrap();
1239
1240 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1241 .with_custom_fixture_loader(Arc::new(custom_loader));
1242
1243 let method = Method::GET;
1244 let uri = Uri::from_static("/api/test");
1245 let headers = HeaderMap::new();
1246
1247 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1248
1249 assert_eq!(response.status_code, 201);
1250 assert_eq!(response.source.source_type, "custom_fixture");
1251 assert!(!response.body.is_empty());
1252 let body_str = String::from_utf8_lossy(&response.body);
1253 assert!(body_str.contains("id"));
1254 }
1255
1256 #[tokio::test]
1257 async fn test_custom_fixture_with_custom_content_type() {
1258 let temp_dir = TempDir::new().unwrap();
1259 let fixtures_dir = temp_dir.path().to_path_buf();
1260 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1261
1262 let fixture_content = r#"{
1264 "method": "GET",
1265 "path": "/api/test",
1266 "status": 200,
1267 "response": "text response",
1268 "headers": {"content-type": "text/plain"}
1269}"#;
1270 let fixture_file = fixtures_dir.join("test.json");
1271 std::fs::write(&fixture_file, fixture_content).unwrap();
1272
1273 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1274 custom_loader.load_fixtures().await.unwrap();
1275
1276 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1277 .with_custom_fixture_loader(Arc::new(custom_loader));
1278
1279 let method = Method::GET;
1280 let uri = Uri::from_static("/api/test");
1281 let headers = HeaderMap::new();
1282
1283 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1284
1285 assert_eq!(response.status_code, 200);
1286 assert_eq!(response.content_type, "text/plain");
1287 }
1288
1289 #[tokio::test]
1290 async fn test_stateful_handler_path() {
1291 let temp_dir = TempDir::new().unwrap();
1292 let fixtures_dir = temp_dir.path().to_path_buf();
1293 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1294
1295 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1297
1298 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1301 .with_stateful_handler(stateful_handler);
1302
1303 let method = Method::GET;
1304 let uri = Uri::from_static("/api/test");
1305 let headers = HeaderMap::new();
1306
1307 let _response = handler.process_request(&method, &uri, &headers, None).await;
1310 }
1312
1313 #[tokio::test]
1314 async fn test_route_chaos_latency_injection() {
1315 let temp_dir = TempDir::new().unwrap();
1316 let fixtures_dir = temp_dir.path().to_path_buf();
1317 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1318
1319 struct LatencyInjector;
1321 #[async_trait]
1322 impl RouteChaosInjectorTrait for LatencyInjector {
1323 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1324 tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
1325 Ok(())
1326 }
1327 fn get_fault_response(
1328 &self,
1329 _method: &Method,
1330 _uri: &Uri,
1331 ) -> Option<RouteFaultResponse> {
1332 None
1333 }
1334 }
1335
1336 let route_chaos = Arc::new(LatencyInjector);
1337 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1338 .with_route_chaos_injector(route_chaos);
1339
1340 let method = Method::GET;
1341 let uri = Uri::from_static("/api/test");
1342 let headers = HeaderMap::new();
1343
1344 let start = std::time::Instant::now();
1345 let _response = handler.process_request(&method, &uri, &headers, None).await;
1346 let elapsed = start.elapsed();
1347
1348 assert!(elapsed.as_millis() >= 20);
1350 }
1351
1352 #[tokio::test]
1353 async fn test_failure_injection_path() {
1354 let temp_dir = TempDir::new().unwrap();
1355 let fixtures_dir = temp_dir.path().to_path_buf();
1356 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1357
1358 let failure_config = crate::failure_injection::FailureConfig {
1360 global_error_rate: 1.0, default_status_codes: vec![500], ..Default::default()
1363 };
1364
1365 let failure_injector = FailureInjector::new(Some(failure_config), true);
1366
1367 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1368 r#"openapi: 3.0.0
1369info:
1370 title: Test API
1371 version: 1.0.0
1372paths:
1373 /api/test:
1374 get:
1375 tags: [test]
1376 responses:
1377 '200':
1378 description: OK
1379"#,
1380 Some("yaml"),
1381 )
1382 .unwrap();
1383
1384 let handler = PriorityHttpHandler::new_with_openapi(
1385 record_replay,
1386 Some(failure_injector),
1387 None,
1388 None,
1389 Some(openapi_spec),
1390 );
1391
1392 let method = Method::GET;
1393 let uri = Uri::from_static("/api/test");
1394 let headers = HeaderMap::new();
1395
1396 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1397
1398 assert_eq!(response.status_code, 500);
1399 assert_eq!(response.source.source_type, "failure_injection");
1400 let body_str = String::from_utf8_lossy(&response.body);
1401 assert!(body_str.contains("Injected failure")); }
1403
1404 #[tokio::test]
1405 async fn test_record_handler_path() {
1406 let temp_dir = TempDir::new().unwrap();
1407 let fixtures_dir = temp_dir.path().to_path_buf();
1408 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, true);
1411
1412 let mock_generator =
1414 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1415 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
1416
1417 let method = Method::POST; let uri = Uri::from_static("/api/test");
1419 let headers = HeaderMap::new();
1420
1421 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1424
1425 assert_eq!(response.status_code, 200);
1426 assert_eq!(response.source.source_type, "mock");
1428 }
1429
1430 #[tokio::test]
1431 async fn test_behavioral_economics_engine_path() {
1432 let temp_dir = TempDir::new().unwrap();
1433 let fixtures_dir = temp_dir.path().to_path_buf();
1434 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1435 let mock_generator =
1436 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1437
1438 let be_config = crate::behavioral_economics::config::BehavioralEconomicsConfig::default();
1439 let be_engine = Arc::new(RwLock::new(BehavioralEconomicsEngine::new(be_config).unwrap()));
1440
1441 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1442 .with_behavioral_economics_engine(be_engine);
1443
1444 let method = Method::GET;
1445 let uri = Uri::from_static("/api/test");
1446 let headers = HeaderMap::new();
1447
1448 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1449
1450 assert_eq!(response.status_code, 200);
1452 }
1453
1454 #[tokio::test]
1455 async fn test_replay_handler_with_recorded_fixture() {
1456 let temp_dir = TempDir::new().unwrap();
1457 let fixtures_dir = temp_dir.path().to_path_buf();
1458 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1460
1461 let method = Method::GET;
1462 let uri = Uri::from_static("/api/test");
1463 let mut headers = HeaderMap::new();
1464 headers.insert("content-type", "application/json".parse().unwrap());
1465
1466 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1468 record_replay
1469 .record_handler()
1470 .record_request(
1471 &fingerprint,
1472 200,
1473 &headers,
1474 r#"{"message": "recorded response"}"#,
1475 None,
1476 )
1477 .await
1478 .unwrap();
1479
1480 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1482
1483 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1485
1486 assert_eq!(response.status_code, 200);
1487 assert_eq!(response.source.source_type, "replay");
1488 let body_str = String::from_utf8_lossy(&response.body);
1489 assert!(body_str.contains("recorded response"));
1490 }
1491
1492 #[tokio::test]
1493 async fn test_behavioral_scenario_replay_with_cookies() {
1494 let temp_dir = TempDir::new().unwrap();
1495 let fixtures_dir = temp_dir.path().to_path_buf();
1496 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1497
1498 struct CookieScenarioReplay;
1502 #[async_trait]
1503 impl BehavioralScenarioReplay for CookieScenarioReplay {
1504 async fn try_replay(
1505 &self,
1506 _method: &Method,
1507 _uri: &Uri,
1508 _headers: &HeaderMap,
1509 _body: Option<&[u8]>,
1510 session_id: Option<&str>,
1511 ) -> Result<Option<BehavioralReplayResponse>> {
1512 if session_id == Some("header-session-123") {
1515 Ok(Some(BehavioralReplayResponse {
1516 status_code: 200,
1517 headers: HashMap::new(),
1518 body: b"header scenario response".to_vec(),
1519 timing_ms: None,
1520 content_type: "application/json".to_string(),
1521 }))
1522 } else {
1523 Ok(None)
1524 }
1525 }
1526 }
1527
1528 let scenario_replay = Arc::new(CookieScenarioReplay);
1529 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1530 .with_behavioral_scenario_replay(scenario_replay);
1531
1532 let method = Method::GET;
1533 let uri = Uri::from_static("/api/test");
1534 let mut headers = HeaderMap::new();
1535 headers.insert("session-id", "header-session-123".parse().unwrap());
1537
1538 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1539
1540 assert_eq!(response.status_code, 200);
1541 assert_eq!(response.source.source_type, "behavioral_scenario");
1542 let body_str = String::from_utf8_lossy(&response.body);
1543 assert!(body_str.contains("header scenario"));
1544 }
1545
1546 #[tokio::test]
1547 async fn test_route_chaos_latency_error_handling() {
1548 let temp_dir = TempDir::new().unwrap();
1549 let fixtures_dir = temp_dir.path().to_path_buf();
1550 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1551
1552 struct ErrorLatencyInjector;
1554 #[async_trait]
1555 impl RouteChaosInjectorTrait for ErrorLatencyInjector {
1556 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1557 Err(Error::generic("Latency injection failed".to_string()))
1558 }
1559 fn get_fault_response(
1560 &self,
1561 _method: &Method,
1562 _uri: &Uri,
1563 ) -> Option<RouteFaultResponse> {
1564 None
1565 }
1566 }
1567
1568 let route_chaos = Arc::new(ErrorLatencyInjector);
1569 let mock_generator =
1570 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1571 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1572 .with_route_chaos_injector(route_chaos);
1573
1574 let method = Method::GET;
1575 let uri = Uri::from_static("/api/test");
1576 let headers = HeaderMap::new();
1577
1578 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1580 assert_eq!(response.status_code, 200);
1581 }
1582
1583 #[tokio::test]
1584 async fn test_behavioral_scenario_replay_with_timing_delay() {
1585 let temp_dir = TempDir::new().unwrap();
1586 let fixtures_dir = temp_dir.path().to_path_buf();
1587 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1588
1589 struct TimingScenarioReplay;
1591 #[async_trait]
1592 impl BehavioralScenarioReplay for TimingScenarioReplay {
1593 async fn try_replay(
1594 &self,
1595 _method: &Method,
1596 _uri: &Uri,
1597 _headers: &HeaderMap,
1598 _body: Option<&[u8]>,
1599 _session_id: Option<&str>,
1600 ) -> Result<Option<BehavioralReplayResponse>> {
1601 Ok(Some(BehavioralReplayResponse {
1602 status_code: 200,
1603 headers: HashMap::new(),
1604 body: b"delayed response".to_vec(),
1605 timing_ms: Some(15), content_type: "application/json".to_string(),
1607 }))
1608 }
1609 }
1610
1611 let scenario_replay = Arc::new(TimingScenarioReplay);
1612 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1613 .with_behavioral_scenario_replay(scenario_replay);
1614
1615 let method = Method::GET;
1616 let uri = Uri::from_static("/api/test");
1617 let headers = HeaderMap::new();
1618
1619 let start = std::time::Instant::now();
1620 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1621 let elapsed = start.elapsed();
1622
1623 assert_eq!(response.status_code, 200);
1624 assert!(elapsed.as_millis() >= 15); }
1626
1627 #[tokio::test]
1628 async fn test_stateful_handler_with_response() {
1629 let temp_dir = TempDir::new().unwrap();
1630 let fixtures_dir = temp_dir.path().to_path_buf();
1631 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1632
1633 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1637 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1638 .with_stateful_handler(stateful_handler);
1639
1640 let method = Method::GET;
1641 let uri = Uri::from_static("/api/test");
1642 let headers = HeaderMap::new();
1643
1644 let _result = handler.process_request(&method, &uri, &headers, None).await;
1647 }
1649
1650 #[tokio::test]
1651 async fn test_replay_handler_content_type_extraction() {
1652 let temp_dir = TempDir::new().unwrap();
1653 let fixtures_dir = temp_dir.path().to_path_buf();
1654 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1655
1656 let method = Method::GET;
1657 let uri = Uri::from_static("/api/test");
1658 let mut headers = HeaderMap::new();
1659 headers.insert("content-type", "application/xml".parse().unwrap());
1660
1661 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1663 record_replay
1664 .record_handler()
1665 .record_request(&fingerprint, 200, &headers, r#"<xml>test</xml>"#, None)
1666 .await
1667 .unwrap();
1668
1669 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1671
1672 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1674 assert_eq!(response.content_type, "application/xml");
1675 }
1676
1677 #[tokio::test]
1678 async fn test_proxy_migration_mode_mock() {
1679 let temp_dir = TempDir::new().unwrap();
1680 let fixtures_dir = temp_dir.path().to_path_buf();
1681 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1682
1683 let mut proxy_config =
1685 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1686 proxy_config.migration_enabled = true;
1687 proxy_config.rules.push(crate::proxy::config::ProxyRule {
1688 path_pattern: "/api/*".to_string(),
1689 target_url: "http://localhost:8080".to_string(),
1690 enabled: true,
1691 pattern: "/api/*".to_string(),
1692 upstream_url: "http://localhost:8080".to_string(),
1693 migration_mode: crate::proxy::config::MigrationMode::Mock, migration_group: None,
1695 condition: None,
1696 });
1697
1698 let proxy_handler = ProxyHandler::new(proxy_config);
1699 let mock_generator =
1700 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1701
1702 let handler = PriorityHttpHandler::new(
1703 record_replay,
1704 None,
1705 Some(proxy_handler),
1706 Some(mock_generator),
1707 );
1708
1709 let method = Method::GET;
1710 let uri = Uri::from_static("/api/test");
1711 let headers = HeaderMap::new();
1712
1713 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1715 assert_eq!(response.status_code, 200);
1716 assert_eq!(response.source.source_type, "mock");
1717 }
1718
1719 #[tokio::test]
1720 async fn test_proxy_migration_mode_disabled() {
1721 let temp_dir = TempDir::new().unwrap();
1722 let fixtures_dir = temp_dir.path().to_path_buf();
1723 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1724
1725 let mut proxy_config =
1727 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1728 proxy_config.migration_enabled = false; proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
1732 let mock_generator =
1733 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1734
1735 let handler = PriorityHttpHandler::new(
1736 record_replay,
1737 None,
1738 Some(proxy_handler),
1739 Some(mock_generator),
1740 );
1741
1742 let method = Method::GET;
1743 let uri = Uri::from_static("/api/test");
1744 let headers = HeaderMap::new();
1745
1746 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1748 assert_eq!(response.status_code, 200);
1749 assert_eq!(response.source.source_type, "mock");
1750 }
1751
1752 #[tokio::test]
1753 async fn test_continuum_engine_enabled_check() {
1754 let temp_dir = TempDir::new().unwrap();
1755 let fixtures_dir = temp_dir.path().to_path_buf();
1756 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1757
1758 let continuum_config = crate::reality_continuum::config::ContinuumConfig::new();
1760 let continuum_engine = Arc::new(RealityContinuumEngine::new(continuum_config));
1761 let mock_generator =
1762 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1763
1764 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1765 .with_continuum_engine(continuum_engine);
1766
1767 let method = Method::GET;
1768 let uri = Uri::from_static("/api/test");
1769 let headers = HeaderMap::new();
1770
1771 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1773 assert_eq!(response.status_code, 200);
1774 }
1775
1776 #[tokio::test]
1777 async fn test_behavioral_scenario_replay_error_handling() {
1778 let temp_dir = TempDir::new().unwrap();
1779 let fixtures_dir = temp_dir.path().to_path_buf();
1780 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1781
1782 struct ErrorScenarioReplay;
1784 #[async_trait]
1785 impl BehavioralScenarioReplay for ErrorScenarioReplay {
1786 async fn try_replay(
1787 &self,
1788 _method: &Method,
1789 _uri: &Uri,
1790 _headers: &HeaderMap,
1791 _body: Option<&[u8]>,
1792 _session_id: Option<&str>,
1793 ) -> Result<Option<BehavioralReplayResponse>> {
1794 Err(Error::generic("Scenario replay error".to_string()))
1795 }
1796 }
1797
1798 let scenario_replay = Arc::new(ErrorScenarioReplay);
1799 let mock_generator =
1800 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1801 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1802 .with_behavioral_scenario_replay(scenario_replay);
1803
1804 let method = Method::GET;
1805 let uri = Uri::from_static("/api/test");
1806 let headers = HeaderMap::new();
1807
1808 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1810 assert_eq!(response.status_code, 200);
1811 assert_eq!(response.source.source_type, "mock");
1812 }
1813
1814 #[tokio::test]
1815 async fn test_behavioral_scenario_replay_with_session_id_header() {
1816 let temp_dir = TempDir::new().unwrap();
1817 let fixtures_dir = temp_dir.path().to_path_buf();
1818 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1819
1820 struct SessionScenarioReplay;
1822 #[async_trait]
1823 impl BehavioralScenarioReplay for SessionScenarioReplay {
1824 async fn try_replay(
1825 &self,
1826 _method: &Method,
1827 _uri: &Uri,
1828 _headers: &HeaderMap,
1829 _body: Option<&[u8]>,
1830 session_id: Option<&str>,
1831 ) -> Result<Option<BehavioralReplayResponse>> {
1832 if session_id == Some("header-session-456") {
1833 Ok(Some(BehavioralReplayResponse {
1834 status_code: 200,
1835 headers: HashMap::new(),
1836 body: b"header session response".to_vec(),
1837 timing_ms: None,
1838 content_type: "application/json".to_string(),
1839 }))
1840 } else {
1841 Ok(None)
1842 }
1843 }
1844 }
1845
1846 let scenario_replay = Arc::new(SessionScenarioReplay);
1847 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1848 .with_behavioral_scenario_replay(scenario_replay);
1849
1850 let method = Method::GET;
1851 let uri = Uri::from_static("/api/test");
1852 let mut headers = HeaderMap::new();
1853 headers.insert("x-session-id", "header-session-456".parse().unwrap());
1854
1855 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1856 assert_eq!(response.status_code, 200);
1857 assert_eq!(response.source.source_type, "behavioral_scenario");
1858 }
1859
1860 #[tokio::test]
1861 async fn test_stateful_handler_returns_response() {
1862 let temp_dir = TempDir::new().unwrap();
1863 let fixtures_dir = temp_dir.path().to_path_buf();
1864 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1865
1866 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1868
1869 let mut state_responses = HashMap::new();
1871 state_responses.insert(
1872 "initial".to_string(),
1873 crate::stateful_handler::StateResponse {
1874 status_code: 200,
1875 headers: HashMap::new(),
1876 body_template: r#"{"status": "initial", "order_id": "123"}"#.to_string(),
1877 content_type: "application/json".to_string(),
1878 },
1879 );
1880
1881 let config = crate::stateful_handler::StatefulConfig {
1882 resource_id_extract: crate::stateful_handler::ResourceIdExtract::PathParam {
1883 param: "order_id".to_string(),
1884 },
1885 resource_type: "order".to_string(),
1886 state_responses,
1887 transitions: vec![],
1888 };
1889
1890 stateful_handler.add_config("/api/orders/{order_id}".to_string(), config).await;
1891
1892 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1893 .with_stateful_handler(stateful_handler);
1894
1895 let method = Method::GET;
1896 let uri = Uri::from_static("/api/orders/123");
1897 let headers = HeaderMap::new();
1898
1899 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1901 assert_eq!(response.status_code, 200);
1902 assert_eq!(response.source.source_type, "stateful");
1903 assert_eq!(response.source.metadata.get("state"), Some(&"initial".to_string()));
1904 assert_eq!(response.source.metadata.get("resource_id"), Some(&"123".to_string()));
1905 }
1906
1907 #[tokio::test]
1908 async fn test_record_handler_path_with_no_other_handlers() {
1909 let temp_dir = TempDir::new().unwrap();
1910 let fixtures_dir = temp_dir.path().to_path_buf();
1911 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, false);
1913
1914 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1915
1916 let method = Method::GET; let uri = Uri::from_static("/api/test");
1918 let headers = HeaderMap::new();
1919
1920 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1922 assert_eq!(response.status_code, 200);
1923 assert_eq!(response.source.source_type, "record");
1924 let body_str = String::from_utf8_lossy(&response.body);
1925 assert!(body_str.contains("Request recorded"));
1926 }
1927
1928 #[tokio::test]
1929 async fn test_mock_generator_with_migration_mode() {
1930 let temp_dir = TempDir::new().unwrap();
1931 let fixtures_dir = temp_dir.path().to_path_buf();
1932 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1933
1934 let mut proxy_config =
1936 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1937 proxy_config.migration_enabled = true;
1938 proxy_config.rules.push(crate::proxy::config::ProxyRule {
1939 path_pattern: "/api/*".to_string(),
1940 target_url: "http://localhost:8080".to_string(),
1941 enabled: true,
1942 pattern: "/api/*".to_string(),
1943 upstream_url: "http://localhost:8080".to_string(),
1944 migration_mode: crate::proxy::config::MigrationMode::Mock,
1945 migration_group: None,
1946 condition: None,
1947 });
1948 proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
1951 let mock_generator = Box::new(SimpleMockGenerator::new(
1952 200,
1953 r#"{"message": "mock with migration"}"#.to_string(),
1954 ));
1955
1956 let handler = PriorityHttpHandler::new(
1957 record_replay,
1958 None,
1959 Some(proxy_handler),
1960 Some(mock_generator),
1961 );
1962
1963 let method = Method::GET;
1964 let uri = Uri::from_static("/api/test");
1965 let headers = HeaderMap::new();
1966
1967 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1969 assert_eq!(response.status_code, 200);
1970 assert_eq!(response.source.source_type, "mock");
1971 let body_str = String::from_utf8_lossy(&response.body);
1972 assert!(body_str.contains("mock with migration"));
1973 }
1974
1975 #[tokio::test]
1976 async fn test_no_handler_can_process_request() {
1977 let temp_dir = TempDir::new().unwrap();
1978 let fixtures_dir = temp_dir.path().to_path_buf();
1979 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, false, false);
1981 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1982
1983 let method = Method::GET;
1984 let uri = Uri::from_static("/api/test");
1985 let headers = HeaderMap::new();
1986
1987 let result = handler.process_request(&method, &uri, &headers, None).await;
1989 assert!(result.is_err());
1990 assert!(result.unwrap_err().to_string().contains("No handler could process"));
1991 }
1992
1993 #[tokio::test]
1994 async fn test_route_chaos_fault_injection() {
1995 let temp_dir = TempDir::new().unwrap();
1996 let fixtures_dir = temp_dir.path().to_path_buf();
1997 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1998
1999 struct FaultInjector;
2001 #[async_trait]
2002 impl RouteChaosInjectorTrait for FaultInjector {
2003 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
2004 Ok(())
2005 }
2006 fn get_fault_response(&self, method: &Method, uri: &Uri) -> Option<RouteFaultResponse> {
2007 if method == Method::GET && uri.path() == "/api/faulty" {
2008 Some(RouteFaultResponse {
2009 status_code: 503,
2010 error_message: "Service unavailable".to_string(),
2011 fault_type: "injected_fault".to_string(),
2012 })
2013 } else {
2014 None
2015 }
2016 }
2017 }
2018
2019 let route_chaos = Arc::new(FaultInjector);
2020 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
2021 .with_route_chaos_injector(route_chaos);
2022
2023 let method = Method::GET;
2024 let uri = Uri::from_static("/api/faulty");
2025 let headers = HeaderMap::new();
2026
2027 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2029 assert_eq!(response.status_code, 503);
2030 let body_str = String::from_utf8_lossy(&response.body);
2031 assert!(body_str.contains("Service unavailable"));
2032 assert!(body_str.contains("injected_failure"));
2033 }
2034}