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