1use crate::cache::{Cache, CachedResponse, ResponseCache};
7use crate::failure_analysis::FailureContextCollector;
8use crate::performance::PerformanceMonitor;
9use crate::templating::TemplateEngine;
10use crate::workspace::core::{EntityId, Folder, MockRequest, MockResponse, Workspace};
11use crate::{
12 routing::{HttpMethod, Route, RouteRegistry},
13 Error, Result,
14};
15use chrono::{DateTime, Utc};
16use serde::{Deserialize, Serialize};
17use std::collections::HashMap;
18use std::sync::Arc;
19use std::time::Duration;
20
21#[derive(Debug, Clone)]
23pub struct RequestExecutionResult {
24 pub request_id: EntityId,
26 pub response: Option<MockResponse>,
28 pub duration_ms: u64,
30 pub success: bool,
32 pub error: Option<String>,
34 pub failure_context: Option<crate::failure_analysis::FailureContext>,
36}
37
38#[derive(Debug, Clone)]
40pub struct RequestMatchCriteria {
41 pub method: HttpMethod,
43 pub path: String,
45 pub query_params: HashMap<String, String>,
47 pub headers: HashMap<String, String>,
49 pub body: Option<String>,
51}
52
53#[derive(Debug, Clone)]
55pub struct RequestProcessor {
56 _template_engine: TemplateEngine,
58 environment_manager: Option<crate::workspace::environment::EnvironmentManager>,
60 performance_monitor: Arc<PerformanceMonitor>,
62 response_cache: Arc<ResponseCache>,
64 validation_cache: Arc<Cache<String, RequestValidationResult>>,
66 optimizations_enabled: bool,
68 failure_collector: Option<Arc<FailureContextCollector>>,
70}
71
72#[derive(Debug, Clone)]
74pub struct RequestValidationResult {
75 pub is_valid: bool,
77 pub errors: Vec<String>,
79 pub warnings: Vec<String>,
81}
82
83#[derive(Debug, Clone)]
85pub struct RequestExecutionContext {
86 pub workspace_id: EntityId,
88 pub environment_variables: HashMap<String, String>,
90 pub global_headers: HashMap<String, String>,
92 pub timeout_seconds: u64,
94 pub ssl_verify: bool,
96}
97
98#[derive(Debug, Clone, Serialize, Deserialize)]
100pub struct RequestMetrics {
101 pub total_requests: u64,
103 pub successful_requests: u64,
105 pub failed_requests: u64,
107 pub average_response_time_ms: f64,
109 pub popular_requests: Vec<(EntityId, u64)>,
111 pub last_execution: Option<DateTime<Utc>>,
113}
114
115impl RequestProcessor {
116 pub fn new() -> Self {
118 Self {
119 _template_engine: TemplateEngine::new(),
120 environment_manager: None,
121 performance_monitor: Arc::new(PerformanceMonitor::new()),
122 response_cache: Arc::new(ResponseCache::new(1000, Duration::from_secs(300))), validation_cache: Arc::new(Cache::with_ttl(500, Duration::from_secs(60))), optimizations_enabled: true,
125 failure_collector: Some(Arc::new(FailureContextCollector::new())),
126 }
127 }
128
129 pub fn with_environment_manager(
131 environment_manager: crate::workspace::environment::EnvironmentManager,
132 ) -> Self {
133 Self {
134 _template_engine: TemplateEngine::new(),
135 environment_manager: Some(environment_manager),
136 performance_monitor: Arc::new(PerformanceMonitor::new()),
137 response_cache: Arc::new(ResponseCache::new(1000, Duration::from_secs(300))),
138 validation_cache: Arc::new(Cache::with_ttl(500, Duration::from_secs(60))),
139 optimizations_enabled: true,
140 failure_collector: Some(Arc::new(FailureContextCollector::new())),
141 }
142 }
143
144 pub fn with_performance_config(
146 environment_manager: Option<crate::workspace::environment::EnvironmentManager>,
147 cache_size: usize,
148 cache_ttl: Duration,
149 enable_optimizations: bool,
150 ) -> Self {
151 Self {
152 _template_engine: TemplateEngine::new(),
153 environment_manager,
154 performance_monitor: Arc::new(PerformanceMonitor::new()),
155 response_cache: Arc::new(ResponseCache::new(cache_size, cache_ttl)),
156 validation_cache: Arc::new(Cache::with_ttl(cache_size / 2, Duration::from_secs(60))),
157 optimizations_enabled: enable_optimizations,
158 failure_collector: Some(Arc::new(FailureContextCollector::new())),
159 }
160 }
161
162 pub fn performance_monitor(&self) -> Arc<PerformanceMonitor> {
164 self.performance_monitor.clone()
165 }
166
167 pub fn set_optimizations_enabled(&mut self, enabled: bool) {
169 self.optimizations_enabled = enabled;
170 }
171
172 pub fn find_matching_request(
174 &self,
175 workspace: &Workspace,
176 criteria: &RequestMatchCriteria,
177 ) -> Option<EntityId> {
178 for request in &workspace.requests {
180 if self.request_matches(request, criteria) {
181 return Some(request.id.clone());
182 }
183 }
184
185 if let Some(request_id) =
187 self.find_matching_request_in_folders(&workspace.folders, criteria)
188 {
189 return Some(request_id);
190 }
191
192 None
193 }
194
195 fn request_matches(&self, request: &MockRequest, criteria: &RequestMatchCriteria) -> bool {
197 if request.method != criteria.method {
199 return false;
200 }
201
202 if !self.url_matches_pattern(&request.url, &criteria.path) {
204 return false;
205 }
206
207 for (key, expected_value) in &criteria.query_params {
209 if let Some(actual_value) = request.query_params.get(key) {
210 if actual_value != expected_value {
211 return false;
212 }
213 } else {
214 return false;
215 }
216 }
217
218 for (key, expected_value) in &criteria.headers {
220 if let Some(actual_value) = request.headers.get(key) {
221 if actual_value != expected_value {
222 return false;
223 }
224 } else {
225 return false;
226 }
227 }
228
229 true
230 }
231
232 pub fn url_matches_pattern(&self, pattern: &str, url: &str) -> bool {
234 if pattern == url {
236 return true;
237 }
238
239 if pattern == "*" {
241 return true;
242 }
243
244 if pattern.contains('*') {
246 return self.matches_path_pattern(pattern, url);
247 }
248
249 false
250 }
251
252 fn matches_path_pattern(&self, pattern: &str, path: &str) -> bool {
254 let pattern_parts: Vec<&str> = pattern.split('/').filter(|s| !s.is_empty()).collect();
255 let path_parts: Vec<&str> = path.split('/').filter(|s| !s.is_empty()).collect();
256
257 self.match_segments(&pattern_parts, &path_parts, 0, 0)
258 }
259
260 #[allow(clippy::only_used_in_recursion)]
262 fn match_segments(
263 &self,
264 pattern_parts: &[&str],
265 path_parts: &[&str],
266 pattern_idx: usize,
267 path_idx: usize,
268 ) -> bool {
269 if pattern_idx == pattern_parts.len() && path_idx == path_parts.len() {
271 return true;
272 }
273
274 if pattern_idx == pattern_parts.len() {
276 return false;
277 }
278
279 let current_pattern = pattern_parts[pattern_idx];
280
281 match current_pattern {
282 "*" => {
283 if path_idx < path_parts.len() {
285 if self.match_segments(pattern_parts, path_parts, pattern_idx + 1, path_idx + 1)
287 {
288 return true;
289 }
290 }
291 false
292 }
293 "**" => {
294 if self.match_segments(pattern_parts, path_parts, pattern_idx + 1, path_idx) {
297 return true;
298 }
299 if path_idx < path_parts.len()
301 && self.match_segments(pattern_parts, path_parts, pattern_idx, path_idx + 1)
302 {
303 return true;
304 }
305 false
306 }
307 _ => {
308 if path_idx < path_parts.len() && current_pattern == path_parts[path_idx] {
310 return self.match_segments(
311 pattern_parts,
312 path_parts,
313 pattern_idx + 1,
314 path_idx + 1,
315 );
316 }
317 false
318 }
319 }
320 }
321
322 fn find_matching_request_in_folders(
324 &self,
325 folders: &[Folder],
326 criteria: &RequestMatchCriteria,
327 ) -> Option<EntityId> {
328 for folder in folders {
329 for request in &folder.requests {
331 if self.request_matches(request, criteria) {
332 return Some(request.id.clone());
333 }
334 }
335
336 if let Some(request_id) =
338 self.find_matching_request_in_folders(&folder.folders, criteria)
339 {
340 return Some(request_id);
341 }
342 }
343
344 None
345 }
346
347 pub async fn execute_request(
349 &self,
350 workspace: &mut Workspace,
351 request_id: &EntityId,
352 context: &RequestExecutionContext,
353 ) -> Result<RequestExecutionResult> {
354 let _perf_guard = if self.optimizations_enabled {
356 self.performance_monitor.start_tracking_named("execute_request")
357 } else {
358 None
359 };
360
361 let cache_key = if self.optimizations_enabled {
363 self.generate_response_cache_key(request_id, context)
364 } else {
365 String::new()
366 };
367
368 if self.optimizations_enabled && !cache_key.is_empty() {
370 if let Some(cached_response) = self.response_cache.get_response(&cache_key).await {
371 self.performance_monitor.record_cache_hit();
372 return Ok(RequestExecutionResult {
373 request_id: request_id.clone(),
374 response: Some(self.convert_cached_response_to_mock_response(cached_response)),
375 duration_ms: 1, success: true,
377 error: None,
378 failure_context: None,
379 });
380 } else {
381 self.performance_monitor.record_cache_miss();
382 }
383 }
384
385 let request = match self.find_request_in_workspace(workspace, request_id) {
387 Some(req) => req,
388 None => {
389 if self.optimizations_enabled {
390 self.performance_monitor.record_error();
391 }
392 let error_msg = format!("Request with ID {} not found", request_id);
393
394 if let Some(ref collector) = self.failure_collector {
398 let _failure_context = collector
399 .collect_context(
400 "UNKNOWN",
401 &request_id.to_string(),
402 None,
403 Some(error_msg.clone()),
404 )
405 .ok();
406 }
407
408 return Err(Error::generic(error_msg));
409 }
410 };
411
412 let start_time = std::time::Instant::now();
413 let method = "GET"; let path = request_id.to_string(); let validation = match self.validate_request_cached(request, context).await {
418 Ok(v) => v,
419 Err(e) => {
420 if self.optimizations_enabled {
421 self.performance_monitor.record_error();
422 }
423 let error_msg = format!("Request validation error: {}", e);
424
425 if let Some(ref collector) = self.failure_collector {
429 let _failure_context = collector
430 .collect_context(method, &path, None, Some(error_msg.clone()))
431 .ok();
432 }
433
434 return Err(e);
435 }
436 };
437
438 if !validation.is_valid {
439 if self.optimizations_enabled {
440 self.performance_monitor.record_error();
441 }
442 let error_msg = format!("Request validation failed: {:?}", validation.errors);
443
444 if let Some(ref collector) = self.failure_collector {
448 let _failure_context =
449 collector.collect_context(method, &path, None, Some(error_msg.clone())).ok();
450 }
451
452 return Err(Error::Validation { message: error_msg });
453 }
454
455 let response = match request.active_response() {
457 Some(resp) => resp,
458 None => {
459 if self.optimizations_enabled {
460 self.performance_monitor.record_error();
461 }
462 let error_msg = "No active response found for request".to_string();
463
464 if let Some(ref collector) = self.failure_collector {
468 let _failure_context = collector
469 .collect_context(method, &path, None, Some(error_msg.clone()))
470 .ok();
471 }
472
473 return Err(Error::generic(error_msg));
474 }
475 };
476
477 let processed_response = match self.process_response(response, context).await {
479 Ok(resp) => resp,
480 Err(e) => {
481 if self.optimizations_enabled {
482 self.performance_monitor.record_error();
483 }
484 let error_msg = format!("Failed to process response: {}", e);
485
486 if let Some(ref collector) = self.failure_collector {
490 let _failure_context = collector
491 .collect_context(method, &path, None, Some(error_msg.clone()))
492 .ok();
493 }
494
495 return Err(e);
496 }
497 };
498
499 let duration_ms = start_time.elapsed().as_millis() as u64;
500
501 if self.optimizations_enabled && !cache_key.is_empty() {
503 let cached_response =
504 self.convert_mock_response_to_cached_response(&processed_response);
505 self.response_cache.cache_response(cache_key, cached_response).await;
506 }
507
508 if let Some(request_mut) = self.find_request_in_workspace_mut(workspace, request_id) {
510 if let Some(response_mut) = request_mut.active_response_mut() {
511 response_mut.record_usage(request_id.clone(), duration_ms);
512 }
513 }
514
515 Ok(RequestExecutionResult {
516 request_id: request_id.clone(),
517 response: Some(processed_response),
518 duration_ms,
519 success: true,
520 error: None,
521 failure_context: None,
522 })
523 }
524
525 fn find_request_in_workspace_mut<'a>(
527 &self,
528 workspace: &'a mut Workspace,
529 request_id: &EntityId,
530 ) -> Option<&'a mut MockRequest> {
531 for request in &mut workspace.requests {
533 if &request.id == request_id {
534 return Some(request);
535 }
536 }
537
538 self.find_request_in_folders_mut(&mut workspace.folders, request_id)
540 }
541
542 #[allow(clippy::only_used_in_recursion)]
544 fn find_request_in_folders_mut<'a>(
545 &self,
546 folders: &'a mut [Folder],
547 request_id: &EntityId,
548 ) -> Option<&'a mut MockRequest> {
549 for folder in folders {
550 for request in &mut folder.requests {
552 if &request.id == request_id {
553 return Some(request);
554 }
555 }
556
557 if let Some(request) = self.find_request_in_folders_mut(&mut folder.folders, request_id)
559 {
560 return Some(request);
561 }
562 }
563
564 None
565 }
566
567 fn find_request_in_workspace<'a>(
569 &self,
570 workspace: &'a Workspace,
571 request_id: &EntityId,
572 ) -> Option<&'a MockRequest> {
573 workspace
575 .requests
576 .iter()
577 .find(|r| &r.id == request_id)
578 .or_else(|| self.find_request_in_folders(&workspace.folders, request_id))
579 }
580
581 #[allow(clippy::only_used_in_recursion)]
583 fn find_request_in_folders<'a>(
584 &self,
585 folders: &'a [Folder],
586 request_id: &EntityId,
587 ) -> Option<&'a MockRequest> {
588 for folder in folders {
589 if let Some(request) = folder.requests.iter().find(|r| &r.id == request_id) {
591 return Some(request);
592 }
593
594 if let Some(request) = self.find_request_in_folders(&folder.folders, request_id) {
596 return Some(request);
597 }
598 }
599
600 None
601 }
602
603 pub fn validate_request(
605 &self,
606 request: &MockRequest,
607 _context: &RequestExecutionContext,
608 ) -> RequestValidationResult {
609 let mut errors = Vec::new();
610 let mut warnings = Vec::new();
611
612 if !request.enabled {
614 errors.push("Request is disabled".to_string());
615 }
616
617 if request.url.is_empty() {
619 errors.push("Request URL cannot be empty".to_string());
620 }
621
622 match request.method {
624 HttpMethod::GET
625 | HttpMethod::POST
626 | HttpMethod::PUT
627 | HttpMethod::DELETE
628 | HttpMethod::PATCH
629 | HttpMethod::HEAD
630 | HttpMethod::OPTIONS => {
631 }
633 }
634
635 if request.active_response().is_none() {
637 warnings.push("No active response configured".to_string());
638 }
639
640 for response in &request.responses {
642 if response.status_code < 100 || response.status_code > 599 {
643 errors.push(format!("Invalid status code: {}", response.status_code));
644 }
645
646 if response.body.is_empty() {
647 warnings.push(format!("Response '{}' has empty body", response.name));
648 }
649 }
650
651 RequestValidationResult {
652 is_valid: errors.is_empty(),
653 errors,
654 warnings,
655 }
656 }
657
658 async fn process_response(
660 &self,
661 response: &MockResponse,
662 context: &RequestExecutionContext,
663 ) -> Result<MockResponse> {
664 if response.delay > 0 {
666 tokio::time::sleep(std::time::Duration::from_millis(response.delay)).await;
667 }
668
669 let mut processed_response = response.clone();
671
672 if let Some(env_manager) = &self.environment_manager {
674 if let Some(_env_vars) = self.get_environment_variables(context) {
675 processed_response.body = env_manager.substitute_variables(&response.body).value;
676 }
677 }
678
679 Ok(processed_response)
680 }
681
682 fn get_environment_variables(
684 &self,
685 context: &RequestExecutionContext,
686 ) -> Option<HashMap<String, String>> {
687 if let Some(env_manager) = &self.environment_manager {
688 if let Some(active_env) = env_manager.get_active_environment() {
689 return Some(active_env.variables.clone());
690 }
691 }
692
693 Some(context.environment_variables.clone())
694 }
695
696 pub fn get_request_metrics(&self, workspace: &Workspace) -> RequestMetrics {
698 let mut total_requests = 0u64;
699 let mut successful_requests = 0u64;
700 let mut failed_requests = 0u64;
701 let mut total_response_time = 0u64;
702 let mut request_counts = HashMap::new();
703 let mut last_execution: Option<DateTime<Utc>> = None;
704
705 for request in &workspace.requests {
707 total_requests += 1;
708
709 for response in &request.responses {
711 let execution_count = response.history.len() as u64;
712 *request_counts.entry(request.id.clone()).or_insert(0) += execution_count;
713
714 for entry in &response.history {
715 total_response_time += entry.duration_ms;
716
717 if let Some(current_last) = last_execution {
719 if entry.timestamp > current_last {
720 last_execution = Some(entry.timestamp);
721 }
722 } else {
723 last_execution = Some(entry.timestamp);
724 }
725
726 if entry.duration_ms < 5000 {
728 successful_requests += 1;
730 } else {
731 failed_requests += 1;
732 }
733 }
734 }
735 }
736
737 self.collect_folder_request_metrics(
739 &workspace.folders,
740 &mut total_requests,
741 &mut successful_requests,
742 &mut failed_requests,
743 &mut total_response_time,
744 &mut request_counts,
745 &mut last_execution,
746 );
747
748 let average_response_time = if total_requests > 0 {
749 total_response_time as f64 / total_requests as f64
750 } else {
751 0.0
752 };
753
754 let mut popular_requests: Vec<_> = request_counts.into_iter().collect();
756 popular_requests.sort_by(|a, b| b.1.cmp(&a.1));
757 popular_requests.truncate(5);
758
759 RequestMetrics {
760 total_requests,
761 successful_requests,
762 failed_requests,
763 average_response_time_ms: average_response_time,
764 popular_requests,
765 last_execution,
766 }
767 }
768
769 #[allow(clippy::only_used_in_recursion)]
771 #[allow(clippy::too_many_arguments)]
772 fn collect_folder_request_metrics(
773 &self,
774 folders: &[Folder],
775 total_requests: &mut u64,
776 successful_requests: &mut u64,
777 failed_requests: &mut u64,
778 total_response_time: &mut u64,
779 request_counts: &mut HashMap<EntityId, u64>,
780 last_execution: &mut Option<DateTime<Utc>>,
781 ) {
782 for folder in folders {
783 for request in &folder.requests {
784 *total_requests += 1;
785
786 for response in &request.responses {
788 let execution_count = response.history.len() as u64;
789 *request_counts.entry(request.id.clone()).or_insert(0) += execution_count;
790
791 for entry in &response.history {
792 *total_response_time += entry.duration_ms;
793
794 if let Some(current_last) = *last_execution {
796 if entry.timestamp > current_last {
797 *last_execution = Some(entry.timestamp);
798 }
799 } else {
800 *last_execution = Some(entry.timestamp);
801 }
802
803 if entry.duration_ms < 5000 {
805 *successful_requests += 1;
806 } else {
807 *failed_requests += 1;
808 }
809 }
810 }
811 }
812
813 self.collect_folder_request_metrics(
815 &folder.folders,
816 total_requests,
817 successful_requests,
818 failed_requests,
819 total_response_time,
820 request_counts,
821 last_execution,
822 );
823 }
824 }
825
826 pub fn create_route_from_request(&self, request: &MockRequest) -> Result<Route> {
828 if !request.enabled {
829 return Err(Error::validation("Request is disabled"));
830 }
831
832 let response = request
833 .active_response()
834 .ok_or_else(|| Error::validation("No active response found"))?;
835
836 let mut route = Route::new(request.method.clone(), request.url.clone());
838
839 route.metadata.insert("id".to_string(), serde_json::json!(request.id));
841 route.metadata.insert("response".to_string(), serde_json::json!(response.body));
842 route
843 .metadata
844 .insert("status_code".to_string(), serde_json::json!(response.status_code));
845 route.metadata.insert("headers".to_string(), serde_json::json!(request.headers));
846 route
847 .metadata
848 .insert("query_params".to_string(), serde_json::json!(request.query_params));
849 route.metadata.insert("enabled".to_string(), serde_json::json!(request.enabled));
850 route
851 .metadata
852 .insert("created_at".to_string(), serde_json::json!(request.created_at));
853 route
854 .metadata
855 .insert("updated_at".to_string(), serde_json::json!(request.updated_at));
856
857 Ok(route)
858 }
859
860 pub fn update_route_registry(
862 &self,
863 workspace: &Workspace,
864 route_registry: &mut RouteRegistry,
865 ) -> Result<()> {
866 route_registry.clear();
867
868 for request in &workspace.requests {
870 if request.enabled {
871 if let Ok(route) = self.create_route_from_request(request) {
872 let _ = route_registry.add_route(route);
873 }
874 }
875 }
876
877 self.add_folder_routes_to_registry(&workspace.folders, route_registry)?;
879
880 Ok(())
881 }
882
883 fn add_folder_routes_to_registry(
885 &self,
886 folders: &[Folder],
887 route_registry: &mut RouteRegistry,
888 ) -> Result<()> {
889 for folder in folders {
890 for request in &folder.requests {
891 if request.enabled {
892 if let Ok(route) = self.create_route_from_request(request) {
893 let _ = route_registry.add_route(route);
894 }
895 }
896 }
897
898 self.add_folder_routes_to_registry(&folder.folders, route_registry)?;
900 }
901
902 Ok(())
903 }
904
905 fn generate_response_cache_key(
909 &self,
910 request_id: &EntityId,
911 context: &RequestExecutionContext,
912 ) -> String {
913 use std::collections::hash_map::DefaultHasher;
914 use std::hash::{Hash, Hasher};
915
916 let mut hasher = DefaultHasher::new();
917 request_id.hash(&mut hasher);
918 context.workspace_id.hash(&mut hasher);
919
920 for (key, value) in &context.environment_variables {
922 key.hash(&mut hasher);
923 value.hash(&mut hasher);
924 }
925
926 for (key, value) in &context.global_headers {
928 key.hash(&mut hasher);
929 value.hash(&mut hasher);
930 }
931
932 format!("req_{}_{}", hasher.finish(), request_id)
933 }
934
935 async fn validate_request_cached(
937 &self,
938 request: &MockRequest,
939 context: &RequestExecutionContext,
940 ) -> Result<RequestValidationResult> {
941 if !self.optimizations_enabled {
942 return Ok(self.validate_request(request, context));
943 }
944
945 let cache_key = format!("val_{}_{}", request.id, context.workspace_id);
947
948 if let Some(cached_result) = self.validation_cache.get(&cache_key).await {
950 return Ok(cached_result);
951 }
952
953 let result = self.validate_request(request, context);
955
956 self.validation_cache.insert(cache_key, result.clone(), None).await;
958
959 Ok(result)
960 }
961
962 fn convert_mock_response_to_cached_response(&self, response: &MockResponse) -> CachedResponse {
964 CachedResponse {
965 status_code: response.status_code,
966 headers: response.headers.clone(),
967 body: response.body.clone(),
968 content_type: response.headers.get("Content-Type").cloned(),
969 }
970 }
971
972 fn convert_cached_response_to_mock_response(&self, cached: CachedResponse) -> MockResponse {
974 MockResponse {
975 id: EntityId::new(),
976 name: "Cached Response".to_string(),
977 status_code: cached.status_code,
978 headers: cached.headers,
979 body: cached.body,
980 delay: 0, active: true,
982 created_at: chrono::Utc::now(),
983 updated_at: chrono::Utc::now(),
984 history: Vec::new(),
985 intelligent: None,
986 drift: None,
987 }
988 }
989
990 pub async fn get_performance_summary(&self) -> crate::performance::PerformanceSummary {
992 self.performance_monitor.get_summary().await
993 }
994
995 pub async fn get_cache_stats(&self) -> (crate::cache::CacheStats, crate::cache::CacheStats) {
997 let response_cache_stats = self.response_cache.stats().await;
998 let validation_cache_stats = self.validation_cache.stats().await;
999 (response_cache_stats, validation_cache_stats)
1000 }
1001
1002 pub async fn clear_caches(&self) {
1004 self.response_cache.get_response("").await; self.validation_cache.clear().await;
1006 }
1007}
1008
1009impl Default for RequestProcessor {
1010 fn default() -> Self {
1011 Self::new()
1012 }
1013}
1014
1015#[cfg(test)]
1016mod tests {
1017 use super::*;
1018 use crate::workspace::core::MockRequest;
1019 use crate::workspace::environment::EnvironmentManager;
1020
1021 #[test]
1022 fn test_request_processor_new() {
1023 let processor = RequestProcessor::new();
1024 let _monitor = processor.performance_monitor(); assert!(processor.optimizations_enabled);
1026 assert!(processor.environment_manager.is_none());
1027 }
1028
1029 #[test]
1030 fn test_request_processor_default() {
1031 let processor = RequestProcessor::default();
1032 let _monitor = processor.performance_monitor(); assert!(processor.optimizations_enabled);
1034 }
1035
1036 #[test]
1037 fn test_request_processor_with_environment_manager() {
1038 let env_manager = EnvironmentManager::new();
1039 let processor = RequestProcessor::with_environment_manager(env_manager);
1040 assert!(processor.environment_manager.is_some());
1041 assert!(processor.optimizations_enabled);
1042 }
1043
1044 #[test]
1045 fn test_request_processor_with_performance_config() {
1046 let processor =
1047 RequestProcessor::with_performance_config(None, 500, Duration::from_secs(120), false);
1048 assert!(!processor.optimizations_enabled);
1049 assert!(processor.environment_manager.is_none());
1050 }
1051
1052 #[test]
1053 fn test_request_processor_with_performance_config_with_env() {
1054 let env_manager = EnvironmentManager::new();
1055 let processor = RequestProcessor::with_performance_config(
1056 Some(env_manager),
1057 2000,
1058 Duration::from_secs(600),
1059 true,
1060 );
1061 assert!(processor.optimizations_enabled);
1062 assert!(processor.environment_manager.is_some());
1063 }
1064
1065 #[test]
1066 fn test_performance_monitor_accessor() {
1067 let processor = RequestProcessor::new();
1068 let monitor = processor.performance_monitor();
1069 assert!(!Arc::ptr_eq(&monitor, &Arc::new(PerformanceMonitor::new())));
1071 }
1072
1073 #[test]
1074 fn test_set_optimizations_enabled() {
1075 let mut processor = RequestProcessor::new();
1076 assert!(processor.optimizations_enabled);
1077
1078 processor.set_optimizations_enabled(false);
1079 assert!(!processor.optimizations_enabled);
1080
1081 processor.set_optimizations_enabled(true);
1082 assert!(processor.optimizations_enabled);
1083 }
1084
1085 #[test]
1086 fn test_request_match_criteria_creation() {
1087 let mut query_params = HashMap::new();
1088 query_params.insert("key".to_string(), "value".to_string());
1089
1090 let mut headers = HashMap::new();
1091 headers.insert("Content-Type".to_string(), "application/json".to_string());
1092
1093 let criteria = RequestMatchCriteria {
1094 method: HttpMethod::GET,
1095 path: "/api/test".to_string(),
1096 query_params,
1097 headers,
1098 body: Some(r#"{"test": "data"}"#.to_string()),
1099 };
1100
1101 assert_eq!(criteria.method, HttpMethod::GET);
1102 assert_eq!(criteria.path, "/api/test");
1103 assert_eq!(criteria.query_params.len(), 1);
1104 assert_eq!(criteria.headers.len(), 1);
1105 assert!(criteria.body.is_some());
1106 }
1107
1108 #[test]
1109 fn test_request_validation_result_creation() {
1110 let result = RequestValidationResult {
1111 is_valid: true,
1112 errors: vec![],
1113 warnings: vec!["Warning message".to_string()],
1114 };
1115
1116 assert!(result.is_valid);
1117 assert!(result.errors.is_empty());
1118 assert_eq!(result.warnings.len(), 1);
1119 }
1120
1121 #[test]
1122 fn test_request_validation_result_with_errors() {
1123 let result = RequestValidationResult {
1124 is_valid: false,
1125 errors: vec!["Error 1".to_string(), "Error 2".to_string()],
1126 warnings: vec![],
1127 };
1128
1129 assert!(!result.is_valid);
1130 assert_eq!(result.errors.len(), 2);
1131 assert!(result.warnings.is_empty());
1132 }
1133
1134 #[test]
1135 fn test_request_execution_context_creation() {
1136 let mut env_vars = HashMap::new();
1137 env_vars.insert("API_KEY".to_string(), "secret123".to_string());
1138
1139 let mut global_headers = HashMap::new();
1140 global_headers.insert("X-Request-ID".to_string(), "req-123".to_string());
1141
1142 let context = RequestExecutionContext {
1143 workspace_id: EntityId::new(),
1144 environment_variables: env_vars,
1145 global_headers,
1146 timeout_seconds: 30,
1147 ssl_verify: true,
1148 };
1149
1150 assert_eq!(context.timeout_seconds, 30);
1151 assert!(context.ssl_verify);
1152 assert_eq!(context.environment_variables.len(), 1);
1153 assert_eq!(context.global_headers.len(), 1);
1154 }
1155
1156 #[test]
1157 fn test_request_metrics_creation() {
1158 let metrics = RequestMetrics {
1159 total_requests: 100,
1160 successful_requests: 95,
1161 failed_requests: 5,
1162 average_response_time_ms: 125.5,
1163 popular_requests: vec![(EntityId::new(), 10), (EntityId::new(), 8)],
1164 last_execution: Some(Utc::now()),
1165 };
1166
1167 assert_eq!(metrics.total_requests, 100);
1168 assert_eq!(metrics.successful_requests, 95);
1169 assert_eq!(metrics.failed_requests, 5);
1170 assert_eq!(metrics.average_response_time_ms, 125.5);
1171 assert_eq!(metrics.popular_requests.len(), 2);
1172 assert!(metrics.last_execution.is_some());
1173 }
1174
1175 #[test]
1176 fn test_request_execution_result_creation() {
1177 let result = RequestExecutionResult {
1178 request_id: EntityId::new(),
1179 response: None,
1180 duration_ms: 150,
1181 success: true,
1182 error: None,
1183 failure_context: None,
1184 };
1185
1186 assert!(result.success);
1187 assert_eq!(result.duration_ms, 150);
1188 assert!(result.error.is_none());
1189 assert!(result.failure_context.is_none());
1190 }
1191
1192 #[test]
1193 fn test_request_execution_result_with_error() {
1194 let result = RequestExecutionResult {
1195 request_id: EntityId::new(),
1196 response: None,
1197 duration_ms: 50,
1198 success: false,
1199 error: Some("Request failed".to_string()),
1200 failure_context: None,
1201 };
1202
1203 assert!(!result.success);
1204 assert!(result.error.is_some());
1205 assert_eq!(result.error.unwrap(), "Request failed");
1206 }
1207
1208 #[tokio::test]
1209 async fn test_clear_caches() {
1210 let processor = RequestProcessor::new();
1211 processor.clear_caches().await;
1213 }
1214
1215 #[test]
1216 fn test_request_execution_result_clone() {
1217 let result1 = RequestExecutionResult {
1218 request_id: EntityId::new(),
1219 response: None,
1220 duration_ms: 100,
1221 success: true,
1222 error: None,
1223 failure_context: None,
1224 };
1225 let result2 = result1.clone();
1226 assert_eq!(result1.success, result2.success);
1227 assert_eq!(result1.duration_ms, result2.duration_ms);
1228 }
1229
1230 #[test]
1231 fn test_request_execution_result_debug() {
1232 let result = RequestExecutionResult {
1233 request_id: EntityId::new(),
1234 response: None,
1235 duration_ms: 100,
1236 success: true,
1237 error: None,
1238 failure_context: None,
1239 };
1240 let debug_str = format!("{:?}", result);
1241 assert!(debug_str.contains("RequestExecutionResult"));
1242 }
1243
1244 #[test]
1245 fn test_request_match_criteria_clone() {
1246 let criteria1 = RequestMatchCriteria {
1247 method: HttpMethod::GET,
1248 path: "/test".to_string(),
1249 query_params: HashMap::new(),
1250 headers: HashMap::new(),
1251 body: None,
1252 };
1253 let criteria2 = criteria1.clone();
1254 assert_eq!(criteria1.method, criteria2.method);
1255 assert_eq!(criteria1.path, criteria2.path);
1256 }
1257
1258 #[test]
1259 fn test_request_match_criteria_debug() {
1260 let criteria = RequestMatchCriteria {
1261 method: HttpMethod::POST,
1262 path: "/api/test".to_string(),
1263 query_params: HashMap::new(),
1264 headers: HashMap::new(),
1265 body: Some("body".to_string()),
1266 };
1267 let debug_str = format!("{:?}", criteria);
1268 assert!(debug_str.contains("RequestMatchCriteria"));
1269 }
1270
1271 #[test]
1272 fn test_request_validation_result_clone() {
1273 let result1 = RequestValidationResult {
1274 is_valid: true,
1275 errors: vec![],
1276 warnings: vec!["Warning".to_string()],
1277 };
1278 let result2 = result1.clone();
1279 assert_eq!(result1.is_valid, result2.is_valid);
1280 assert_eq!(result1.warnings, result2.warnings);
1281 }
1282
1283 #[test]
1284 fn test_request_validation_result_debug() {
1285 let result = RequestValidationResult {
1286 is_valid: false,
1287 errors: vec!["Error".to_string()],
1288 warnings: vec![],
1289 };
1290 let debug_str = format!("{:?}", result);
1291 assert!(debug_str.contains("RequestValidationResult"));
1292 }
1293
1294 #[test]
1295 fn test_request_execution_context_clone() {
1296 let context1 = RequestExecutionContext {
1297 workspace_id: EntityId::new(),
1298 environment_variables: HashMap::new(),
1299 global_headers: HashMap::new(),
1300 timeout_seconds: 30,
1301 ssl_verify: true,
1302 };
1303 let context2 = context1.clone();
1304 assert_eq!(context1.timeout_seconds, context2.timeout_seconds);
1305 assert_eq!(context1.ssl_verify, context2.ssl_verify);
1306 }
1307
1308 #[test]
1309 fn test_request_execution_context_debug() {
1310 let context = RequestExecutionContext {
1311 workspace_id: EntityId::new(),
1312 environment_variables: HashMap::new(),
1313 global_headers: HashMap::new(),
1314 timeout_seconds: 60,
1315 ssl_verify: false,
1316 };
1317 let debug_str = format!("{:?}", context);
1318 assert!(debug_str.contains("RequestExecutionContext"));
1319 }
1320
1321 #[test]
1322 fn test_request_metrics_clone() {
1323 let metrics1 = RequestMetrics {
1324 total_requests: 10,
1325 successful_requests: 8,
1326 failed_requests: 2,
1327 average_response_time_ms: 50.0,
1328 popular_requests: vec![],
1329 last_execution: None,
1330 };
1331 let metrics2 = metrics1.clone();
1332 assert_eq!(metrics1.total_requests, metrics2.total_requests);
1333 }
1334
1335 #[test]
1336 fn test_request_metrics_debug() {
1337 let metrics = RequestMetrics {
1338 total_requests: 5,
1339 successful_requests: 4,
1340 failed_requests: 1,
1341 average_response_time_ms: 100.0,
1342 popular_requests: vec![],
1343 last_execution: Some(Utc::now()),
1344 };
1345 let debug_str = format!("{:?}", metrics);
1346 assert!(debug_str.contains("RequestMetrics"));
1347 }
1348
1349 #[test]
1350 fn test_request_processor_clone() {
1351 let processor1 = RequestProcessor::new();
1352 let processor2 = processor1.clone();
1353 assert_eq!(processor1.optimizations_enabled, processor2.optimizations_enabled);
1354 }
1355
1356 #[test]
1357 fn test_request_processor_debug() {
1358 let processor = RequestProcessor::new();
1359 let debug_str = format!("{:?}", processor);
1360 assert!(debug_str.contains("RequestProcessor"));
1361 }
1362
1363 #[tokio::test]
1364 async fn test_execute_request_success() {
1365 let processor = RequestProcessor::new();
1366 let mut workspace = Workspace::new("Test Workspace".to_string());
1367
1368 let mut request =
1369 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1370 let response =
1371 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
1372 request.add_response(response);
1373 workspace.add_request(request.clone());
1374
1375 let context = RequestExecutionContext {
1376 workspace_id: workspace.id.clone(),
1377 environment_variables: HashMap::new(),
1378 global_headers: HashMap::new(),
1379 timeout_seconds: 30,
1380 ssl_verify: true,
1381 };
1382
1383 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1384
1385 assert!(result.is_ok());
1386 let execution_result = result.unwrap();
1387 assert!(execution_result.success);
1388 assert!(execution_result.response.is_some());
1389 assert_eq!(execution_result.response.unwrap().status_code, 200);
1390 }
1391
1392 #[tokio::test]
1393 async fn test_execute_request_not_found() {
1394 let processor = RequestProcessor::new();
1395 let mut workspace = Workspace::new("Test Workspace".to_string());
1396
1397 let context = RequestExecutionContext {
1398 workspace_id: workspace.id.clone(),
1399 environment_variables: HashMap::new(),
1400 global_headers: HashMap::new(),
1401 timeout_seconds: 30,
1402 ssl_verify: true,
1403 };
1404
1405 let non_existent_id = EntityId::new();
1406 let result = processor.execute_request(&mut workspace, &non_existent_id, &context).await;
1407
1408 assert!(result.is_err());
1409 }
1410
1411 #[tokio::test]
1412 async fn test_execute_request_with_delay() {
1413 let processor = RequestProcessor::new();
1414 let mut workspace = Workspace::new("Test Workspace".to_string());
1415
1416 let mut request =
1417 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1418 let mut response =
1419 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
1420 response.delay = 10; request.add_response(response);
1422 workspace.add_request(request.clone());
1423
1424 let context = RequestExecutionContext {
1425 workspace_id: workspace.id.clone(),
1426 environment_variables: HashMap::new(),
1427 global_headers: HashMap::new(),
1428 timeout_seconds: 30,
1429 ssl_verify: true,
1430 };
1431
1432 let start = std::time::Instant::now();
1433 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1434 let elapsed = start.elapsed();
1435
1436 assert!(result.is_ok());
1437 assert!(elapsed.as_millis() >= 10); }
1439
1440 #[test]
1441 fn test_find_matching_request_exact() {
1442 let processor = RequestProcessor::new();
1443 let mut workspace = Workspace::new("Test Workspace".to_string());
1444
1445 let request =
1446 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1447 workspace.add_request(request.clone());
1448
1449 let criteria = RequestMatchCriteria {
1450 method: HttpMethod::GET,
1451 path: "/api/test".to_string(),
1452 query_params: HashMap::new(),
1453 headers: HashMap::new(),
1454 body: None,
1455 };
1456
1457 let matched_id = processor.find_matching_request(&workspace, &criteria);
1458 assert_eq!(matched_id, Some(request.id));
1459 }
1460
1461 #[test]
1462 fn test_find_matching_request_with_query_params() {
1463 let processor = RequestProcessor::new();
1464 let mut workspace = Workspace::new("Test Workspace".to_string());
1465
1466 let mut request =
1467 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1468 request.query_params.insert("key".to_string(), "value".to_string());
1469 workspace.add_request(request.clone());
1470
1471 let mut criteria = RequestMatchCriteria {
1472 method: HttpMethod::GET,
1473 path: "/api/test".to_string(),
1474 query_params: HashMap::new(),
1475 headers: HashMap::new(),
1476 body: None,
1477 };
1478 criteria.query_params.insert("key".to_string(), "value".to_string());
1479
1480 let matched_id = processor.find_matching_request(&workspace, &criteria);
1481 assert_eq!(matched_id, Some(request.id));
1482 }
1483
1484 #[test]
1485 fn test_url_matches_pattern_exact() {
1486 let processor = RequestProcessor::new();
1487 assert!(processor.url_matches_pattern("/api/test", "/api/test"));
1488 }
1489
1490 #[test]
1491 fn test_url_matches_pattern_wildcard() {
1492 let processor = RequestProcessor::new();
1493 assert!(processor.url_matches_pattern("*", "/any/path"));
1494 assert!(processor.url_matches_pattern("/api/*", "/api/test"));
1495 assert!(processor.url_matches_pattern("/api/*", "/api/users"));
1496 }
1497
1498 #[test]
1499 fn test_url_matches_pattern_double_wildcard() {
1500 let processor = RequestProcessor::new();
1501 assert!(processor.url_matches_pattern("/api/**", "/api/test"));
1502 assert!(processor.url_matches_pattern("/api/**", "/api/users/123"));
1503 assert!(processor.url_matches_pattern("/api/**", "/api/v1/users/123/posts"));
1504 }
1505
1506 #[test]
1507 fn test_create_route_from_request() {
1508 let processor = RequestProcessor::new();
1509 let mut request =
1510 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1511 let response =
1512 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
1513 request.add_response(response);
1514
1515 let route = processor.create_route_from_request(&request).unwrap();
1516 assert_eq!(route.method, HttpMethod::GET);
1517 assert_eq!(route.path, "/api/test");
1518 }
1519
1520 #[test]
1521 fn test_create_route_from_disabled_request() {
1522 let processor = RequestProcessor::new();
1523 let mut request =
1524 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1525 request.enabled = false;
1526
1527 let result = processor.create_route_from_request(&request);
1528 assert!(result.is_err());
1529 }
1530
1531 #[test]
1532 fn test_update_route_registry() {
1533 let processor = RequestProcessor::new();
1534 let mut workspace = Workspace::new("Test Workspace".to_string());
1535
1536 let mut request1 =
1537 MockRequest::new("Request 1".to_string(), HttpMethod::GET, "/api/test1".to_string());
1538 let response1 =
1539 MockResponse::new(200, "Success".to_string(), r#"{"message": "test1"}"#.to_string());
1540 request1.add_response(response1);
1541 workspace.add_request(request1);
1542
1543 let mut request2 =
1544 MockRequest::new("Request 2".to_string(), HttpMethod::POST, "/api/test2".to_string());
1545 let response2 =
1546 MockResponse::new(201, "Created".to_string(), r#"{"message": "test2"}"#.to_string());
1547 request2.add_response(response2);
1548 workspace.add_request(request2);
1549
1550 let mut registry = RouteRegistry::new();
1551 processor.update_route_registry(&workspace, &mut registry).unwrap();
1552
1553 let get_routes = registry.find_http_routes(&HttpMethod::GET, "/api/test1");
1555 let post_routes = registry.find_http_routes(&HttpMethod::POST, "/api/test2");
1556 assert!(!get_routes.is_empty() || !post_routes.is_empty());
1557 }
1558
1559 #[test]
1560 fn test_get_request_metrics() {
1561 let processor = RequestProcessor::new();
1562 let mut workspace = Workspace::new("Test Workspace".to_string());
1563
1564 let mut request =
1565 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1566 let mut response =
1567 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
1568 response.record_usage(request.id.clone(), 100);
1569 request.add_response(response);
1570 workspace.add_request(request);
1571
1572 let metrics = processor.get_request_metrics(&workspace);
1573 assert_eq!(metrics.total_requests, 1);
1574 assert!(metrics.successful_requests > 0 || metrics.failed_requests > 0);
1575 }
1576
1577 #[test]
1578 fn test_find_matching_request_in_folder() {
1579 let processor = RequestProcessor::new();
1580 let mut workspace = Workspace::new("Test Workspace".to_string());
1581
1582 let mut folder = Folder::new("Test Folder".to_string());
1583 let request =
1584 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1585 folder.add_request(request.clone());
1586 workspace.add_folder(folder);
1587
1588 let criteria = RequestMatchCriteria {
1589 method: HttpMethod::GET,
1590 path: "/api/test".to_string(),
1591 query_params: HashMap::new(),
1592 headers: HashMap::new(),
1593 body: None,
1594 };
1595
1596 let matched_id = processor.find_matching_request(&workspace, &criteria);
1597 assert_eq!(matched_id, Some(request.id));
1598 }
1599
1600 #[tokio::test]
1601 async fn test_execute_request_with_cache_hit() {
1602 let processor = RequestProcessor::with_performance_config(
1604 None,
1605 100,
1606 Duration::from_secs(60),
1607 true, );
1609 let mut workspace = Workspace::new("Test Workspace".to_string());
1610
1611 let mut request =
1612 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1613 request.add_response(MockResponse::new(
1614 200,
1615 "Success".to_string(),
1616 r#"{"message": "test"}"#.to_string(),
1617 ));
1618 workspace.add_request(request.clone());
1619
1620 let context = RequestExecutionContext {
1621 workspace_id: workspace.id.clone(),
1622 environment_variables: HashMap::new(),
1623 global_headers: HashMap::new(),
1624 timeout_seconds: 30,
1625 ssl_verify: true,
1626 };
1627
1628 let result1 =
1630 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1631 assert!(result1.success);
1632 let result2 =
1636 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1637 assert!(result2.success);
1638 assert_eq!(result2.duration_ms, 1);
1640 }
1641
1642 #[tokio::test]
1643 async fn test_execute_request_with_cache_miss() {
1644 let processor = RequestProcessor::with_performance_config(
1646 None,
1647 100,
1648 Duration::from_secs(60),
1649 true, );
1651 let mut workspace = Workspace::new("Test Workspace".to_string());
1652
1653 let mut request =
1654 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1655 request.add_response(MockResponse::new(
1656 200,
1657 "Success".to_string(),
1658 r#"{"message": "test"}"#.to_string(),
1659 ));
1660 workspace.add_request(request.clone());
1661
1662 let context = RequestExecutionContext {
1663 workspace_id: workspace.id.clone(),
1664 environment_variables: HashMap::new(),
1665 global_headers: HashMap::new(),
1666 timeout_seconds: 30,
1667 ssl_verify: true,
1668 };
1669
1670 let result =
1672 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1673 assert!(result.success);
1674 assert!(result.duration_ms != 1 || result.duration_ms == 0);
1677 }
1678
1679 #[tokio::test]
1680 async fn test_execute_request_not_found_with_optimizations() {
1681 let processor = RequestProcessor::with_performance_config(
1683 None,
1684 100,
1685 Duration::from_secs(60),
1686 true, );
1688 let mut workspace = Workspace::new("Test Workspace".to_string());
1689
1690 let non_existent_id = EntityId::new();
1691 let context = RequestExecutionContext {
1692 workspace_id: workspace.id.clone(),
1693 environment_variables: HashMap::new(),
1694 global_headers: HashMap::new(),
1695 timeout_seconds: 30,
1696 ssl_verify: true,
1697 };
1698
1699 let result = processor.execute_request(&mut workspace, &non_existent_id, &context).await;
1701 assert!(result.is_err());
1702 assert!(result.unwrap_err().to_string().contains("not found"));
1703 }
1704
1705 #[tokio::test]
1706 async fn test_execute_request_caches_response() {
1707 let processor = RequestProcessor::with_performance_config(
1709 None,
1710 100,
1711 Duration::from_secs(60),
1712 true, );
1714 let mut workspace = Workspace::new("Test Workspace".to_string());
1715
1716 let mut request =
1717 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1718 request.add_response(MockResponse::new(
1719 200,
1720 "Success".to_string(),
1721 r#"{"message": "test"}"#.to_string(),
1722 ));
1723 workspace.add_request(request.clone());
1724
1725 let context = RequestExecutionContext {
1726 workspace_id: workspace.id.clone(),
1727 environment_variables: HashMap::new(),
1728 global_headers: HashMap::new(),
1729 timeout_seconds: 30,
1730 ssl_verify: true,
1731 };
1732
1733 let result1 =
1735 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1736 assert!(result1.success);
1737
1738 let result2 =
1740 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1741 assert!(result2.success);
1742 assert!(result2.duration_ms <= 1);
1745 }
1746
1747 #[tokio::test]
1748 async fn test_execute_request_with_no_active_response() {
1749 let processor = RequestProcessor::new();
1751 let mut workspace = Workspace::new("Test Workspace".to_string());
1752
1753 let mut request =
1754 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1755 workspace.add_request(request.clone());
1757
1758 let context = RequestExecutionContext {
1759 workspace_id: workspace.id.clone(),
1760 environment_variables: HashMap::new(),
1761 global_headers: HashMap::new(),
1762 timeout_seconds: 30,
1763 ssl_verify: true,
1764 };
1765
1766 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1768 assert!(result.is_err());
1769 assert!(result.unwrap_err().to_string().contains("No active response"));
1770 }
1771
1772 #[tokio::test]
1773 async fn test_execute_request_with_response_processing_error() {
1774 let processor = RequestProcessor::new();
1776 let mut workspace = Workspace::new("Test Workspace".to_string());
1777
1778 let mut request =
1779 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1780 let mut response =
1782 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
1783 response.delay = 0; request.add_response(response);
1785 workspace.add_request(request.clone());
1786
1787 let context = RequestExecutionContext {
1788 workspace_id: workspace.id.clone(),
1789 environment_variables: HashMap::new(),
1790 global_headers: HashMap::new(),
1791 timeout_seconds: 30,
1792 ssl_verify: true,
1793 };
1794
1795 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1797 assert!(result.is_ok());
1798 }
1799
1800 #[tokio::test]
1801 async fn test_execute_request_records_usage() {
1802 let processor = RequestProcessor::new();
1804 let mut workspace = Workspace::new("Test Workspace".to_string());
1805
1806 let mut request =
1807 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1808 request.add_response(MockResponse::new(
1809 200,
1810 "Success".to_string(),
1811 r#"{"message": "test"}"#.to_string(),
1812 ));
1813 workspace.add_request(request.clone());
1814
1815 let context = RequestExecutionContext {
1816 workspace_id: workspace.id.clone(),
1817 environment_variables: HashMap::new(),
1818 global_headers: HashMap::new(),
1819 timeout_seconds: 30,
1820 ssl_verify: true,
1821 };
1822
1823 let result =
1825 processor.execute_request(&mut workspace, &request.id, &context).await.unwrap();
1826 assert!(result.success);
1827
1828 let request_ref = workspace.requests.iter().find(|r| r.id == request.id).unwrap();
1830 let response_ref = request_ref.active_response().unwrap();
1831 assert!(!response_ref.history.is_empty());
1832 }
1833
1834 #[tokio::test]
1835 async fn test_execute_request_validation_error() {
1836 let processor = RequestProcessor::with_performance_config(
1838 None,
1839 100,
1840 Duration::from_secs(60),
1841 true, );
1843 let mut workspace = Workspace::new("Test Workspace".to_string());
1844
1845 let mut request =
1847 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1848 request.enabled = false; request.add_response(MockResponse::new(
1850 200,
1851 "Success".to_string(),
1852 r#"{"message": "test"}"#.to_string(),
1853 ));
1854 workspace.add_request(request.clone());
1855
1856 let context = RequestExecutionContext {
1857 workspace_id: workspace.id.clone(),
1858 environment_variables: HashMap::new(),
1859 global_headers: HashMap::new(),
1860 timeout_seconds: 30,
1861 ssl_verify: true,
1862 };
1863
1864 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1866 assert!(result.is_err());
1867 let error_msg = result.unwrap_err().to_string();
1869 assert!(error_msg.contains("validation") || error_msg.contains("disabled"));
1870 }
1871
1872 #[tokio::test]
1873 async fn test_execute_request_validation_error_with_collector() {
1874 let processor = RequestProcessor::with_performance_config(
1876 None,
1877 100,
1878 Duration::from_secs(60),
1879 true, );
1881 let mut workspace = Workspace::new("Test Workspace".to_string());
1882
1883 let mut request =
1885 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "".to_string());
1886 request.add_response(MockResponse::new(
1887 200,
1888 "Success".to_string(),
1889 r#"{"message": "test"}"#.to_string(),
1890 ));
1891 workspace.add_request(request.clone());
1892
1893 let context = RequestExecutionContext {
1894 workspace_id: workspace.id.clone(),
1895 environment_variables: HashMap::new(),
1896 global_headers: HashMap::new(),
1897 timeout_seconds: 30,
1898 ssl_verify: true,
1899 };
1900
1901 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1903 assert!(result.is_err());
1904 let error_msg = result.unwrap_err().to_string();
1906 assert!(error_msg.contains("validation") || error_msg.contains("empty"));
1907 }
1908
1909 #[tokio::test]
1910 async fn test_execute_request_with_invalid_status_code() {
1911 let processor = RequestProcessor::new();
1913 let mut workspace = Workspace::new("Test Workspace".to_string());
1914
1915 let mut request =
1916 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1917 request.add_response(MockResponse::new(
1919 999,
1920 "Invalid".to_string(),
1921 r#"{"message": "test"}"#.to_string(),
1922 ));
1923 workspace.add_request(request.clone());
1924
1925 let context = RequestExecutionContext {
1926 workspace_id: workspace.id.clone(),
1927 environment_variables: HashMap::new(),
1928 global_headers: HashMap::new(),
1929 timeout_seconds: 30,
1930 ssl_verify: true,
1931 };
1932
1933 let result = processor.execute_request(&mut workspace, &request.id, &context).await;
1935 assert!(result.is_err());
1936 let error_msg = result.unwrap_err().to_string();
1937 assert!(error_msg.contains("validation") || error_msg.contains("Invalid status"));
1938 }
1939
1940 #[tokio::test]
1941 async fn test_validate_request_cached() {
1942 let processor = RequestProcessor::with_performance_config(
1944 None,
1945 100,
1946 Duration::from_secs(60),
1947 true, );
1949 let mut workspace = Workspace::new("Test Workspace".to_string());
1950
1951 let mut request =
1952 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1953 request.add_response(MockResponse::new(
1954 200,
1955 "Success".to_string(),
1956 r#"{"message": "test"}"#.to_string(),
1957 ));
1958 workspace.add_request(request.clone());
1959
1960 let context = RequestExecutionContext {
1961 workspace_id: workspace.id.clone(),
1962 environment_variables: HashMap::new(),
1963 global_headers: HashMap::new(),
1964 timeout_seconds: 30,
1965 ssl_verify: true,
1966 };
1967
1968 let validation1 = processor.validate_request_cached(&request, &context).await.unwrap();
1970 assert!(validation1.is_valid);
1971
1972 let validation2 = processor.validate_request_cached(&request, &context).await.unwrap();
1974 assert!(validation2.is_valid);
1975 }
1976
1977 #[test]
1978 fn test_create_route_from_request_with_metadata() {
1979 let processor = RequestProcessor::new();
1981 let mut request =
1982 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
1983 request.add_response(MockResponse::new(
1984 200,
1985 "Success".to_string(),
1986 r#"{"message": "test"}"#.to_string(),
1987 ));
1988
1989 let route = processor.create_route_from_request(&request).unwrap();
1990 assert_eq!(route.method, HttpMethod::GET);
1991 assert_eq!(route.path, "/api/test");
1992 assert_eq!(route.metadata.get("status_code"), Some(&serde_json::json!(200)));
1993 }
1994
1995 #[test]
1996 fn test_create_route_from_request_disabled_error() {
1997 let processor = RequestProcessor::new();
1999 let mut request =
2000 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
2001 request.enabled = false;
2002 request.add_response(MockResponse::new(
2003 200,
2004 "Success".to_string(),
2005 r#"{"message": "test"}"#.to_string(),
2006 ));
2007
2008 let result = processor.create_route_from_request(&request);
2009 assert!(result.is_err());
2010 assert!(result.unwrap_err().to_string().contains("disabled"));
2011 }
2012
2013 #[test]
2014 fn test_create_route_from_request_no_active_response_error() {
2015 let processor = RequestProcessor::new();
2017 let request =
2018 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
2019 let result = processor.create_route_from_request(&request);
2022 assert!(result.is_err());
2023 assert!(result.unwrap_err().to_string().contains("No active response"));
2024 }
2025
2026 #[test]
2027 fn test_update_route_registry_adds_routes() {
2028 let processor = RequestProcessor::new();
2030 let mut workspace = Workspace::new("Test Workspace".to_string());
2031 let mut registry = RouteRegistry::new();
2032
2033 let mut request =
2034 MockRequest::new("Test Request".to_string(), HttpMethod::GET, "/api/test".to_string());
2035 request.add_response(MockResponse::new(
2036 200,
2037 "Success".to_string(),
2038 r#"{"message": "test"}"#.to_string(),
2039 ));
2040 workspace.add_request(request);
2041
2042 processor.update_route_registry(&workspace, &mut registry).unwrap();
2043
2044 let found_routes = registry.find_http_routes(&HttpMethod::GET, "/api/test");
2046 assert!(!found_routes.is_empty());
2047 }
2048
2049 #[test]
2050 fn test_update_route_registry_with_folder_requests() {
2051 let processor = RequestProcessor::new();
2053 let mut workspace = Workspace::new("Test Workspace".to_string());
2054 let mut registry = RouteRegistry::new();
2055
2056 let mut folder = Folder::new("Test Folder".to_string());
2057 let mut request =
2058 MockRequest::new("Test Request".to_string(), HttpMethod::POST, "/api/test".to_string());
2059 request.add_response(MockResponse::new(
2060 201,
2061 "Created".to_string(),
2062 r#"{"message": "created"}"#.to_string(),
2063 ));
2064 folder.add_request(request);
2065 workspace.add_folder(folder);
2066
2067 processor.update_route_registry(&workspace, &mut registry).unwrap();
2068
2069 let found_routes = registry.find_http_routes(&HttpMethod::POST, "/api/test");
2071 assert!(!found_routes.is_empty());
2072 }
2073
2074 #[test]
2075 fn test_convert_mock_response_to_cached_response() {
2076 let processor = RequestProcessor::new();
2078 let mut response =
2079 MockResponse::new(200, "Success".to_string(), r#"{"message": "test"}"#.to_string());
2080 response
2081 .headers
2082 .insert("Content-Type".to_string(), "application/json".to_string());
2083
2084 let cached = processor.convert_mock_response_to_cached_response(&response);
2085 assert_eq!(cached.status_code, 200);
2086 assert_eq!(cached.body, r#"{"message": "test"}"#);
2087 assert_eq!(cached.content_type, Some("application/json".to_string()));
2088 }
2089
2090 #[test]
2091 fn test_convert_cached_response_to_mock_response() {
2092 let processor = RequestProcessor::new();
2094 let cached = CachedResponse {
2095 status_code: 200,
2096 headers: HashMap::from([("Content-Type".to_string(), "application/json".to_string())]),
2097 body: r#"{"message": "test"}"#.to_string(),
2098 content_type: Some("application/json".to_string()),
2099 };
2100
2101 let mock_response = processor.convert_cached_response_to_mock_response(cached);
2102 assert_eq!(mock_response.status_code, 200);
2103 assert_eq!(mock_response.body, r#"{"message": "test"}"#);
2104 assert_eq!(mock_response.name, "Cached Response");
2105 assert_eq!(mock_response.delay, 0);
2106 }
2107
2108 #[tokio::test]
2109 async fn test_get_performance_summary() {
2110 let processor = RequestProcessor::new();
2112 let summary = processor.get_performance_summary().await;
2113 let _ = summary.total_requests;
2115 }
2116
2117 #[tokio::test]
2118 async fn test_get_cache_stats() {
2119 let processor = RequestProcessor::new();
2121 let (response_stats, validation_stats) = processor.get_cache_stats().await;
2122 let _ = response_stats.hits;
2124 let _ = validation_stats.hits;
2125 }
2126}