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