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