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 PriorityRequest<'a> {
72 pub method: &'a Method,
74 pub uri: &'a Uri,
76 pub headers: &'a HeaderMap,
78 pub body: Option<&'a [u8]>,
80 pub fingerprint: &'a RequestFingerprint,
82}
83
84#[async_trait]
90pub trait PriorityStep: Send + Sync {
91 fn name(&self) -> &str;
93
94 fn priority(&self) -> u16;
96
97 async fn try_handle(&self, req: &PriorityRequest<'_>) -> Result<Option<PriorityResponse>>;
100}
101
102pub struct PriorityHttpHandler {
104 custom_fixture_loader: Option<Arc<CustomFixtureLoader>>,
106 record_replay: RecordReplayHandler,
108 behavioral_scenario_replay: Option<Arc<dyn BehavioralScenarioReplay + Send + Sync>>,
110 stateful_handler: Option<Arc<StatefulResponseHandler>>,
112 route_chaos_injector: Option<Arc<dyn RouteChaosInjectorTrait>>,
115 failure_injector: Option<FailureInjector>,
117 proxy_handler: Option<ProxyHandler>,
119 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
121 openapi_spec: Option<crate::openapi::spec::OpenApiSpec>,
123 continuum_engine: Option<Arc<RealityContinuumEngine>>,
125 behavioral_economics_engine: Option<Arc<RwLock<BehavioralEconomicsEngine>>>,
127 #[allow(dead_code, clippy::type_complexity)]
129 request_metrics: Arc<RwLock<HashMap<String, (u64, u64, std::time::Instant)>>>,
130}
131
132#[derive(Debug, Clone)]
135pub enum GenerationResult {
136 Generated(MockResponse),
138 NoMatchingSchema {
140 path: String,
142 method: String,
144 },
145 GeneratorDisabled,
147 AmbiguousOperation {
149 candidates: Vec<String>,
151 },
152}
153
154impl GenerationResult {
155 pub fn into_response(self) -> Option<MockResponse> {
157 match self {
158 Self::Generated(r) => Some(r),
159 _ => None,
160 }
161 }
162
163 pub fn is_generated(&self) -> bool {
165 matches!(self, Self::Generated(_))
166 }
167}
168
169pub trait MockGenerator {
171 fn generate_mock_response(
176 &self,
177 fingerprint: &RequestFingerprint,
178 headers: &HeaderMap,
179 body: Option<&[u8]>,
180 ) -> Result<GenerationResult>;
181}
182
183#[derive(Debug, Clone)]
185pub struct MockResponse {
186 pub status_code: u16,
188 pub headers: HashMap<String, String>,
190 pub body: String,
192 pub content_type: String,
194}
195
196impl PriorityHttpHandler {
197 pub fn new(
199 record_replay: RecordReplayHandler,
200 failure_injector: Option<FailureInjector>,
201 proxy_handler: Option<ProxyHandler>,
202 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
203 ) -> Self {
204 Self {
205 custom_fixture_loader: None,
206 record_replay,
207 behavioral_scenario_replay: None,
208 stateful_handler: None,
209 route_chaos_injector: None,
210 failure_injector,
211 proxy_handler,
212 mock_generator,
213 openapi_spec: None,
214 continuum_engine: None,
215 behavioral_economics_engine: None,
216 request_metrics: Arc::new(RwLock::new(HashMap::new())),
217 }
218 }
219
220 pub fn new_with_openapi(
222 record_replay: RecordReplayHandler,
223 failure_injector: Option<FailureInjector>,
224 proxy_handler: Option<ProxyHandler>,
225 mock_generator: Option<Box<dyn MockGenerator + Send + Sync>>,
226 openapi_spec: Option<crate::openapi::spec::OpenApiSpec>,
227 ) -> Self {
228 Self {
229 custom_fixture_loader: None,
230 record_replay,
231 behavioral_scenario_replay: None,
232 stateful_handler: None,
233 route_chaos_injector: None,
234 failure_injector,
235 proxy_handler,
236 mock_generator,
237 openapi_spec,
238 continuum_engine: None,
239 behavioral_economics_engine: None,
240 request_metrics: Arc::new(RwLock::new(HashMap::new())),
241 }
242 }
243
244 pub fn with_custom_fixture_loader(mut self, loader: Arc<CustomFixtureLoader>) -> Self {
246 self.custom_fixture_loader = Some(loader);
247 self
248 }
249
250 pub fn with_stateful_handler(mut self, handler: Arc<StatefulResponseHandler>) -> Self {
252 self.stateful_handler = Some(handler);
253 self
254 }
255
256 pub fn with_route_chaos_injector(mut self, injector: Arc<dyn RouteChaosInjectorTrait>) -> Self {
258 self.route_chaos_injector = Some(injector);
259 self
260 }
261
262 pub fn with_continuum_engine(mut self, engine: Arc<RealityContinuumEngine>) -> Self {
264 self.continuum_engine = Some(engine);
265 self
266 }
267
268 pub fn with_behavioral_economics_engine(
270 mut self,
271 engine: Arc<RwLock<BehavioralEconomicsEngine>>,
272 ) -> Self {
273 self.behavioral_economics_engine = Some(engine);
274 self
275 }
276
277 pub fn with_behavioral_scenario_replay(
279 mut self,
280 replay_engine: Arc<dyn BehavioralScenarioReplay + Send + Sync>,
281 ) -> Self {
282 self.behavioral_scenario_replay = Some(replay_engine);
283 self
284 }
285
286 pub async fn process_request(
288 &self,
289 method: &Method,
290 uri: &Uri,
291 headers: &HeaderMap,
292 body: Option<&[u8]>,
293 ) -> Result<PriorityResponse> {
294 let normalized_path = CustomFixtureLoader::normalize_path(uri.path());
297 let normalized_uri_str = if let Some(query) = uri.query() {
298 format!("{}?{}", normalized_path, query)
299 } else {
300 normalized_path
301 };
302 let normalized_uri = normalized_uri_str.parse::<Uri>().unwrap_or_else(|_| uri.clone());
303
304 let fingerprint = RequestFingerprint::new(method.clone(), &normalized_uri, headers, body);
305
306 if let Some(ref custom_loader) = self.custom_fixture_loader {
308 if let Some(custom_fixture) = custom_loader.load_fixture(&fingerprint) {
309 if custom_fixture.delay_ms > 0 {
311 tokio::time::sleep(tokio::time::Duration::from_millis(custom_fixture.delay_ms))
312 .await;
313 }
314
315 let response_body = match custom_fixture.response.as_str() {
317 Some(s) => s.to_string(),
318 None => serde_json::to_string(&custom_fixture.response).map_err(|e| {
319 Error::internal(format!(
320 "Failed to serialize custom fixture response: {}",
321 e
322 ))
323 })?,
324 };
325
326 let content_type = custom_fixture
328 .headers
329 .get("content-type")
330 .cloned()
331 .unwrap_or_else(|| "application/json".to_string());
332
333 return Ok(PriorityResponse {
334 source: ResponseSource::new(
335 ResponsePriority::Replay,
336 "custom_fixture".to_string(),
337 )
338 .with_metadata("fixture_path".to_string(), custom_fixture.path.clone()),
339 status_code: custom_fixture.status,
340 headers: custom_fixture.headers.clone(),
341 body: response_body.into_bytes(),
342 content_type,
343 });
344 }
345 }
346
347 if let Some(recorded_request) =
349 self.record_replay.replay_handler().load_fixture(&fingerprint).await?
350 {
351 let content_type = recorded_request
352 .response_headers
353 .get("content-type")
354 .unwrap_or(&"application/json".to_string())
355 .clone();
356
357 return Ok(PriorityResponse {
358 source: ResponseSource::new(ResponsePriority::Replay, "replay".to_string())
359 .with_metadata("fixture_path".to_string(), "recorded".to_string()),
360 status_code: recorded_request.status_code,
361 headers: recorded_request.response_headers,
362 body: recorded_request.response_body.into_bytes(),
363 content_type,
364 });
365 }
366
367 if let Some(ref scenario_replay) = self.behavioral_scenario_replay {
369 let session_id = headers
371 .get("x-session-id")
372 .or_else(|| headers.get("session-id"))
373 .and_then(|v| v.to_str().ok())
374 .map(|s| s.to_string());
375
376 if let Ok(Some(replay_response)) = scenario_replay
377 .try_replay(method, uri, headers, body, session_id.as_deref())
378 .await
379 {
380 if let Some(timing_ms) = replay_response.timing_ms {
382 tokio::time::sleep(tokio::time::Duration::from_millis(timing_ms)).await;
383 }
384 return Ok(PriorityResponse {
385 source: ResponseSource::new(
386 ResponsePriority::Replay,
387 "behavioral_scenario".to_string(),
388 )
389 .with_metadata("replay_type".to_string(), "scenario".to_string()),
390 status_code: replay_response.status_code,
391 headers: replay_response.headers,
392 body: replay_response.body,
393 content_type: replay_response.content_type,
394 });
395 }
396 }
397
398 if let Some(ref stateful_handler) = self.stateful_handler {
400 if let Some(stateful_response) =
401 stateful_handler.process_request(method, uri, headers, body).await?
402 {
403 return Ok(PriorityResponse {
404 source: ResponseSource::new(ResponsePriority::Stateful, "stateful".to_string())
405 .with_metadata("state".to_string(), stateful_response.state)
406 .with_metadata("resource_id".to_string(), stateful_response.resource_id),
407 status_code: stateful_response.status_code,
408 headers: stateful_response.headers,
409 body: stateful_response.body.into_bytes(),
410 content_type: stateful_response.content_type,
411 });
412 }
413 }
414
415 if let Some(ref route_chaos) = self.route_chaos_injector {
417 if let Err(e) = route_chaos.inject_latency(method, uri).await {
419 tracing::warn!("Failed to inject per-route latency: {}", e);
420 }
421
422 if let Some(fault_response) = route_chaos.get_fault_response(method, uri) {
424 let error_response = serde_json::json!({
425 "error": fault_response.error_message,
426 "injected_failure": true,
427 "fault_type": fault_response.fault_type,
428 "timestamp": chrono::Utc::now().to_rfc3339()
429 });
430
431 return Ok(PriorityResponse {
432 source: ResponseSource::new(
433 ResponsePriority::Fail,
434 "route_fault_injection".to_string(),
435 )
436 .with_metadata("fault_type".to_string(), fault_response.fault_type)
437 .with_metadata("error_message".to_string(), fault_response.error_message),
438 status_code: fault_response.status_code,
439 headers: HashMap::new(),
440 body: serde_json::to_string(&error_response)?.into_bytes(),
441 content_type: "application/json".to_string(),
442 });
443 }
444 }
445
446 if let Some(ref failure_injector) = self.failure_injector {
448 let tags = if let Some(ref spec) = self.openapi_spec {
449 fingerprint.openapi_tags(spec).unwrap_or_else(|| fingerprint.tags())
450 } else {
451 fingerprint.tags()
452 };
453 if let Some((status_code, error_message)) = failure_injector.process_request(&tags) {
454 let error_response = serde_json::json!({
455 "error": error_message,
456 "injected_failure": true,
457 "timestamp": chrono::Utc::now().to_rfc3339()
458 });
459
460 return Ok(PriorityResponse {
461 source: ResponseSource::new(
462 ResponsePriority::Fail,
463 "failure_injection".to_string(),
464 )
465 .with_metadata("error_message".to_string(), error_message),
466 status_code,
467 headers: HashMap::new(),
468 body: serde_json::to_string(&error_response)?.into_bytes(),
469 content_type: "application/json".to_string(),
470 });
471 }
472 }
473
474 let should_blend = if let Some(ref continuum_engine) = self.continuum_engine {
476 continuum_engine.is_enabled().await
477 } else {
478 false
479 };
480
481 if let Some(ref proxy_handler) = self.proxy_handler {
483 let migration_mode = if proxy_handler.config.migration_enabled {
485 proxy_handler.config.get_effective_migration_mode(uri.path())
486 } else {
487 None
488 };
489
490 if let Some(crate::proxy::config::MigrationMode::Mock) = migration_mode {
492 } else if proxy_handler.config.should_proxy_with_condition(method, uri, headers, body) {
494 let is_shadow = proxy_handler.config.should_shadow(uri.path());
496
497 if should_blend {
499 let proxy_future = proxy_handler.proxy_request(method, uri, headers, body);
501 let mock_result = if let Some(ref mock_generator) = self.mock_generator {
502 mock_generator
503 .generate_mock_response(&fingerprint, headers, body)
504 .map(|r| r.into_response())
505 } else {
506 Ok(None)
507 };
508
509 let proxy_result = proxy_future.await;
511
512 match (proxy_result, mock_result) {
514 (Ok(proxy_response), Ok(Some(mock_response))) => {
515 if let Some(ref continuum_engine) = self.continuum_engine {
517 let blend_ratio =
518 continuum_engine.get_blend_ratio(uri.path()).await;
519 let blender = continuum_engine.blender();
520
521 let mock_body_str = &mock_response.body;
523 let real_body_bytes =
524 proxy_response.body.clone().unwrap_or_default();
525 let real_body_str = String::from_utf8_lossy(&real_body_bytes);
526
527 let mock_json: serde_json::Value =
528 serde_json::from_str(mock_body_str)
529 .unwrap_or_else(|_| serde_json::json!({}));
530 let real_json: serde_json::Value =
531 serde_json::from_str(&real_body_str)
532 .unwrap_or_else(|_| serde_json::json!({}));
533
534 let blended_json =
536 blender.blend_responses(&mock_json, &real_json, blend_ratio);
537 let blended_body = serde_json::to_string(&blended_json)
538 .unwrap_or_else(|_| real_body_str.to_string());
539
540 let blended_status = blender.blend_status_code(
542 mock_response.status_code,
543 proxy_response.status_code,
544 blend_ratio,
545 );
546
547 let mut proxy_headers = HashMap::new();
549 for (key, value) in proxy_response.headers.iter() {
550 if let Ok(value_str) = value.to_str() {
551 proxy_headers.insert(
552 key.as_str().to_string(),
553 value_str.to_string(),
554 );
555 }
556 }
557 let blended_headers = blender.blend_headers(
558 &mock_response.headers,
559 &proxy_headers,
560 blend_ratio,
561 );
562
563 let content_type = blended_headers
564 .get("content-type")
565 .cloned()
566 .or_else(|| {
567 proxy_response
568 .headers
569 .get("content-type")
570 .and_then(|v| v.to_str().ok())
571 .map(|s| s.to_string())
572 })
573 .unwrap_or_else(|| "application/json".to_string());
574
575 tracing::info!(
576 path = %uri.path(),
577 blend_ratio = blend_ratio,
578 "Reality Continuum: blended mock and real responses"
579 );
580
581 let mut source = ResponseSource::new(
582 ResponsePriority::Proxy,
583 "continuum".to_string(),
584 )
585 .with_metadata("blend_ratio".to_string(), blend_ratio.to_string())
586 .with_metadata(
587 "upstream_url".to_string(),
588 proxy_handler.config.get_upstream_url(uri.path()),
589 );
590
591 if let Some(mode) = migration_mode {
592 source = source.with_metadata(
593 "migration_mode".to_string(),
594 format!("{:?}", mode),
595 );
596 }
597
598 return Ok(PriorityResponse {
599 source,
600 status_code: blended_status,
601 headers: blended_headers,
602 body: blended_body.into_bytes(),
603 content_type,
604 });
605 }
606 }
607 (Ok(_proxy_response), Ok(None)) => {
608 tracing::debug!(
610 path = %uri.path(),
611 "Continuum: mock generation failed, using real response"
612 );
613 }
615 (Ok(_proxy_response), Err(_)) => {
616 tracing::debug!(
618 path = %uri.path(),
619 "Continuum: mock generation failed, using real response"
620 );
621 }
623 (Err(e), Ok(Some(mock_response))) => {
624 tracing::debug!(
626 path = %uri.path(),
627 error = %e,
628 "Continuum: proxy failed, using mock response"
629 );
630 let mut source = ResponseSource::new(
632 ResponsePriority::Mock,
633 "continuum_fallback".to_string(),
634 )
635 .with_metadata("generated_from".to_string(), "openapi_spec".to_string())
636 .with_metadata(
637 "fallback_reason".to_string(),
638 "proxy_failed".to_string(),
639 );
640
641 if let Some(mode) = migration_mode {
642 source = source.with_metadata(
643 "migration_mode".to_string(),
644 format!("{:?}", mode),
645 );
646 }
647
648 return Ok(PriorityResponse {
649 source,
650 status_code: mock_response.status_code,
651 headers: mock_response.headers,
652 body: mock_response.body.into_bytes(),
653 content_type: mock_response.content_type,
654 });
655 }
656 (Err(e), _) => {
657 tracing::warn!(
659 path = %uri.path(),
660 error = %e,
661 "Continuum: both proxy and mock failed"
662 );
663 if let Some(crate::proxy::config::MigrationMode::Real) = migration_mode
665 {
666 return Err(Error::internal(format!(
667 "Proxy request failed in real mode: {}",
668 e
669 )));
670 }
671 }
673 }
674 }
675
676 match proxy_handler.proxy_request(method, uri, headers, body).await {
678 Ok(proxy_response) => {
679 let mut response_headers = HashMap::new();
680 for (key, value) in proxy_response.headers.iter() {
681 let key_str = key.as_str();
682 if let Ok(value_str) = value.to_str() {
683 response_headers.insert(key_str.to_string(), value_str.to_string());
684 }
685 }
686
687 let content_type = response_headers
688 .get("content-type")
689 .unwrap_or(&"application/json".to_string())
690 .clone();
691
692 if is_shadow {
694 if let Some(ref mock_generator) = self.mock_generator {
695 if let Ok(GenerationResult::Generated(mock_response)) =
696 mock_generator.generate_mock_response(
697 &fingerprint,
698 headers,
699 body,
700 )
701 {
702 tracing::info!(
704 path = %uri.path(),
705 real_status = proxy_response.status_code,
706 mock_status = mock_response.status_code,
707 "Shadow mode: comparing real and mock responses"
708 );
709
710 let real_body_bytes =
712 proxy_response.body.clone().unwrap_or_default();
713 let real_body = String::from_utf8_lossy(&real_body_bytes);
714 let mock_body = &mock_response.body;
715
716 if real_body != *mock_body {
717 tracing::warn!(
718 path = %uri.path(),
719 "Shadow mode: real and mock responses differ"
720 );
721 }
722 }
723 }
724 }
725
726 let mut source = ResponseSource::new(
727 ResponsePriority::Proxy,
728 if is_shadow {
729 "shadow".to_string()
730 } else {
731 "proxy".to_string()
732 },
733 )
734 .with_metadata(
735 "upstream_url".to_string(),
736 proxy_handler.config.get_upstream_url(uri.path()),
737 );
738
739 if let Some(mode) = migration_mode {
740 source = source
741 .with_metadata("migration_mode".to_string(), format!("{:?}", mode));
742 }
743
744 return Ok(PriorityResponse {
745 source,
746 status_code: proxy_response.status_code,
747 headers: response_headers,
748 body: proxy_response.body.unwrap_or_default(),
749 content_type,
750 });
751 }
752 Err(e) => {
753 tracing::warn!("Proxy request failed: {}", e);
754 if let Some(crate::proxy::config::MigrationMode::Real) = migration_mode {
756 return Err(Error::internal(format!(
757 "Proxy request failed in real mode: {}",
758 e
759 )));
760 }
761 }
763 }
764 }
765 }
766
767 if let Some(ref mock_generator) = self.mock_generator {
769 let migration_mode = if let Some(ref proxy_handler) = self.proxy_handler {
771 if proxy_handler.config.migration_enabled {
772 proxy_handler.config.get_effective_migration_mode(uri.path())
773 } else {
774 None
775 }
776 } else {
777 None
778 };
779
780 if let GenerationResult::Generated(mock_response) =
781 mock_generator.generate_mock_response(&fingerprint, headers, body)?
782 {
783 let mut source = ResponseSource::new(ResponsePriority::Mock, "mock".to_string())
784 .with_metadata("generated_from".to_string(), "openapi_spec".to_string());
785
786 if let Some(mode) = migration_mode {
787 source =
788 source.with_metadata("migration_mode".to_string(), format!("{:?}", mode));
789 }
790
791 return Ok(PriorityResponse {
792 source,
793 status_code: mock_response.status_code,
794 headers: mock_response.headers,
795 body: mock_response.body.into_bytes(),
796 content_type: mock_response.content_type,
797 });
798 }
799 }
800
801 if self.record_replay.record_handler().should_record(method) {
803 let default_response = serde_json::json!({
805 "message": "Request recorded for future replay",
806 "timestamp": chrono::Utc::now().to_rfc3339(),
807 "fingerprint": fingerprint.to_hash()
808 });
809
810 let response_body = serde_json::to_string(&default_response)?;
811 let status_code = 200;
812
813 self.record_replay
815 .record_handler()
816 .record_request(&fingerprint, status_code, headers, &response_body, None)
817 .await?;
818
819 return Ok(PriorityResponse {
820 source: ResponseSource::new(ResponsePriority::Record, "record".to_string())
821 .with_metadata("recorded".to_string(), "true".to_string()),
822 status_code,
823 headers: HashMap::new(),
824 body: response_body.into_bytes(),
825 content_type: "application/json".to_string(),
826 });
827 }
828
829 Err(Error::internal("No handler could process the request".to_string()))
831 }
832
833 #[allow(dead_code)]
838 async fn apply_behavioral_economics(
839 &self,
840 response: PriorityResponse,
841 _method: &Method,
842 uri: &Uri,
843 latency_ms: Option<u64>,
844 ) -> Result<PriorityResponse> {
845 if let Some(ref engine) = self.behavioral_economics_engine {
846 let engine = engine.read().await;
847 let evaluator = engine.condition_evaluator();
848
849 {
851 let mut eval = evaluator.write().await;
852 if let Some(latency) = latency_ms {
853 eval.update_latency(uri.path(), latency);
854 }
855
856 let endpoint = uri.path().to_string();
858 let mut metrics = self.request_metrics.write().await;
859 let now = std::time::Instant::now();
860
861 let (request_count, error_count, last_request_time) =
863 metrics.entry(endpoint.clone()).or_insert_with(|| (0, 0, now));
864
865 *request_count += 1;
867
868 if response.status_code >= 400 {
870 *error_count += 1;
871 }
872
873 let error_rate = if *request_count > 0 {
875 *error_count as f64 / *request_count as f64
876 } else {
877 0.0
878 };
879 eval.update_error_rate(&endpoint, error_rate);
880
881 let time_elapsed = now.duration_since(*last_request_time).as_secs_f64();
883 if time_elapsed > 0.0 {
884 let rps = *request_count as f64 / time_elapsed.max(1.0);
885 eval.update_load(rps);
886 }
887
888 if time_elapsed > 60.0 {
890 *request_count = 1;
891 *error_count = if response.status_code >= 400 { 1 } else { 0 };
892 *last_request_time = now;
893 } else {
894 *last_request_time = now;
895 }
896 }
897
898 let executed_actions = engine.evaluate().await?;
900
901 if !executed_actions.is_empty() {
903 tracing::debug!(
904 "Behavioral economics engine executed {} actions",
905 executed_actions.len()
906 );
907 }
911 }
912
913 Ok(response)
914 }
915}
916
917#[derive(Debug, Clone)]
919pub struct PriorityResponse {
920 pub source: ResponseSource,
922 pub status_code: u16,
924 pub headers: HashMap<String, String>,
926 pub body: Vec<u8>,
928 pub content_type: String,
930}
931
932impl PriorityResponse {
933 pub fn to_axum_response(self) -> axum::response::Response {
935 let mut response = axum::response::Response::new(axum::body::Body::from(self.body));
936 *response.status_mut() = StatusCode::from_u16(self.status_code).unwrap_or(StatusCode::OK);
937
938 for (key, value) in self.headers {
940 if let (Ok(header_name), Ok(header_value)) =
941 (key.parse::<axum::http::HeaderName>(), value.parse::<axum::http::HeaderValue>())
942 {
943 response.headers_mut().insert(header_name, header_value);
944 }
945 }
946
947 if !response.headers().contains_key("content-type") {
949 if let Ok(header_value) = self.content_type.parse::<axum::http::HeaderValue>() {
950 response.headers_mut().insert("content-type", header_value);
951 }
952 }
953
954 response
955 }
956}
957
958pub struct CustomFixtureStep {
962 loader: Arc<CustomFixtureLoader>,
963}
964
965impl CustomFixtureStep {
966 pub fn new(loader: Arc<CustomFixtureLoader>) -> Self {
968 Self { loader }
969 }
970}
971
972#[async_trait]
973impl PriorityStep for CustomFixtureStep {
974 fn name(&self) -> &str {
975 "custom_fixture"
976 }
977 fn priority(&self) -> u16 {
978 0
979 }
980 async fn try_handle(&self, req: &PriorityRequest<'_>) -> Result<Option<PriorityResponse>> {
981 if let Some(custom_fixture) = self.loader.load_fixture(req.fingerprint) {
982 if custom_fixture.delay_ms > 0 {
983 tokio::time::sleep(tokio::time::Duration::from_millis(custom_fixture.delay_ms))
984 .await;
985 }
986
987 let response_body = match custom_fixture.response.as_str() {
988 Some(s) => s.to_string(),
989 None => serde_json::to_string(&custom_fixture.response)
990 .map_err(|e| Error::internal(format!("Failed to serialize fixture: {}", e)))?,
991 };
992
993 let content_type = custom_fixture
994 .headers
995 .get("content-type")
996 .cloned()
997 .unwrap_or_else(|| "application/json".to_string());
998
999 return Ok(Some(PriorityResponse {
1000 source: ResponseSource::new(ResponsePriority::Replay, "custom_fixture".to_string())
1001 .with_metadata("fixture_path".to_string(), custom_fixture.path.clone()),
1002 status_code: custom_fixture.status,
1003 headers: custom_fixture.headers.clone(),
1004 body: response_body.into_bytes(),
1005 content_type,
1006 }));
1007 }
1008 Ok(None)
1009 }
1010}
1011
1012pub struct FailureInjectionStep {
1014 injector: FailureInjector,
1015 spec: Option<crate::openapi::spec::OpenApiSpec>,
1016}
1017
1018impl FailureInjectionStep {
1019 pub fn new(injector: FailureInjector, spec: Option<crate::openapi::spec::OpenApiSpec>) -> Self {
1021 Self { injector, spec }
1022 }
1023}
1024
1025#[async_trait]
1026impl PriorityStep for FailureInjectionStep {
1027 fn name(&self) -> &str {
1028 "failure_injection"
1029 }
1030 fn priority(&self) -> u16 {
1031 300
1032 }
1033 async fn try_handle(&self, req: &PriorityRequest<'_>) -> Result<Option<PriorityResponse>> {
1034 let tags = if let Some(ref spec) = self.spec {
1035 req.fingerprint.openapi_tags(spec).unwrap_or_else(|| req.fingerprint.tags())
1036 } else {
1037 req.fingerprint.tags()
1038 };
1039 if let Some((status_code, error_message)) = self.injector.process_request(&tags) {
1040 let error_response = serde_json::json!({
1041 "error": error_message,
1042 "injected_failure": true,
1043 "timestamp": chrono::Utc::now().to_rfc3339()
1044 });
1045
1046 return Ok(Some(PriorityResponse {
1047 source: ResponseSource::new(
1048 ResponsePriority::Fail,
1049 "failure_injection".to_string(),
1050 )
1051 .with_metadata("error_message".to_string(), error_message),
1052 status_code,
1053 headers: HashMap::new(),
1054 body: serde_json::to_string(&error_response)?.into_bytes(),
1055 content_type: "application/json".to_string(),
1056 }));
1057 }
1058 Ok(None)
1059 }
1060}
1061
1062pub struct SimpleMockGenerator {
1064 pub default_status: u16,
1066 pub default_body: String,
1068}
1069
1070impl SimpleMockGenerator {
1071 pub fn new(default_status: u16, default_body: String) -> Self {
1073 Self {
1074 default_status,
1075 default_body,
1076 }
1077 }
1078}
1079
1080impl MockGenerator for SimpleMockGenerator {
1081 fn generate_mock_response(
1082 &self,
1083 _fingerprint: &RequestFingerprint,
1084 _headers: &HeaderMap,
1085 _body: Option<&[u8]>,
1086 ) -> Result<GenerationResult> {
1087 Ok(GenerationResult::Generated(MockResponse {
1088 status_code: self.default_status,
1089 headers: HashMap::new(),
1090 body: self.default_body.clone(),
1091 content_type: "application/json".to_string(),
1092 }))
1093 }
1094}
1095
1096#[cfg(test)]
1097mod tests {
1098 use super::*;
1099 use tempfile::TempDir;
1100
1101 struct MockRouteChaosInjector;
1103
1104 #[async_trait]
1105 impl RouteChaosInjectorTrait for MockRouteChaosInjector {
1106 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1107 Ok(())
1108 }
1109
1110 fn get_fault_response(&self, _method: &Method, _uri: &Uri) -> Option<RouteFaultResponse> {
1111 Some(RouteFaultResponse {
1112 status_code: 503,
1113 error_message: "Service unavailable".to_string(),
1114 fault_type: "test_fault".to_string(),
1115 })
1116 }
1117 }
1118
1119 struct MockBehavioralScenarioReplay;
1120
1121 #[async_trait]
1122 impl BehavioralScenarioReplay for MockBehavioralScenarioReplay {
1123 async fn try_replay(
1124 &self,
1125 _method: &Method,
1126 _uri: &Uri,
1127 _headers: &HeaderMap,
1128 _body: Option<&[u8]>,
1129 _session_id: Option<&str>,
1130 ) -> Result<Option<BehavioralReplayResponse>> {
1131 Ok(Some(BehavioralReplayResponse {
1132 status_code: 200,
1133 headers: HashMap::new(),
1134 body: b"scenario response".to_vec(),
1135 timing_ms: Some(100),
1136 content_type: "application/json".to_string(),
1137 }))
1138 }
1139 }
1140
1141 #[tokio::test]
1142 async fn test_priority_chain() {
1143 let temp_dir = TempDir::new().unwrap();
1144 let fixtures_dir = temp_dir.path().to_path_buf();
1145
1146 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1147 let mock_generator =
1148 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock response"}"#.to_string()));
1149
1150 let handler = PriorityHttpHandler::new_with_openapi(
1151 record_replay,
1152 None, None, Some(mock_generator),
1155 None, );
1157
1158 let method = Method::GET;
1159 let uri = Uri::from_static("/api/test");
1160 let headers = HeaderMap::new();
1161
1162 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1163
1164 assert_eq!(response.status_code, 200);
1165 assert_eq!(response.source.source_type, "mock");
1166 }
1167
1168 #[tokio::test]
1169 async fn test_builder_methods() {
1170 let temp_dir = TempDir::new().unwrap();
1171 let fixtures_dir = temp_dir.path().to_path_buf();
1172 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1173 let mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1174
1175 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
1176
1177 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
1179 let handler = handler.with_custom_fixture_loader(custom_loader);
1180 assert!(handler.custom_fixture_loader.is_some());
1181
1182 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1184 let handler = handler.with_stateful_handler(stateful_handler);
1185 assert!(handler.stateful_handler.is_some());
1186
1187 let route_chaos = Arc::new(MockRouteChaosInjector);
1189 let handler = handler.with_route_chaos_injector(route_chaos);
1190 assert!(handler.route_chaos_injector.is_some());
1191
1192 let continuum_engine = Arc::new(RealityContinuumEngine::new(
1194 crate::reality_continuum::config::ContinuumConfig::default(),
1195 ));
1196 let handler = handler.with_continuum_engine(continuum_engine);
1197 assert!(handler.continuum_engine.is_some());
1198
1199 let behavioral_engine = Arc::new(RwLock::new(
1201 BehavioralEconomicsEngine::new(
1202 crate::behavioral_economics::config::BehavioralEconomicsConfig::default(),
1203 )
1204 .unwrap(),
1205 ));
1206 let handler = handler.with_behavioral_economics_engine(behavioral_engine);
1207 assert!(handler.behavioral_economics_engine.is_some());
1208
1209 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1211 let handler = handler.with_behavioral_scenario_replay(scenario_replay);
1212 assert!(handler.behavioral_scenario_replay.is_some());
1213 }
1214
1215 #[tokio::test]
1216 async fn test_custom_fixture_priority() {
1217 let temp_dir = TempDir::new().unwrap();
1218 let fixtures_dir = temp_dir.path().to_path_buf();
1219 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1220 let custom_loader = Arc::new(CustomFixtureLoader::new(temp_dir.path().to_path_buf(), true));
1221
1222 let fixture_path = temp_dir.path().join("custom_fixture.json");
1224 std::fs::write(
1225 &fixture_path,
1226 r#"{"status": 201, "response": {"message": "custom"}, "headers": {"x-custom": "value"}}"#,
1227 )
1228 .unwrap();
1229
1230 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1231 .with_custom_fixture_loader(custom_loader);
1232
1233 let _method = Method::GET;
1234 let _uri = Uri::from_static("/api/test");
1235 let _headers = HeaderMap::new();
1236
1237 let _handler = handler; }
1241
1242 #[tokio::test]
1243 async fn test_route_chaos_injection() {
1244 let temp_dir = TempDir::new().unwrap();
1245 let fixtures_dir = temp_dir.path().to_path_buf();
1246 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1247 let route_chaos = Arc::new(MockRouteChaosInjector);
1248
1249 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1250 .with_route_chaos_injector(route_chaos);
1251
1252 let method = Method::GET;
1253 let uri = Uri::from_static("/api/test");
1254 let headers = HeaderMap::new();
1255
1256 let response = handler.process_request(&method, &uri, &headers, None).await;
1257
1258 if let Ok(resp) = response {
1260 assert_eq!(resp.status_code, 503);
1261 assert_eq!(resp.source.source_type, "route_fault_injection");
1262 }
1263 }
1264
1265 #[tokio::test]
1266 async fn test_behavioral_scenario_replay() {
1267 let temp_dir = TempDir::new().unwrap();
1268 let fixtures_dir = temp_dir.path().to_path_buf();
1269 let record_replay = RecordReplayHandler::new(fixtures_dir, true, true, false);
1270 let scenario_replay = Arc::new(MockBehavioralScenarioReplay);
1271
1272 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1273 .with_behavioral_scenario_replay(scenario_replay);
1274
1275 let method = Method::GET;
1276 let uri = Uri::from_static("/api/test");
1277 let mut headers = HeaderMap::new();
1278 headers.insert("x-session-id", "test-session".parse().unwrap());
1279
1280 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1281
1282 assert_eq!(response.status_code, 200);
1283 assert_eq!(response.source.source_type, "behavioral_scenario");
1284 assert_eq!(response.body, b"scenario response");
1285 }
1286
1287 #[tokio::test]
1288 async fn test_priority_response_to_axum() {
1289 let response = PriorityResponse {
1290 source: ResponseSource::new(ResponsePriority::Mock, "test".to_string()),
1291 status_code: 201,
1292 headers: {
1293 let mut h = HashMap::new();
1294 h.insert("x-custom".to_string(), "value".to_string());
1295 h
1296 },
1297 body: b"test body".to_vec(),
1298 content_type: "application/json".to_string(),
1299 };
1300
1301 let axum_response = response.to_axum_response();
1302 assert_eq!(axum_response.status(), StatusCode::CREATED);
1303 }
1304
1305 #[tokio::test]
1306 async fn test_simple_mock_generator() {
1307 let generator = SimpleMockGenerator::new(404, r#"{"error": "not found"}"#.to_string());
1308 let fingerprint = RequestFingerprint::new(
1309 Method::GET,
1310 &Uri::from_static("/api/test"),
1311 &HeaderMap::new(),
1312 None,
1313 );
1314
1315 let result =
1316 generator.generate_mock_response(&fingerprint, &HeaderMap::new(), None).unwrap();
1317
1318 assert!(result.is_generated());
1319 let mock_response = result.into_response().unwrap();
1320 assert_eq!(mock_response.status_code, 404);
1321 assert_eq!(mock_response.body, r#"{"error": "not found"}"#);
1322 }
1323
1324 #[tokio::test]
1325 async fn test_new_vs_new_with_openapi() {
1326 let temp_dir = TempDir::new().unwrap();
1327 let fixtures_dir = temp_dir.path().to_path_buf();
1328 let _record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1329 let _mock_generator = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1330
1331 let record_replay1 = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1333 let mock_generator1 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1334 let handler1 = PriorityHttpHandler::new(record_replay1, None, None, Some(mock_generator1));
1335 assert!(handler1.openapi_spec.is_none());
1336
1337 let record_replay2 = RecordReplayHandler::new(fixtures_dir, true, true, false);
1339 let mock_generator2 = Box::new(SimpleMockGenerator::new(200, "{}".to_string()));
1340 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1341 r#"openapi: 3.0.0
1342info:
1343 title: Test API
1344 version: 1.0.0
1345paths:
1346 /test:
1347 get:
1348 responses:
1349 '200':
1350 description: OK
1351"#,
1352 Some("yaml"),
1353 )
1354 .unwrap();
1355 let handler2 = PriorityHttpHandler::new_with_openapi(
1356 record_replay2,
1357 None,
1358 None,
1359 Some(mock_generator2),
1360 Some(openapi_spec),
1361 );
1362 assert!(handler2.openapi_spec.is_some());
1363 }
1364
1365 #[tokio::test]
1366 async fn test_custom_fixture_with_delay() {
1367 let temp_dir = TempDir::new().unwrap();
1368 let fixtures_dir = temp_dir.path().to_path_buf();
1369 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1370
1371 let fixture_content = r#"{
1373 "method": "GET",
1374 "path": "/api/test",
1375 "status": 200,
1376 "response": {"message": "delayed response"},
1377 "delay_ms": 10
1378}"#;
1379 let fixture_file = fixtures_dir.join("test.json");
1380 std::fs::write(&fixture_file, fixture_content).unwrap();
1381
1382 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1383 custom_loader.load_fixtures().await.unwrap();
1384
1385 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1386 .with_custom_fixture_loader(Arc::new(custom_loader));
1387
1388 let method = Method::GET;
1389 let uri = Uri::from_static("/api/test");
1390 let headers = HeaderMap::new();
1391
1392 let start = std::time::Instant::now();
1393 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1394 let elapsed = start.elapsed();
1395
1396 assert_eq!(response.status_code, 200);
1397 assert_eq!(response.source.source_type, "custom_fixture");
1398 assert!(elapsed.as_millis() >= 10); }
1400
1401 #[tokio::test]
1402 async fn test_custom_fixture_with_non_string_response() {
1403 let temp_dir = TempDir::new().unwrap();
1404 let fixtures_dir = temp_dir.path().to_path_buf();
1405 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1406
1407 let fixture_content = r#"{
1409 "method": "GET",
1410 "path": "/api/test",
1411 "status": 201,
1412 "response": {"id": 123, "name": "test"},
1413 "headers": {"content-type": "application/json"}
1414}"#;
1415 let fixture_file = fixtures_dir.join("test.json");
1416 std::fs::write(&fixture_file, fixture_content).unwrap();
1417
1418 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1419 custom_loader.load_fixtures().await.unwrap();
1420
1421 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1422 .with_custom_fixture_loader(Arc::new(custom_loader));
1423
1424 let method = Method::GET;
1425 let uri = Uri::from_static("/api/test");
1426 let headers = HeaderMap::new();
1427
1428 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1429
1430 assert_eq!(response.status_code, 201);
1431 assert_eq!(response.source.source_type, "custom_fixture");
1432 assert!(!response.body.is_empty());
1433 let body_str = String::from_utf8_lossy(&response.body);
1434 assert!(body_str.contains("id"));
1435 }
1436
1437 #[tokio::test]
1438 async fn test_custom_fixture_with_custom_content_type() {
1439 let temp_dir = TempDir::new().unwrap();
1440 let fixtures_dir = temp_dir.path().to_path_buf();
1441 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1442
1443 let fixture_content = r#"{
1445 "method": "GET",
1446 "path": "/api/test",
1447 "status": 200,
1448 "response": "text response",
1449 "headers": {"content-type": "text/plain"}
1450}"#;
1451 let fixture_file = fixtures_dir.join("test.json");
1452 std::fs::write(&fixture_file, fixture_content).unwrap();
1453
1454 let mut custom_loader = CustomFixtureLoader::new(fixtures_dir.clone(), true);
1455 custom_loader.load_fixtures().await.unwrap();
1456
1457 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1458 .with_custom_fixture_loader(Arc::new(custom_loader));
1459
1460 let method = Method::GET;
1461 let uri = Uri::from_static("/api/test");
1462 let headers = HeaderMap::new();
1463
1464 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1465
1466 assert_eq!(response.status_code, 200);
1467 assert_eq!(response.content_type, "text/plain");
1468 }
1469
1470 #[tokio::test]
1471 async fn test_stateful_handler_path() {
1472 let temp_dir = TempDir::new().unwrap();
1473 let fixtures_dir = temp_dir.path().to_path_buf();
1474 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1475
1476 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1478
1479 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1482 .with_stateful_handler(stateful_handler);
1483
1484 let method = Method::GET;
1485 let uri = Uri::from_static("/api/test");
1486 let headers = HeaderMap::new();
1487
1488 let _response = handler.process_request(&method, &uri, &headers, None).await;
1491 }
1493
1494 #[tokio::test]
1495 async fn test_route_chaos_latency_injection() {
1496 let temp_dir = TempDir::new().unwrap();
1497 let fixtures_dir = temp_dir.path().to_path_buf();
1498 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1499
1500 struct LatencyInjector;
1502 #[async_trait]
1503 impl RouteChaosInjectorTrait for LatencyInjector {
1504 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1505 tokio::time::sleep(tokio::time::Duration::from_millis(20)).await;
1506 Ok(())
1507 }
1508 fn get_fault_response(
1509 &self,
1510 _method: &Method,
1511 _uri: &Uri,
1512 ) -> Option<RouteFaultResponse> {
1513 None
1514 }
1515 }
1516
1517 let route_chaos = Arc::new(LatencyInjector);
1518 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1519 .with_route_chaos_injector(route_chaos);
1520
1521 let method = Method::GET;
1522 let uri = Uri::from_static("/api/test");
1523 let headers = HeaderMap::new();
1524
1525 let start = std::time::Instant::now();
1526 let _response = handler.process_request(&method, &uri, &headers, None).await;
1527 let elapsed = start.elapsed();
1528
1529 assert!(elapsed.as_millis() >= 20);
1531 }
1532
1533 #[tokio::test]
1534 async fn test_failure_injection_path() {
1535 let temp_dir = TempDir::new().unwrap();
1536 let fixtures_dir = temp_dir.path().to_path_buf();
1537 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1538
1539 let failure_config = crate::failure_injection::FailureConfig {
1541 global_error_rate: 1.0, default_status_codes: vec![500], ..Default::default()
1544 };
1545
1546 let failure_injector = FailureInjector::new(Some(failure_config), true);
1547
1548 let openapi_spec = crate::openapi::spec::OpenApiSpec::from_string(
1549 r#"openapi: 3.0.0
1550info:
1551 title: Test API
1552 version: 1.0.0
1553paths:
1554 /api/test:
1555 get:
1556 tags: [test]
1557 responses:
1558 '200':
1559 description: OK
1560"#,
1561 Some("yaml"),
1562 )
1563 .unwrap();
1564
1565 let handler = PriorityHttpHandler::new_with_openapi(
1566 record_replay,
1567 Some(failure_injector),
1568 None,
1569 None,
1570 Some(openapi_spec),
1571 );
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();
1578
1579 assert_eq!(response.status_code, 500);
1580 assert_eq!(response.source.source_type, "failure_injection");
1581 let body_str = String::from_utf8_lossy(&response.body);
1582 assert!(body_str.contains("Injected failure")); }
1584
1585 #[tokio::test]
1586 async fn test_record_handler_path() {
1587 let temp_dir = TempDir::new().unwrap();
1588 let fixtures_dir = temp_dir.path().to_path_buf();
1589 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, true);
1592
1593 let mock_generator =
1595 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1596 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator));
1597
1598 let method = Method::POST; let uri = Uri::from_static("/api/test");
1600 let headers = HeaderMap::new();
1601
1602 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1605
1606 assert_eq!(response.status_code, 200);
1607 assert_eq!(response.source.source_type, "mock");
1609 }
1610
1611 #[tokio::test]
1612 async fn test_behavioral_economics_engine_path() {
1613 let temp_dir = TempDir::new().unwrap();
1614 let fixtures_dir = temp_dir.path().to_path_buf();
1615 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1616 let mock_generator =
1617 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1618
1619 let be_config = crate::behavioral_economics::config::BehavioralEconomicsConfig::default();
1620 let be_engine = Arc::new(RwLock::new(BehavioralEconomicsEngine::new(be_config).unwrap()));
1621
1622 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1623 .with_behavioral_economics_engine(be_engine);
1624
1625 let method = Method::GET;
1626 let uri = Uri::from_static("/api/test");
1627 let headers = HeaderMap::new();
1628
1629 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1630
1631 assert_eq!(response.status_code, 200);
1633 }
1634
1635 #[tokio::test]
1636 async fn test_replay_handler_with_recorded_fixture() {
1637 let temp_dir = TempDir::new().unwrap();
1638 let fixtures_dir = temp_dir.path().to_path_buf();
1639 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1641
1642 let method = Method::GET;
1643 let uri = Uri::from_static("/api/test");
1644 let mut headers = HeaderMap::new();
1645 headers.insert("content-type", "application/json".parse().unwrap());
1646
1647 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1649 record_replay
1650 .record_handler()
1651 .record_request(
1652 &fingerprint,
1653 200,
1654 &headers,
1655 r#"{"message": "recorded response"}"#,
1656 None,
1657 )
1658 .await
1659 .unwrap();
1660
1661 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1663
1664 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1666
1667 assert_eq!(response.status_code, 200);
1668 assert_eq!(response.source.source_type, "replay");
1669 let body_str = String::from_utf8_lossy(&response.body);
1670 assert!(body_str.contains("recorded response"));
1671 }
1672
1673 #[tokio::test]
1674 async fn test_behavioral_scenario_replay_with_cookies() {
1675 let temp_dir = TempDir::new().unwrap();
1676 let fixtures_dir = temp_dir.path().to_path_buf();
1677 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1678
1679 struct CookieScenarioReplay;
1683 #[async_trait]
1684 impl BehavioralScenarioReplay for CookieScenarioReplay {
1685 async fn try_replay(
1686 &self,
1687 _method: &Method,
1688 _uri: &Uri,
1689 _headers: &HeaderMap,
1690 _body: Option<&[u8]>,
1691 session_id: Option<&str>,
1692 ) -> Result<Option<BehavioralReplayResponse>> {
1693 if session_id == Some("header-session-123") {
1696 Ok(Some(BehavioralReplayResponse {
1697 status_code: 200,
1698 headers: HashMap::new(),
1699 body: b"header scenario response".to_vec(),
1700 timing_ms: None,
1701 content_type: "application/json".to_string(),
1702 }))
1703 } else {
1704 Ok(None)
1705 }
1706 }
1707 }
1708
1709 let scenario_replay = Arc::new(CookieScenarioReplay);
1710 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1711 .with_behavioral_scenario_replay(scenario_replay);
1712
1713 let method = Method::GET;
1714 let uri = Uri::from_static("/api/test");
1715 let mut headers = HeaderMap::new();
1716 headers.insert("session-id", "header-session-123".parse().unwrap());
1718
1719 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1720
1721 assert_eq!(response.status_code, 200);
1722 assert_eq!(response.source.source_type, "behavioral_scenario");
1723 let body_str = String::from_utf8_lossy(&response.body);
1724 assert!(body_str.contains("header scenario"));
1725 }
1726
1727 #[tokio::test]
1728 async fn test_route_chaos_latency_error_handling() {
1729 let temp_dir = TempDir::new().unwrap();
1730 let fixtures_dir = temp_dir.path().to_path_buf();
1731 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1732
1733 struct ErrorLatencyInjector;
1735 #[async_trait]
1736 impl RouteChaosInjectorTrait for ErrorLatencyInjector {
1737 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
1738 Err(Error::internal("Latency injection failed".to_string()))
1739 }
1740 fn get_fault_response(
1741 &self,
1742 _method: &Method,
1743 _uri: &Uri,
1744 ) -> Option<RouteFaultResponse> {
1745 None
1746 }
1747 }
1748
1749 let route_chaos = Arc::new(ErrorLatencyInjector);
1750 let mock_generator =
1751 Box::new(SimpleMockGenerator::new(200, r#"{"message": "test"}"#.to_string()));
1752 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1753 .with_route_chaos_injector(route_chaos);
1754
1755 let method = Method::GET;
1756 let uri = Uri::from_static("/api/test");
1757 let headers = HeaderMap::new();
1758
1759 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1761 assert_eq!(response.status_code, 200);
1762 }
1763
1764 #[tokio::test]
1765 async fn test_behavioral_scenario_replay_with_timing_delay() {
1766 let temp_dir = TempDir::new().unwrap();
1767 let fixtures_dir = temp_dir.path().to_path_buf();
1768 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1769
1770 struct TimingScenarioReplay;
1772 #[async_trait]
1773 impl BehavioralScenarioReplay for TimingScenarioReplay {
1774 async fn try_replay(
1775 &self,
1776 _method: &Method,
1777 _uri: &Uri,
1778 _headers: &HeaderMap,
1779 _body: Option<&[u8]>,
1780 _session_id: Option<&str>,
1781 ) -> Result<Option<BehavioralReplayResponse>> {
1782 Ok(Some(BehavioralReplayResponse {
1783 status_code: 200,
1784 headers: HashMap::new(),
1785 body: b"delayed response".to_vec(),
1786 timing_ms: Some(15), content_type: "application/json".to_string(),
1788 }))
1789 }
1790 }
1791
1792 let scenario_replay = Arc::new(TimingScenarioReplay);
1793 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1794 .with_behavioral_scenario_replay(scenario_replay);
1795
1796 let method = Method::GET;
1797 let uri = Uri::from_static("/api/test");
1798 let headers = HeaderMap::new();
1799
1800 let start = std::time::Instant::now();
1801 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1802 let elapsed = start.elapsed();
1803
1804 assert_eq!(response.status_code, 200);
1805 assert!(elapsed.as_millis() >= 15); }
1807
1808 #[tokio::test]
1809 async fn test_stateful_handler_with_response() {
1810 let temp_dir = TempDir::new().unwrap();
1811 let fixtures_dir = temp_dir.path().to_path_buf();
1812 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1813
1814 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
1818 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
1819 .with_stateful_handler(stateful_handler);
1820
1821 let method = Method::GET;
1822 let uri = Uri::from_static("/api/test");
1823 let headers = HeaderMap::new();
1824
1825 let _result = handler.process_request(&method, &uri, &headers, None).await;
1828 }
1830
1831 #[tokio::test]
1832 async fn test_replay_handler_content_type_extraction() {
1833 let temp_dir = TempDir::new().unwrap();
1834 let fixtures_dir = temp_dir.path().to_path_buf();
1835 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1836
1837 let method = Method::GET;
1838 let uri = Uri::from_static("/api/test");
1839 let mut headers = HeaderMap::new();
1840 headers.insert("content-type", "application/xml".parse().unwrap());
1841
1842 let fingerprint = RequestFingerprint::new(method.clone(), &uri, &headers, None);
1844 record_replay
1845 .record_handler()
1846 .record_request(&fingerprint, 200, &headers, r#"<xml>test</xml>"#, None)
1847 .await
1848 .unwrap();
1849
1850 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
1852
1853 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1855 assert_eq!(response.content_type, "application/xml");
1856 }
1857
1858 #[tokio::test]
1859 async fn test_proxy_migration_mode_mock() {
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 mut proxy_config =
1866 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1867 proxy_config.migration_enabled = true;
1868 proxy_config.rules.push(crate::proxy::config::ProxyRule {
1869 path_pattern: "/api/*".to_string(),
1870 target_url: "http://localhost:8080".to_string(),
1871 enabled: true,
1872 pattern: "/api/*".to_string(),
1873 upstream_url: "http://localhost:8080".to_string(),
1874 migration_mode: crate::proxy::config::MigrationMode::Mock, migration_group: None,
1876 condition: None,
1877 });
1878
1879 let proxy_handler = ProxyHandler::new(proxy_config);
1880 let mock_generator =
1881 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1882
1883 let handler = PriorityHttpHandler::new(
1884 record_replay,
1885 None,
1886 Some(proxy_handler),
1887 Some(mock_generator),
1888 );
1889
1890 let method = Method::GET;
1891 let uri = Uri::from_static("/api/test");
1892 let headers = HeaderMap::new();
1893
1894 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1896 assert_eq!(response.status_code, 200);
1897 assert_eq!(response.source.source_type, "mock");
1898 }
1899
1900 #[tokio::test]
1901 async fn test_proxy_migration_mode_disabled() {
1902 let temp_dir = TempDir::new().unwrap();
1903 let fixtures_dir = temp_dir.path().to_path_buf();
1904 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1905
1906 let mut proxy_config =
1908 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
1909 proxy_config.migration_enabled = false; proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
1913 let mock_generator =
1914 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1915
1916 let handler = PriorityHttpHandler::new(
1917 record_replay,
1918 None,
1919 Some(proxy_handler),
1920 Some(mock_generator),
1921 );
1922
1923 let method = Method::GET;
1924 let uri = Uri::from_static("/api/test");
1925 let headers = HeaderMap::new();
1926
1927 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1929 assert_eq!(response.status_code, 200);
1930 assert_eq!(response.source.source_type, "mock");
1931 }
1932
1933 #[tokio::test]
1934 async fn test_continuum_engine_enabled_check() {
1935 let temp_dir = TempDir::new().unwrap();
1936 let fixtures_dir = temp_dir.path().to_path_buf();
1937 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1938
1939 let continuum_config = crate::reality_continuum::config::ContinuumConfig::new();
1941 let continuum_engine = Arc::new(RealityContinuumEngine::new(continuum_config));
1942 let mock_generator =
1943 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1944
1945 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1946 .with_continuum_engine(continuum_engine);
1947
1948 let method = Method::GET;
1949 let uri = Uri::from_static("/api/test");
1950 let headers = HeaderMap::new();
1951
1952 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1954 assert_eq!(response.status_code, 200);
1955 }
1956
1957 #[tokio::test]
1958 async fn test_behavioral_scenario_replay_error_handling() {
1959 let temp_dir = TempDir::new().unwrap();
1960 let fixtures_dir = temp_dir.path().to_path_buf();
1961 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
1962
1963 struct ErrorScenarioReplay;
1965 #[async_trait]
1966 impl BehavioralScenarioReplay for ErrorScenarioReplay {
1967 async fn try_replay(
1968 &self,
1969 _method: &Method,
1970 _uri: &Uri,
1971 _headers: &HeaderMap,
1972 _body: Option<&[u8]>,
1973 _session_id: Option<&str>,
1974 ) -> Result<Option<BehavioralReplayResponse>> {
1975 Err(Error::internal("Scenario replay error".to_string()))
1976 }
1977 }
1978
1979 let scenario_replay = Arc::new(ErrorScenarioReplay);
1980 let mock_generator =
1981 Box::new(SimpleMockGenerator::new(200, r#"{"message": "mock"}"#.to_string()));
1982 let handler = PriorityHttpHandler::new(record_replay, None, None, Some(mock_generator))
1983 .with_behavioral_scenario_replay(scenario_replay);
1984
1985 let method = Method::GET;
1986 let uri = Uri::from_static("/api/test");
1987 let headers = HeaderMap::new();
1988
1989 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
1991 assert_eq!(response.status_code, 200);
1992 assert_eq!(response.source.source_type, "mock");
1993 }
1994
1995 #[tokio::test]
1996 async fn test_behavioral_scenario_replay_with_session_id_header() {
1997 let temp_dir = TempDir::new().unwrap();
1998 let fixtures_dir = temp_dir.path().to_path_buf();
1999 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
2000
2001 struct SessionScenarioReplay;
2003 #[async_trait]
2004 impl BehavioralScenarioReplay for SessionScenarioReplay {
2005 async fn try_replay(
2006 &self,
2007 _method: &Method,
2008 _uri: &Uri,
2009 _headers: &HeaderMap,
2010 _body: Option<&[u8]>,
2011 session_id: Option<&str>,
2012 ) -> Result<Option<BehavioralReplayResponse>> {
2013 if session_id == Some("header-session-456") {
2014 Ok(Some(BehavioralReplayResponse {
2015 status_code: 200,
2016 headers: HashMap::new(),
2017 body: b"header session response".to_vec(),
2018 timing_ms: None,
2019 content_type: "application/json".to_string(),
2020 }))
2021 } else {
2022 Ok(None)
2023 }
2024 }
2025 }
2026
2027 let scenario_replay = Arc::new(SessionScenarioReplay);
2028 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
2029 .with_behavioral_scenario_replay(scenario_replay);
2030
2031 let method = Method::GET;
2032 let uri = Uri::from_static("/api/test");
2033 let mut headers = HeaderMap::new();
2034 headers.insert("x-session-id", "header-session-456".parse().unwrap());
2035
2036 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2037 assert_eq!(response.status_code, 200);
2038 assert_eq!(response.source.source_type, "behavioral_scenario");
2039 }
2040
2041 #[tokio::test]
2042 async fn test_stateful_handler_returns_response() {
2043 let temp_dir = TempDir::new().unwrap();
2044 let fixtures_dir = temp_dir.path().to_path_buf();
2045 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
2046
2047 let stateful_handler = Arc::new(StatefulResponseHandler::new().unwrap());
2049
2050 let mut state_responses = HashMap::new();
2052 state_responses.insert(
2053 "initial".to_string(),
2054 crate::stateful_handler::StateResponse {
2055 status_code: 200,
2056 headers: HashMap::new(),
2057 body_template: r#"{"status": "initial", "order_id": "123"}"#.to_string(),
2058 content_type: "application/json".to_string(),
2059 },
2060 );
2061
2062 let config = crate::stateful_handler::StatefulConfig {
2063 resource_id_extract: crate::stateful_handler::ResourceIdExtract::PathParam {
2064 param: "order_id".to_string(),
2065 },
2066 resource_type: "order".to_string(),
2067 state_responses,
2068 transitions: vec![],
2069 };
2070
2071 stateful_handler.add_config("/api/orders/{order_id}".to_string(), config).await;
2072
2073 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
2074 .with_stateful_handler(stateful_handler);
2075
2076 let method = Method::GET;
2077 let uri = Uri::from_static("/api/orders/123");
2078 let headers = HeaderMap::new();
2079
2080 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2082 assert_eq!(response.status_code, 200);
2083 assert_eq!(response.source.source_type, "stateful");
2084 assert_eq!(response.source.metadata.get("state"), Some(&"initial".to_string()));
2085 assert_eq!(response.source.metadata.get("resource_id"), Some(&"123".to_string()));
2086 }
2087
2088 #[tokio::test]
2089 async fn test_record_handler_path_with_no_other_handlers() {
2090 let temp_dir = TempDir::new().unwrap();
2091 let fixtures_dir = temp_dir.path().to_path_buf();
2092 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, true, false);
2094
2095 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
2096
2097 let method = Method::GET; let uri = Uri::from_static("/api/test");
2099 let headers = HeaderMap::new();
2100
2101 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2103 assert_eq!(response.status_code, 200);
2104 assert_eq!(response.source.source_type, "record");
2105 let body_str = String::from_utf8_lossy(&response.body);
2106 assert!(body_str.contains("Request recorded"));
2107 }
2108
2109 #[tokio::test]
2110 async fn test_mock_generator_with_migration_mode() {
2111 let temp_dir = TempDir::new().unwrap();
2112 let fixtures_dir = temp_dir.path().to_path_buf();
2113 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
2114
2115 let mut proxy_config =
2117 crate::proxy::config::ProxyConfig::new("http://localhost:8080".to_string());
2118 proxy_config.migration_enabled = true;
2119 proxy_config.rules.push(crate::proxy::config::ProxyRule {
2120 path_pattern: "/api/*".to_string(),
2121 target_url: "http://localhost:8080".to_string(),
2122 enabled: true,
2123 pattern: "/api/*".to_string(),
2124 upstream_url: "http://localhost:8080".to_string(),
2125 migration_mode: crate::proxy::config::MigrationMode::Mock,
2126 migration_group: None,
2127 condition: None,
2128 });
2129 proxy_config.enabled = false; let proxy_handler = ProxyHandler::new(proxy_config);
2132 let mock_generator = Box::new(SimpleMockGenerator::new(
2133 200,
2134 r#"{"message": "mock with migration"}"#.to_string(),
2135 ));
2136
2137 let handler = PriorityHttpHandler::new(
2138 record_replay,
2139 None,
2140 Some(proxy_handler),
2141 Some(mock_generator),
2142 );
2143
2144 let method = Method::GET;
2145 let uri = Uri::from_static("/api/test");
2146 let headers = HeaderMap::new();
2147
2148 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2150 assert_eq!(response.status_code, 200);
2151 assert_eq!(response.source.source_type, "mock");
2152 let body_str = String::from_utf8_lossy(&response.body);
2153 assert!(body_str.contains("mock with migration"));
2154 }
2155
2156 #[tokio::test]
2157 async fn test_no_handler_can_process_request() {
2158 let temp_dir = TempDir::new().unwrap();
2159 let fixtures_dir = temp_dir.path().to_path_buf();
2160 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), false, false, false);
2162 let handler = PriorityHttpHandler::new(record_replay, None, None, None);
2163
2164 let method = Method::GET;
2165 let uri = Uri::from_static("/api/test");
2166 let headers = HeaderMap::new();
2167
2168 let result = handler.process_request(&method, &uri, &headers, None).await;
2170 assert!(result.is_err());
2171 assert!(result.unwrap_err().to_string().contains("No handler could process"));
2172 }
2173
2174 #[tokio::test]
2175 async fn test_route_chaos_fault_injection() {
2176 let temp_dir = TempDir::new().unwrap();
2177 let fixtures_dir = temp_dir.path().to_path_buf();
2178 let record_replay = RecordReplayHandler::new(fixtures_dir.clone(), true, true, false);
2179
2180 struct FaultInjector;
2182 #[async_trait]
2183 impl RouteChaosInjectorTrait for FaultInjector {
2184 async fn inject_latency(&self, _method: &Method, _uri: &Uri) -> Result<()> {
2185 Ok(())
2186 }
2187 fn get_fault_response(&self, method: &Method, uri: &Uri) -> Option<RouteFaultResponse> {
2188 if method == Method::GET && uri.path() == "/api/faulty" {
2189 Some(RouteFaultResponse {
2190 status_code: 503,
2191 error_message: "Service unavailable".to_string(),
2192 fault_type: "injected_fault".to_string(),
2193 })
2194 } else {
2195 None
2196 }
2197 }
2198 }
2199
2200 let route_chaos = Arc::new(FaultInjector);
2201 let handler = PriorityHttpHandler::new(record_replay, None, None, None)
2202 .with_route_chaos_injector(route_chaos);
2203
2204 let method = Method::GET;
2205 let uri = Uri::from_static("/api/faulty");
2206 let headers = HeaderMap::new();
2207
2208 let response = handler.process_request(&method, &uri, &headers, None).await.unwrap();
2210 assert_eq!(response.status_code, 503);
2211 let body_str = String::from_utf8_lossy(&response.body);
2212 assert!(body_str.contains("Service unavailable"));
2213 assert!(body_str.contains("injected_failure"));
2214 }
2215}