1use crate::domain::error::{DomainError, DomainResult};
4use crate::domain::models::Recommendation;
5use serde::{Deserialize, Serialize};
6use std::collections::HashMap;
7
8#[derive(Debug, Clone, Serialize, Deserialize)]
26pub struct DomainRequest {
27 pub id: String,
29 pub domains: Vec<String>,
31 pub content: String,
33 pub context: HashMap<String, serde_json::Value>,
35}
36
37#[derive(Debug, Clone)]
51pub struct DomainCoordinator;
52
53impl DomainCoordinator {
54 pub fn new() -> Self {
56 Self
57 }
58
59 pub fn route_request(&self, request: &DomainRequest) -> DomainResult<Vec<String>> {
69 if request.domains.is_empty() {
70 return Err(DomainError::coordination_error(
71 "Request must specify at least one target domain",
72 ));
73 }
74
75 let valid_domains = vec!["web", "backend", "devops"];
77 for domain in &request.domains {
78 if !valid_domains.contains(&domain.as_str()) && !domain.starts_with("custom-") {
79 return Err(DomainError::coordination_error(format!(
80 "Unknown domain: {}",
81 domain
82 )));
83 }
84 }
85
86 Ok(request.domains.clone())
87 }
88
89 pub fn infer_domains(&self, content: &str) -> DomainResult<Vec<String>> {
99 let mut domains = Vec::new();
100 let content_lower = content.to_lowercase();
101
102 if content_lower.contains("frontend")
104 || content_lower.contains("react")
105 || content_lower.contains("vue")
106 || content_lower.contains("angular")
107 || content_lower.contains("styling")
108 || content_lower.contains("ui")
109 || content_lower.contains("web")
110 {
111 domains.push("web".to_string());
112 }
113
114 if content_lower.contains("backend")
116 || content_lower.contains("api")
117 || content_lower.contains("database")
118 || content_lower.contains("server")
119 || content_lower.contains("rest")
120 || content_lower.contains("graphql")
121 || content_lower.contains("microservice")
122 {
123 domains.push("backend".to_string());
124 }
125
126 if content_lower.contains("devops")
128 || content_lower.contains("deployment")
129 || content_lower.contains("ci/cd")
130 || content_lower.contains("docker")
131 || content_lower.contains("kubernetes")
132 || content_lower.contains("infrastructure")
133 || content_lower.contains("terraform")
134 {
135 domains.push("devops".to_string());
136 }
137
138 if domains.is_empty() {
140 domains = vec!["web".to_string(), "backend".to_string(), "devops".to_string()];
141 }
142
143 Ok(domains)
144 }
145
146 pub fn coordinate_responses(
156 &self,
157 responses: Vec<Recommendation>,
158 ) -> DomainResult<CoordinatedResponse> {
159 let mut by_domain: std::collections::HashMap<String, Vec<Recommendation>> =
161 std::collections::HashMap::new();
162
163 for response in responses {
164 by_domain
165 .entry(response.domain.clone())
166 .or_insert_with(Vec::new)
167 .push(response);
168 }
169
170 let coordinated = CoordinatedResponse {
172 recommendations: by_domain.values().flatten().cloned().collect(),
173 domain_count: by_domain.len(),
174 total_recommendations: by_domain.values().map(|v| v.len()).sum(),
175 };
176
177 Ok(coordinated)
178 }
179
180 pub fn sequence_operations(
190 &self,
191 operations: Vec<Operation>,
192 ) -> DomainResult<Vec<Operation>> {
193 let mut sequenced = operations;
195 sequenced.sort_by_key(|op| op.priority);
196
197 Ok(sequenced)
198 }
199
200 pub fn validate_consistency(&self, recommendations: &[Recommendation]) -> DomainResult<bool> {
210 if recommendations.is_empty() {
212 return Ok(true);
213 }
214
215 for rec in recommendations {
217 if rec.domain.is_empty() || rec.category.is_empty() {
218 return Err(DomainError::coordination_error(
219 "Recommendation missing required fields",
220 ));
221 }
222 }
223
224 let mut domains_present = std::collections::HashSet::new();
226 for rec in recommendations {
227 domains_present.insert(rec.domain.clone());
228 }
229
230 if domains_present.len() > 1 {
232 let mut by_domain: HashMap<String, Vec<&Recommendation>> = HashMap::new();
234 for rec in recommendations {
235 by_domain
236 .entry(rec.domain.clone())
237 .or_insert_with(Vec::new)
238 .push(rec);
239 }
240
241 for domain_recs in by_domain.values() {
243 if domain_recs.is_empty() {
244 return Err(DomainError::coordination_error(
245 "Domain has no recommendations",
246 ));
247 }
248 }
249 }
250
251 Ok(true)
252 }
253
254 pub fn coordinate_full_stack(
264 &self,
265 recommendations: Vec<Recommendation>,
266 ) -> DomainResult<FullStackCoordination> {
267 self.validate_consistency(&recommendations)?;
269
270 let mut by_domain: HashMap<String, Vec<Recommendation>> = HashMap::new();
272 for rec in recommendations {
273 by_domain
274 .entry(rec.domain.clone())
275 .or_insert_with(Vec::new)
276 .push(rec);
277 }
278
279 let has_web = by_domain.contains_key("web");
281 let has_backend = by_domain.contains_key("backend");
282 let has_devops = by_domain.contains_key("devops");
283
284 let is_full_stack = has_web && has_backend && has_devops;
285
286 Ok(FullStackCoordination {
287 web_recommendations: by_domain.get("web").cloned().unwrap_or_default(),
288 backend_recommendations: by_domain.get("backend").cloned().unwrap_or_default(),
289 devops_recommendations: by_domain.get("devops").cloned().unwrap_or_default(),
290 is_full_stack,
291 total_recommendations: by_domain.values().map(|v| v.len()).sum(),
292 })
293 }
294
295 pub fn ensure_full_stack_consistency(
305 &self,
306 coordination: &FullStackCoordination,
307 ) -> DomainResult<bool> {
308 if !coordination.is_full_stack {
309 return Ok(true);
310 }
311
312 if coordination.web_recommendations.is_empty() {
314 return Err(DomainError::coordination_error(
315 "Web domain has no recommendations",
316 ));
317 }
318
319 if coordination.backend_recommendations.is_empty() {
320 return Err(DomainError::coordination_error(
321 "Backend domain has no recommendations",
322 ));
323 }
324
325 if coordination.devops_recommendations.is_empty() {
326 return Err(DomainError::coordination_error(
327 "DevOps domain has no recommendations",
328 ));
329 }
330
331 let mut all_techs = std::collections::HashSet::new();
333 for rec in &coordination.web_recommendations {
334 for tech in &rec.technologies {
335 all_techs.insert(tech.clone());
336 }
337 }
338 for rec in &coordination.backend_recommendations {
339 for tech in &rec.technologies {
340 all_techs.insert(tech.clone());
341 }
342 }
343 for rec in &coordination.devops_recommendations {
344 for tech in &rec.technologies {
345 all_techs.insert(tech.clone());
346 }
347 }
348
349 if all_techs.is_empty() {
351 return Err(DomainError::coordination_error(
352 "No technologies recommended across domains",
353 ));
354 }
355
356 Ok(true)
357 }
358
359 pub fn detect_full_stack_conflicts(
369 &self,
370 coordination: &FullStackCoordination,
371 ) -> DomainResult<Vec<String>> {
372 let mut conflicts = Vec::new();
373
374 let web_techs: std::collections::HashSet<_> = coordination
376 .web_recommendations
377 .iter()
378 .flat_map(|r| r.technologies.clone())
379 .collect();
380
381 let backend_techs: std::collections::HashSet<_> = coordination
382 .backend_recommendations
383 .iter()
384 .flat_map(|r| r.technologies.clone())
385 .collect();
386
387 let devops_techs: std::collections::HashSet<_> = coordination
388 .devops_recommendations
389 .iter()
390 .flat_map(|r| r.technologies.clone())
391 .collect();
392
393 let incompatible_pairs = vec![
395 ("Webpack", "Vite"),
396 ("npm", "yarn"),
397 ("PostgreSQL", "MongoDB"),
398 ("REST", "GraphQL"),
399 ("Microservices", "Monolithic"),
400 ];
401
402 for (tech_a, tech_b) in incompatible_pairs {
403 let has_a_web = web_techs.contains(tech_a);
404 let has_b_web = web_techs.contains(tech_b);
405 let has_a_backend = backend_techs.contains(tech_a);
406 let has_b_backend = backend_techs.contains(tech_b);
407 let has_a_devops = devops_techs.contains(tech_a);
408 let has_b_devops = devops_techs.contains(tech_b);
409
410 if (has_a_web || has_a_backend || has_a_devops)
411 && (has_b_web || has_b_backend || has_b_devops)
412 {
413 conflicts.push(format!(
414 "Incompatible technologies: {} and {} are recommended",
415 tech_a, tech_b
416 ));
417 }
418 }
419
420 Ok(conflicts)
421 }
422}
423
424impl Default for DomainCoordinator {
425 fn default() -> Self {
426 Self::new()
427 }
428}
429
430#[derive(Debug, Clone)]
432pub struct CoordinatedResponse {
433 pub recommendations: Vec<Recommendation>,
435 pub domain_count: usize,
437 pub total_recommendations: usize,
439}
440
441#[derive(Debug, Clone)]
460pub struct FullStackCoordination {
461 pub web_recommendations: Vec<Recommendation>,
463 pub backend_recommendations: Vec<Recommendation>,
465 pub devops_recommendations: Vec<Recommendation>,
467 pub is_full_stack: bool,
469 pub total_recommendations: usize,
471}
472
473#[derive(Debug, Clone)]
475pub struct Operation {
476 pub id: String,
478 pub name: String,
480 pub priority: u32,
482 pub dependencies: Vec<String>,
484}
485
486#[cfg(test)]
487mod tests {
488 use super::*;
489 use crate::domain::models::Recommendation;
490
491 fn create_test_recommendation(domain: &str) -> Recommendation {
492 Recommendation {
493 domain: domain.to_string(),
494 category: "test".to_string(),
495 content: "Test recommendation".to_string(),
496 technologies: vec!["Tech1".to_string()],
497 rationale: "Test rationale".to_string(),
498 }
499 }
500
501 #[test]
502 fn test_coordinator_creation() {
503 let coordinator = DomainCoordinator::new();
504 assert_eq!(std::mem::size_of_val(&coordinator), 0); }
506
507 #[test]
508 fn test_coordinate_responses_single_domain() {
509 let coordinator = DomainCoordinator::new();
510 let responses = vec![create_test_recommendation("web")];
511
512 let coordinated = coordinator.coordinate_responses(responses).unwrap();
513
514 assert_eq!(coordinated.domain_count, 1);
515 assert_eq!(coordinated.total_recommendations, 1);
516 }
517
518 #[test]
519 fn test_coordinate_responses_multiple_domains() {
520 let coordinator = DomainCoordinator::new();
521 let responses = vec![
522 create_test_recommendation("web"),
523 create_test_recommendation("backend"),
524 create_test_recommendation("devops"),
525 ];
526
527 let coordinated = coordinator.coordinate_responses(responses).unwrap();
528
529 assert_eq!(coordinated.domain_count, 3);
530 assert_eq!(coordinated.total_recommendations, 3);
531 }
532
533 #[test]
534 fn test_coordinate_responses_empty() {
535 let coordinator = DomainCoordinator::new();
536 let responses = vec![];
537
538 let coordinated = coordinator.coordinate_responses(responses).unwrap();
539
540 assert_eq!(coordinated.domain_count, 0);
541 assert_eq!(coordinated.total_recommendations, 0);
542 }
543
544 #[test]
545 fn test_sequence_operations() {
546 let coordinator = DomainCoordinator::new();
547 let operations = vec![
548 Operation {
549 id: "op1".to_string(),
550 name: "Operation 1".to_string(),
551 priority: 3,
552 dependencies: vec![],
553 },
554 Operation {
555 id: "op2".to_string(),
556 name: "Operation 2".to_string(),
557 priority: 1,
558 dependencies: vec![],
559 },
560 Operation {
561 id: "op3".to_string(),
562 name: "Operation 3".to_string(),
563 priority: 2,
564 dependencies: vec![],
565 },
566 ];
567
568 let sequenced = coordinator.sequence_operations(operations).unwrap();
569
570 assert_eq!(sequenced[0].priority, 1);
571 assert_eq!(sequenced[1].priority, 2);
572 assert_eq!(sequenced[2].priority, 3);
573 }
574
575 #[test]
576 fn test_validate_consistency_valid() {
577 let coordinator = DomainCoordinator::new();
578 let recommendations = vec![create_test_recommendation("web")];
579
580 assert!(coordinator.validate_consistency(&recommendations).unwrap());
581 }
582
583 #[test]
584 fn test_validate_consistency_empty() {
585 let coordinator = DomainCoordinator::new();
586 let recommendations = vec![];
587
588 assert!(coordinator.validate_consistency(&recommendations).unwrap());
589 }
590
591 #[test]
592 fn test_validate_consistency_invalid_domain() {
593 let coordinator = DomainCoordinator::new();
594 let mut rec = create_test_recommendation("web");
595 rec.domain = String::new();
596
597 assert!(coordinator.validate_consistency(&[rec]).is_err());
598 }
599
600 #[test]
601 fn test_validate_consistency_invalid_category() {
602 let coordinator = DomainCoordinator::new();
603 let mut rec = create_test_recommendation("web");
604 rec.category = String::new();
605
606 assert!(coordinator.validate_consistency(&[rec]).is_err());
607 }
608
609 #[test]
610 fn test_default_coordinator() {
611 let coordinator = DomainCoordinator::default();
612 let responses = vec![create_test_recommendation("web")];
613
614 assert!(coordinator.coordinate_responses(responses).is_ok());
615 }
616
617 #[test]
618 fn test_coordinated_response_structure() {
619 let coordinator = DomainCoordinator::new();
620 let responses = vec![
621 create_test_recommendation("web"),
622 create_test_recommendation("web"),
623 create_test_recommendation("backend"),
624 ];
625
626 let coordinated = coordinator.coordinate_responses(responses).unwrap();
627
628 assert_eq!(coordinated.domain_count, 2);
629 assert_eq!(coordinated.total_recommendations, 3);
630 assert_eq!(coordinated.recommendations.len(), 3);
631 }
632
633 #[test]
634 fn test_operation_sequencing_with_dependencies() {
635 let coordinator = DomainCoordinator::new();
636 let operations = vec![
637 Operation {
638 id: "setup".to_string(),
639 name: "Setup".to_string(),
640 priority: 1,
641 dependencies: vec![],
642 },
643 Operation {
644 id: "deploy".to_string(),
645 name: "Deploy".to_string(),
646 priority: 2,
647 dependencies: vec!["setup".to_string()],
648 },
649 ];
650
651 let sequenced = coordinator.sequence_operations(operations).unwrap();
652
653 assert_eq!(sequenced[0].id, "setup");
654 assert_eq!(sequenced[1].id, "deploy");
655 }
656
657 #[test]
658 fn test_multiple_recommendations_same_domain() {
659 let coordinator = DomainCoordinator::new();
660 let responses = vec![
661 create_test_recommendation("web"),
662 create_test_recommendation("web"),
663 create_test_recommendation("web"),
664 ];
665
666 let coordinated = coordinator.coordinate_responses(responses).unwrap();
667
668 assert_eq!(coordinated.domain_count, 1);
669 assert_eq!(coordinated.total_recommendations, 3);
670 }
671
672 #[test]
673 fn test_route_request_valid() {
674 let coordinator = DomainCoordinator::new();
675 let request = DomainRequest {
676 id: "req-1".to_string(),
677 domains: vec!["web".to_string(), "backend".to_string()],
678 content: "Help me set up a full-stack app".to_string(),
679 context: std::collections::HashMap::new(),
680 };
681
682 let routed = coordinator.route_request(&request).unwrap();
683 assert_eq!(routed.len(), 2);
684 assert!(routed.contains(&"web".to_string()));
685 assert!(routed.contains(&"backend".to_string()));
686 }
687
688 #[test]
689 fn test_route_request_empty_domains() {
690 let coordinator = DomainCoordinator::new();
691 let request = DomainRequest {
692 id: "req-1".to_string(),
693 domains: vec![],
694 content: "Help me set up a full-stack app".to_string(),
695 context: std::collections::HashMap::new(),
696 };
697
698 assert!(coordinator.route_request(&request).is_err());
699 }
700
701 #[test]
702 fn test_route_request_invalid_domain() {
703 let coordinator = DomainCoordinator::new();
704 let request = DomainRequest {
705 id: "req-1".to_string(),
706 domains: vec!["invalid-domain".to_string()],
707 content: "Help me set up a full-stack app".to_string(),
708 context: std::collections::HashMap::new(),
709 };
710
711 assert!(coordinator.route_request(&request).is_err());
712 }
713
714 #[test]
715 fn test_route_request_custom_domain() {
716 let coordinator = DomainCoordinator::new();
717 let request = DomainRequest {
718 id: "req-1".to_string(),
719 domains: vec!["custom-mobile".to_string()],
720 content: "Help me set up a mobile app".to_string(),
721 context: std::collections::HashMap::new(),
722 };
723
724 let routed = coordinator.route_request(&request).unwrap();
725 assert_eq!(routed.len(), 1);
726 assert_eq!(routed[0], "custom-mobile");
727 }
728
729 #[test]
730 fn test_infer_domains_web() {
731 let coordinator = DomainCoordinator::new();
732 let domains = coordinator
733 .infer_domains("I need help with React and styling")
734 .unwrap();
735
736 assert!(domains.contains(&"web".to_string()));
737 }
738
739 #[test]
740 fn test_infer_domains_backend() {
741 let coordinator = DomainCoordinator::new();
742 let domains = coordinator
743 .infer_domains("I need help with REST API and database design")
744 .unwrap();
745
746 assert!(domains.contains(&"backend".to_string()));
747 }
748
749 #[test]
750 fn test_infer_domains_devops() {
751 let coordinator = DomainCoordinator::new();
752 let domains = coordinator
753 .infer_domains("I need help with Docker and Kubernetes")
754 .unwrap();
755
756 assert!(domains.contains(&"devops".to_string()));
757 }
758
759 #[test]
760 fn test_infer_domains_full_stack() {
761 let coordinator = DomainCoordinator::new();
762 let domains = coordinator
763 .infer_domains("I need help with React, Node.js API, and Docker deployment")
764 .unwrap();
765
766 assert!(domains.contains(&"web".to_string()));
767 assert!(domains.contains(&"backend".to_string()));
768 assert!(domains.contains(&"devops".to_string()));
769 }
770
771 #[test]
772 fn test_infer_domains_empty_defaults_to_all() {
773 let coordinator = DomainCoordinator::new();
774 let domains = coordinator.infer_domains("general help").unwrap();
775
776 assert_eq!(domains.len(), 3);
777 assert!(domains.contains(&"web".to_string()));
778 assert!(domains.contains(&"backend".to_string()));
779 assert!(domains.contains(&"devops".to_string()));
780 }
781
782 #[test]
783 fn test_coordinate_full_stack() {
784 let coordinator = DomainCoordinator::new();
785 let responses = vec![
786 create_test_recommendation("web"),
787 create_test_recommendation("backend"),
788 create_test_recommendation("devops"),
789 ];
790
791 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
792
793 assert!(coordination.is_full_stack);
794 assert_eq!(coordination.web_recommendations.len(), 1);
795 assert_eq!(coordination.backend_recommendations.len(), 1);
796 assert_eq!(coordination.devops_recommendations.len(), 1);
797 assert_eq!(coordination.total_recommendations, 3);
798 }
799
800 #[test]
801 fn test_coordinate_full_stack_partial() {
802 let coordinator = DomainCoordinator::new();
803 let responses = vec![
804 create_test_recommendation("web"),
805 create_test_recommendation("backend"),
806 ];
807
808 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
809
810 assert!(!coordination.is_full_stack);
811 assert_eq!(coordination.web_recommendations.len(), 1);
812 assert_eq!(coordination.backend_recommendations.len(), 1);
813 assert_eq!(coordination.devops_recommendations.len(), 0);
814 assert_eq!(coordination.total_recommendations, 2);
815 }
816
817 #[test]
818 fn test_validate_consistency_cross_domain() {
819 let coordinator = DomainCoordinator::new();
820 let recommendations = vec![
821 create_test_recommendation("web"),
822 create_test_recommendation("backend"),
823 create_test_recommendation("devops"),
824 ];
825
826 assert!(coordinator.validate_consistency(&recommendations).unwrap());
827 }
828
829 #[test]
830 fn test_domain_request_creation() {
831 let request = DomainRequest {
832 id: "req-1".to_string(),
833 domains: vec!["web".to_string()],
834 content: "Help me".to_string(),
835 context: std::collections::HashMap::new(),
836 };
837
838 assert_eq!(request.id, "req-1");
839 assert_eq!(request.domains.len(), 1);
840 }
841
842 #[test]
843 fn test_full_stack_coordination_structure() {
844 let coordinator = DomainCoordinator::new();
845 let responses = vec![
846 create_test_recommendation("web"),
847 create_test_recommendation("web"),
848 create_test_recommendation("backend"),
849 create_test_recommendation("devops"),
850 ];
851
852 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
853
854 assert!(coordination.is_full_stack);
855 assert_eq!(coordination.web_recommendations.len(), 2);
856 assert_eq!(coordination.backend_recommendations.len(), 1);
857 assert_eq!(coordination.devops_recommendations.len(), 1);
858 assert_eq!(coordination.total_recommendations, 4);
859 }
860
861 #[test]
862 fn test_ensure_full_stack_consistency_valid() {
863 let coordinator = DomainCoordinator::new();
864 let responses = vec![
865 create_test_recommendation("web"),
866 create_test_recommendation("backend"),
867 create_test_recommendation("devops"),
868 ];
869
870 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
871 assert!(coordinator
872 .ensure_full_stack_consistency(&coordination)
873 .unwrap());
874 }
875
876 #[test]
877 fn test_ensure_full_stack_consistency_partial() {
878 let coordinator = DomainCoordinator::new();
879 let responses = vec![
880 create_test_recommendation("web"),
881 create_test_recommendation("backend"),
882 ];
883
884 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
885 assert!(coordinator
887 .ensure_full_stack_consistency(&coordination)
888 .unwrap());
889 }
890
891 #[test]
892 fn test_ensure_full_stack_consistency_missing_web() {
893 let coordinator = DomainCoordinator::new();
894 let coordination = FullStackCoordination {
895 web_recommendations: vec![],
896 backend_recommendations: vec![create_test_recommendation("backend")],
897 devops_recommendations: vec![create_test_recommendation("devops")],
898 is_full_stack: true,
899 total_recommendations: 2,
900 };
901
902 assert!(coordinator
903 .ensure_full_stack_consistency(&coordination)
904 .is_err());
905 }
906
907 #[test]
908 fn test_detect_full_stack_conflicts_none() {
909 let coordinator = DomainCoordinator::new();
910 let responses = vec![
911 create_test_recommendation("web"),
912 create_test_recommendation("backend"),
913 create_test_recommendation("devops"),
914 ];
915
916 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
917 let conflicts = coordinator
918 .detect_full_stack_conflicts(&coordination)
919 .unwrap();
920
921 assert!(conflicts.is_empty());
922 }
923
924 #[test]
925 fn test_detect_full_stack_conflicts_incompatible_techs() {
926 let coordinator = DomainCoordinator::new();
927
928 let mut web_rec = create_test_recommendation("web");
929 web_rec.technologies = vec!["Webpack".to_string()];
930
931 let mut backend_rec = create_test_recommendation("backend");
932 backend_rec.technologies = vec!["Node.js".to_string()];
933
934 let mut devops_rec = create_test_recommendation("devops");
935 devops_rec.technologies = vec!["Vite".to_string()];
936
937 let coordination = FullStackCoordination {
938 web_recommendations: vec![web_rec],
939 backend_recommendations: vec![backend_rec],
940 devops_recommendations: vec![devops_rec],
941 is_full_stack: true,
942 total_recommendations: 3,
943 };
944
945 let conflicts = coordinator
946 .detect_full_stack_conflicts(&coordination)
947 .unwrap();
948
949 assert!(!conflicts.is_empty());
950 assert!(conflicts[0].contains("Incompatible"));
951 }
952
953 #[test]
954 fn test_full_stack_coordination_empty() {
955 let coordinator = DomainCoordinator::new();
956 let responses = vec![];
957
958 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
959
960 assert!(!coordination.is_full_stack);
961 assert_eq!(coordination.total_recommendations, 0);
962 }
963
964 #[test]
965 fn test_full_stack_coordination_single_domain() {
966 let coordinator = DomainCoordinator::new();
967 let responses = vec![create_test_recommendation("web")];
968
969 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
970
971 assert!(!coordination.is_full_stack);
972 assert_eq!(coordination.web_recommendations.len(), 1);
973 assert_eq!(coordination.backend_recommendations.len(), 0);
974 assert_eq!(coordination.devops_recommendations.len(), 0);
975 }
976
977 #[test]
978 fn test_full_stack_coordination_two_domains() {
979 let coordinator = DomainCoordinator::new();
980 let responses = vec![
981 create_test_recommendation("web"),
982 create_test_recommendation("backend"),
983 ];
984
985 let coordination = coordinator.coordinate_full_stack(responses).unwrap();
986
987 assert!(!coordination.is_full_stack);
988 assert_eq!(coordination.web_recommendations.len(), 1);
989 assert_eq!(coordination.backend_recommendations.len(), 1);
990 assert_eq!(coordination.devops_recommendations.len(), 0);
991 }
992}