1use crate::ai_studio::{
45 artifact_freezer::{ArtifactFreezer, FreezeMetadata, FreezeRequest},
46 config::DeterministicModeConfig,
47};
48use crate::intelligent_behavior::{
49 config::IntelligentBehaviorConfig,
50 llm_client::{LlmClient, LlmUsage},
51 types::LlmGenerationRequest,
52};
53use mockforge_foundation::Result;
54pub use mockforge_foundation::ai_studio_types::{
56 AppliedSystem, GeneratedSystem, SystemArtifact, SystemGenerationRequest, SystemMetadata,
57};
58use serde_json::Value;
59use sha2::{Digest, Sha256};
60use std::collections::HashMap;
61use uuid::Uuid;
62
63pub struct SystemGenerator {
65 llm_client: LlmClient,
67
68 config: IntelligentBehaviorConfig,
70
71 artifact_freezer: ArtifactFreezer,
73}
74
75impl SystemGenerator {
76 pub fn new(config: IntelligentBehaviorConfig) -> Self {
78 let llm_client = LlmClient::new(config.behavior_model.clone());
79 let artifact_freezer = ArtifactFreezer::new();
80 Self {
81 llm_client,
82 config,
83 artifact_freezer,
84 }
85 }
86
87 pub fn with_freeze_dir<P: AsRef<std::path::Path>>(
89 config: IntelligentBehaviorConfig,
90 freeze_dir: P,
91 ) -> Self {
92 let llm_client = LlmClient::new(config.behavior_model.clone());
93 let artifact_freezer = ArtifactFreezer::with_base_dir(freeze_dir);
94 Self {
95 llm_client,
96 config,
97 artifact_freezer,
98 }
99 }
100
101 pub async fn generate(
105 &self,
106 request: &SystemGenerationRequest,
107 deterministic_config: Option<&DeterministicModeConfig>,
108 ) -> Result<GeneratedSystem> {
109 let system_id = request
111 .system_id
112 .clone()
113 .unwrap_or_else(|| format!("system-{}", Uuid::new_v4()));
114
115 let version = "v1".to_string();
118
119 let system_prompt = self.build_system_prompt();
121 let user_prompt = self.build_user_prompt(request)?;
122
123 let llm_request = LlmGenerationRequest {
125 system_prompt,
126 user_prompt,
127 temperature: 0.7, max_tokens: 8000, schema: None,
130 seed: None,
131 };
132
133 let (response_json, usage) = self.llm_client.generate_with_usage(&llm_request).await?;
134
135 let artifacts = self.parse_system_response(response_json, &request.output_formats)?;
137
138 let metadata = self.extract_metadata(request, &artifacts)?;
140
141 let cost_usd = self.estimate_cost(&usage);
143
144 let should_auto_freeze = deterministic_config
146 .map(|cfg| cfg.enabled && cfg.is_auto_freeze_enabled())
147 .unwrap_or(false);
148
149 let status = if should_auto_freeze {
150 self.freeze_system_artifacts(&system_id, &version, &artifacts, deterministic_config)
152 .await?;
153 "frozen".to_string()
154 } else {
155 "draft".to_string()
156 };
157
158 Ok(GeneratedSystem {
159 system_id,
160 version,
161 artifacts,
162 workspace_id: request.workspace_id.clone(),
163 status,
164 tokens_used: Some(usage.total_tokens),
165 cost_usd: Some(cost_usd),
166 metadata,
167 })
168 }
169
170 pub async fn freeze_system_artifacts(
172 &self,
173 system_id: &str,
174 version: &str,
175 artifacts: &HashMap<String, SystemArtifact>,
176 _deterministic_config: Option<&DeterministicModeConfig>,
177 ) -> Result<Vec<String>> {
178 let mut frozen_ids = Vec::new();
179
180 for (artifact_type, artifact) in artifacts {
181 let freeze_request = FreezeRequest {
182 artifact_type: format!("system_{}", artifact_type),
183 content: artifact.content.clone(),
184 format: artifact.format.clone(),
185 path: Some(format!(
186 "{}/{}_{}_{}.{}",
187 self.artifact_freezer.base_dir().display(),
188 system_id,
189 version,
190 artifact_type,
191 artifact.format
192 )),
193 metadata: Some(FreezeMetadata {
194 llm_provider: Some(self.config.behavior_model.llm_provider.clone()),
195 llm_model: Some(self.config.behavior_model.model.clone()),
196 llm_version: None,
197 prompt_hash: Some(self.hash_description(artifact_type)),
198 output_hash: None,
199 original_prompt: None,
200 }),
201 };
202
203 let frozen = self.artifact_freezer.freeze(&freeze_request).await?;
204 frozen_ids.push(frozen.path);
205 }
206
207 Ok(frozen_ids)
208 }
209
210 pub async fn apply_system_design(
216 &self,
217 system: &GeneratedSystem,
218 deterministic_config: Option<&DeterministicModeConfig>,
219 artifact_ids: Option<Vec<String>>,
220 ) -> Result<AppliedSystem> {
221 if system.status == "frozen" {
223 return Ok(AppliedSystem {
224 system_id: system.system_id.clone(),
225 version: system.version.clone(),
226 applied_artifacts: system.artifacts.keys().cloned().collect(),
227 frozen: true,
228 });
229 }
230
231 let should_freeze = deterministic_config
233 .map(|cfg| cfg.enabled && cfg.is_auto_freeze_enabled())
234 .unwrap_or(false);
235
236 let artifacts_to_apply = if let Some(ids) = artifact_ids {
238 system
239 .artifacts
240 .iter()
241 .filter(|(_, artifact)| ids.contains(&artifact.artifact_id))
242 .map(|(k, v)| (k.clone(), v.clone()))
243 .collect()
244 } else {
245 system.artifacts.clone()
246 };
247
248 if should_freeze {
249 let frozen_paths = self
250 .freeze_system_artifacts(
251 &system.system_id,
252 &system.version,
253 &artifacts_to_apply,
254 deterministic_config,
255 )
256 .await?;
257
258 Ok(AppliedSystem {
259 system_id: system.system_id.clone(),
260 version: system.version.clone(),
261 applied_artifacts: artifacts_to_apply.keys().cloned().collect(),
262 frozen: !frozen_paths.is_empty(),
263 })
264 } else {
265 Ok(AppliedSystem {
267 system_id: system.system_id.clone(),
268 version: system.version.clone(),
269 applied_artifacts: artifacts_to_apply.keys().cloned().collect(),
270 frozen: false,
271 })
272 }
273 }
274
275 pub async fn freeze_artifacts(
277 &self,
278 system: &GeneratedSystem,
279 artifact_ids: Vec<String>,
280 ) -> Result<Vec<String>> {
281 let artifacts_to_freeze: HashMap<String, SystemArtifact> = system
282 .artifacts
283 .iter()
284 .filter(|(_, artifact)| artifact_ids.contains(&artifact.artifact_id))
285 .map(|(k, v)| (k.clone(), v.clone()))
286 .collect();
287
288 self.freeze_system_artifacts(&system.system_id, &system.version, &artifacts_to_freeze, None)
289 .await
290 }
291
292 fn hash_description(&self, artifact_type: &str) -> String {
294 let mut hasher = Sha256::new();
295 hasher.update(artifact_type.as_bytes());
296 format!("{:x}", hasher.finalize())
297 }
298
299 fn build_system_prompt(&self) -> String {
301 r#"You are an expert backend architect and system designer. Your task is to generate complete backend systems from natural language descriptions.
302
303Generate comprehensive backend systems including:
304
3051. **OpenAPI Specification** (20-30 REST endpoints)
306 - Full CRUD operations for all entities
307 - Realistic request/response schemas
308 - Proper HTTP methods and status codes
309 - Authentication and authorization where appropriate
310
3112. **Personas** (4-5 personas based on entity roles)
312 - Each persona should have realistic traits, goals, and behaviors
313 - Personas should match the roles mentioned in the description
314
3153. **Lifecycle States** (6-10 states for main entities)
316 - State machines for key entities (e.g., trip: requested → matched → in_progress → completed)
317 - State transitions with realistic conditions
318
3194. **WebSocket Topics** (if real-time features mentioned)
320 - Topic names and event schemas
321 - Event types and payloads
322
3235. **Chaos/Failure Scenarios** (if applicable)
324 - Payment failure scenarios
325 - Network error scenarios
326 - Surge pricing profiles (if pricing mentioned)
327
3286. **CI/CD Templates** (optional)
329 - GitHub Actions workflows
330 - GitLab CI configurations
331
3327. **GraphQL Schema** (optional, if requested)
333 - Type definitions
334 - Queries and mutations
335
3368. **TypeScript Typings** (optional)
337 - Type definitions from OpenAPI schema
338
339Return your generation as a JSON object with the following structure:
340{
341 "openapi": { ... OpenAPI 3.1 specification ... },
342 "personas": [
343 {
344 "name": "persona_name",
345 "traits": { ... },
346 "goals": [...],
347 "behaviors": [...]
348 }
349 ],
350 "lifecycles": [
351 {
352 "entity": "entity_name",
353 "states": ["state1", "state2", ...],
354 "transitions": [
355 {
356 "from": "state1",
357 "to": "state2",
358 "condition": "..."
359 }
360 ]
361 }
362 ],
363 "websocket_topics": [
364 {
365 "topic": "topic_name",
366 "event_types": [...],
367 "schema": { ... }
368 }
369 ],
370 "chaos_profiles": [
371 {
372 "name": "profile_name",
373 "type": "payment_failure|surge_pricing|network_error",
374 "config": { ... }
375 }
376 ],
377 "ci_templates": {
378 "github_actions": "...",
379 "gitlab_ci": "..."
380 },
381 "graphql": "... GraphQL SDL ...",
382 "typings": {
383 "typescript": "...",
384 "go": "...",
385 "rust": "..."
386 },
387 "metadata": {
388 "entities": ["entity1", "entity2", ...],
389 "relationships": ["entity1 -> entity2", ...],
390 "operations": ["create", "read", "update", "delete", ...]
391 }
392}
393
394Be thorough and generate realistic, production-ready artifacts. Ensure all artifacts are coherent (personas match endpoints, lifecycles match entities)."#
395 .to_string()
396 }
397
398 fn build_user_prompt(&self, request: &SystemGenerationRequest) -> Result<String> {
400 let formats_text = if request.output_formats.is_empty() {
401 "all available formats".to_string()
402 } else {
403 request.output_formats.join(", ")
404 };
405
406 Ok(format!(
407 r#"Generate a complete backend system from this description:
408
409Description:
410{}
411
412Please generate the following formats: {}
413
414Make sure to:
4151. Extract all entities, relationships, and operations from the description
4162. Generate realistic and comprehensive artifacts
4173. Ensure coherence across all artifacts (personas match endpoints, lifecycles match entities)
4184. Include proper error handling and edge cases
4195. Make it production-ready
420
421Provide a complete system that can bootstrap a startup backend."#,
422 request.description, formats_text
423 ))
424 }
425
426 fn parse_system_response(
428 &self,
429 response: Value,
430 requested_formats: &[String],
431 ) -> Result<HashMap<String, SystemArtifact>> {
432 let mut artifacts = HashMap::new();
433
434 if requested_formats.is_empty() || requested_formats.contains(&"openapi".to_string()) {
436 if let Some(openapi) = response.get("openapi") {
437 let artifact_id = format!("openapi-{}", Uuid::new_v4());
438 artifacts.insert(
439 "openapi".to_string(),
440 SystemArtifact {
441 artifact_type: "openapi".to_string(),
442 content: openapi.clone(),
443 format: "json".to_string(),
444 artifact_id,
445 },
446 );
447 }
448 }
449
450 if requested_formats.is_empty() || requested_formats.contains(&"personas".to_string()) {
452 if let Some(personas) = response.get("personas") {
453 let artifact_id = format!("personas-{}", Uuid::new_v4());
454 artifacts.insert(
455 "personas".to_string(),
456 SystemArtifact {
457 artifact_type: "personas".to_string(),
458 content: personas.clone(),
459 format: "json".to_string(),
460 artifact_id,
461 },
462 );
463 }
464 }
465
466 if requested_formats.is_empty() || requested_formats.contains(&"lifecycles".to_string()) {
468 if let Some(lifecycles) = response.get("lifecycles") {
469 let artifact_id = format!("lifecycles-{}", Uuid::new_v4());
470 artifacts.insert(
471 "lifecycles".to_string(),
472 SystemArtifact {
473 artifact_type: "lifecycles".to_string(),
474 content: lifecycles.clone(),
475 format: "json".to_string(),
476 artifact_id,
477 },
478 );
479 }
480 }
481
482 if requested_formats.contains(&"websocket".to_string()) {
484 if let Some(websocket) = response.get("websocket_topics") {
485 let artifact_id = format!("websocket-{}", Uuid::new_v4());
486 artifacts.insert(
487 "websocket".to_string(),
488 SystemArtifact {
489 artifact_type: "websocket".to_string(),
490 content: websocket.clone(),
491 format: "json".to_string(),
492 artifact_id,
493 },
494 );
495 }
496 }
497
498 if requested_formats.contains(&"chaos".to_string()) {
500 if let Some(chaos) = response.get("chaos_profiles") {
501 let artifact_id = format!("chaos-{}", Uuid::new_v4());
502 artifacts.insert(
503 "chaos".to_string(),
504 SystemArtifact {
505 artifact_type: "chaos".to_string(),
506 content: chaos.clone(),
507 format: "json".to_string(),
508 artifact_id,
509 },
510 );
511 }
512 }
513
514 if requested_formats.contains(&"ci".to_string()) {
516 if let Some(ci) = response.get("ci_templates") {
517 let artifact_id = format!("ci-{}", Uuid::new_v4());
518 artifacts.insert(
519 "ci".to_string(),
520 SystemArtifact {
521 artifact_type: "ci".to_string(),
522 content: ci.clone(),
523 format: "yaml".to_string(),
524 artifact_id,
525 },
526 );
527 }
528 }
529
530 if requested_formats.contains(&"graphql".to_string()) {
532 if let Some(graphql) = response.get("graphql") {
533 let artifact_id = format!("graphql-{}", Uuid::new_v4());
534 artifacts.insert(
535 "graphql".to_string(),
536 SystemArtifact {
537 artifact_type: "graphql".to_string(),
538 content: graphql.clone(),
539 format: "graphql".to_string(),
540 artifact_id,
541 },
542 );
543 }
544 }
545
546 if requested_formats.contains(&"typings".to_string()) {
548 if let Some(typings) = response.get("typings") {
549 let artifact_id = format!("typings-{}", Uuid::new_v4());
550 artifacts.insert(
551 "typings".to_string(),
552 SystemArtifact {
553 artifact_type: "typings".to_string(),
554 content: typings.clone(),
555 format: "json".to_string(),
556 artifact_id,
557 },
558 );
559 }
560 }
561
562 Ok(artifacts)
563 }
564
565 fn extract_metadata(
567 &self,
568 request: &SystemGenerationRequest,
569 _artifacts: &HashMap<String, SystemArtifact>,
570 ) -> Result<SystemMetadata> {
571 let entities = self.extract_entities(&request.description);
574 let relationships = self.extract_relationships(&request.description);
575 let operations = vec![
576 "create".to_string(),
577 "read".to_string(),
578 "update".to_string(),
579 "delete".to_string(),
580 ];
581
582 Ok(SystemMetadata {
583 description: request.description.clone(),
584 entities,
585 relationships,
586 operations,
587 generated_at: chrono::Utc::now().to_rfc3339(),
588 })
589 }
590
591 fn extract_entities(&self, description: &str) -> Vec<String> {
593 let mut entities = Vec::new();
595 let words: Vec<&str> = description.split_whitespace().collect();
596
597 for word in words {
599 if word.ends_with('s') && word.len() > 3 {
600 let singular = word.trim_end_matches('s');
601 if !entities.contains(&singular.to_string()) {
602 entities.push(singular.to_string());
603 }
604 }
605 }
606
607 entities
608 }
609
610 fn extract_relationships(&self, description: &str) -> Vec<String> {
612 let mut relationships = Vec::new();
614 let entities = self.extract_entities(description);
615
616 for i in 0..entities.len() {
618 for j in (i + 1)..entities.len() {
619 relationships.push(format!("{} -> {}", entities[i], entities[j]));
620 }
621 }
622
623 relationships
624 }
625
626 fn estimate_cost(&self, usage: &LlmUsage) -> f64 {
628 let cost_per_1k_tokens =
630 match self.config.behavior_model.llm_provider.to_lowercase().as_str() {
631 "openai" => match self.config.behavior_model.model.to_lowercase().as_str() {
632 model if model.contains("gpt-4") => 0.03,
633 model if model.contains("gpt-3.5") => 0.002,
634 _ => 0.002,
635 },
636 "anthropic" => 0.008,
637 "ollama" => 0.0, _ => 0.002,
639 };
640
641 (usage.total_tokens as f64 / 1000.0) * cost_per_1k_tokens
642 }
643}
644
645#[cfg(test)]
646mod tests {
647 use super::*;
648 use crate::intelligent_behavior::config::BehaviorModelConfig;
649
650 fn create_test_config() -> IntelligentBehaviorConfig {
651 IntelligentBehaviorConfig {
652 behavior_model: BehaviorModelConfig {
653 llm_provider: "ollama".to_string(),
654 model: "llama2".to_string(),
655 api_endpoint: Some("http://localhost:11434/api/chat".to_string()),
656 api_key: None,
657 temperature: 0.7,
658 max_tokens: 2000,
659 rules: crate::intelligent_behavior::types::BehaviorRules::default(),
660 seed: None,
661 },
662 ..Default::default()
663 }
664 }
665
666 #[test]
667 fn test_system_generation_request_serialization() {
668 let request = SystemGenerationRequest {
669 description: "Ride-sharing app".to_string(),
670 output_formats: vec!["openapi".to_string(), "personas".to_string()],
671 workspace_id: None,
672 system_id: None,
673 };
674
675 let json = serde_json::to_string(&request).unwrap();
676 assert!(json.contains("Ride-sharing"));
677 assert!(json.contains("openapi"));
678 }
679
680 #[test]
681 fn test_entity_extraction() {
682 let config = create_test_config();
683 let generator = SystemGenerator::new(config);
684 let entities = generator.extract_entities(
685 "I'm building a ride-sharing app with drivers, riders, trips, payments",
686 );
687 assert!(!entities.is_empty());
688 }
689
690 #[test]
691 fn test_system_generation_request_creation() {
692 let request = SystemGenerationRequest {
693 description: "Test system".to_string(),
694 output_formats: vec!["openapi".to_string()],
695 workspace_id: Some("workspace-123".to_string()),
696 system_id: Some("system-456".to_string()),
697 };
698
699 assert_eq!(request.description, "Test system");
700 assert_eq!(request.output_formats.len(), 1);
701 assert_eq!(request.workspace_id, Some("workspace-123".to_string()));
702 assert_eq!(request.system_id, Some("system-456".to_string()));
703 }
704
705 #[test]
706 fn test_system_generation_request_default_output_formats() {
707 let request = SystemGenerationRequest {
708 description: "Test".to_string(),
709 output_formats: vec![],
710 workspace_id: None,
711 system_id: None,
712 };
713
714 assert!(request.output_formats.is_empty());
715 }
716
717 #[test]
718 fn test_generated_system_creation() {
719 let mut artifacts = HashMap::new();
720 artifacts.insert(
721 "openapi".to_string(),
722 SystemArtifact {
723 artifact_type: "openapi".to_string(),
724 content: serde_json::json!({"openapi": "3.0.0"}),
725 format: "json".to_string(),
726 artifact_id: "artifact-1".to_string(),
727 },
728 );
729
730 let system = GeneratedSystem {
731 system_id: "system-123".to_string(),
732 version: "v1".to_string(),
733 artifacts,
734 workspace_id: Some("workspace-456".to_string()),
735 status: "draft".to_string(),
736 tokens_used: Some(1000),
737 cost_usd: Some(0.01),
738 metadata: SystemMetadata {
739 description: "Test system".to_string(),
740 entities: vec!["User".to_string()],
741 relationships: vec![],
742 operations: vec![],
743 generated_at: "2024-01-01T00:00:00Z".to_string(),
744 },
745 };
746
747 assert_eq!(system.system_id, "system-123");
748 assert_eq!(system.version, "v1");
749 assert_eq!(system.artifacts.len(), 1);
750 assert_eq!(system.status, "draft");
751 }
752
753 #[test]
754 fn test_applied_system_creation() {
755 let applied = AppliedSystem {
756 system_id: "system-123".to_string(),
757 version: "v1".to_string(),
758 applied_artifacts: vec!["artifact-1".to_string(), "artifact-2".to_string()],
759 frozen: true,
760 };
761
762 assert_eq!(applied.system_id, "system-123");
763 assert_eq!(applied.version, "v1");
764 assert_eq!(applied.applied_artifacts.len(), 2);
765 assert!(applied.frozen);
766 }
767
768 #[test]
769 fn test_system_artifact_creation() {
770 let artifact = SystemArtifact {
771 artifact_type: "openapi".to_string(),
772 content: serde_json::json!({"openapi": "3.0.0", "info": {"title": "API"}}),
773 format: "yaml".to_string(),
774 artifact_id: "artifact-123".to_string(),
775 };
776
777 assert_eq!(artifact.artifact_type, "openapi");
778 assert_eq!(artifact.format, "yaml");
779 assert_eq!(artifact.artifact_id, "artifact-123");
780 }
781
782 #[test]
783 fn test_system_metadata_creation() {
784 let metadata = SystemMetadata {
785 description: "Ride-sharing app".to_string(),
786 entities: vec![
787 "Driver".to_string(),
788 "Rider".to_string(),
789 "Trip".to_string(),
790 ],
791 relationships: vec!["Driver has many Trips".to_string()],
792 operations: vec!["create_trip".to_string(), "update_trip".to_string()],
793 generated_at: "2024-01-01T00:00:00Z".to_string(),
794 };
795
796 assert_eq!(metadata.description, "Ride-sharing app");
797 assert_eq!(metadata.entities.len(), 3);
798 assert_eq!(metadata.relationships.len(), 1);
799 assert_eq!(metadata.operations.len(), 2);
800 }
801
802 #[test]
803 fn test_system_generator_new() {
804 let config = create_test_config();
805 let generator = SystemGenerator::new(config);
806 let _ = generator;
808 }
809
810 #[test]
811 fn test_system_generator_with_freeze_dir() {
812 let config = create_test_config();
813 let generator = SystemGenerator::with_freeze_dir(config, "/tmp/freeze");
814 let _ = generator;
816 }
817
818 #[test]
819 fn test_system_generation_request_clone() {
820 let request1 = SystemGenerationRequest {
821 description: "Test system".to_string(),
822 output_formats: vec!["openapi".to_string()],
823 workspace_id: Some("workspace-123".to_string()),
824 system_id: Some("system-456".to_string()),
825 };
826 let request2 = request1.clone();
827 assert_eq!(request1.description, request2.description);
828 assert_eq!(request1.output_formats, request2.output_formats);
829 }
830
831 #[test]
832 fn test_system_generation_request_debug() {
833 let request = SystemGenerationRequest {
834 description: "Test".to_string(),
835 output_formats: vec![],
836 workspace_id: None,
837 system_id: None,
838 };
839 let debug_str = format!("{:?}", request);
840 assert!(debug_str.contains("SystemGenerationRequest"));
841 }
842
843 #[test]
844 fn test_generated_system_clone() {
845 let system1 = GeneratedSystem {
846 system_id: "system-123".to_string(),
847 version: "v1".to_string(),
848 artifacts: HashMap::new(),
849 workspace_id: None,
850 status: "draft".to_string(),
851 tokens_used: None,
852 cost_usd: None,
853 metadata: SystemMetadata {
854 description: "Test".to_string(),
855 entities: vec![],
856 relationships: vec![],
857 operations: vec![],
858 generated_at: "2024-01-01T00:00:00Z".to_string(),
859 },
860 };
861 let system2 = system1.clone();
862 assert_eq!(system1.system_id, system2.system_id);
863 assert_eq!(system1.version, system2.version);
864 }
865
866 #[test]
867 fn test_generated_system_debug() {
868 let system = GeneratedSystem {
869 system_id: "system-123".to_string(),
870 version: "v1".to_string(),
871 artifacts: HashMap::new(),
872 workspace_id: None,
873 status: "draft".to_string(),
874 tokens_used: None,
875 cost_usd: None,
876 metadata: SystemMetadata {
877 description: "Test".to_string(),
878 entities: vec![],
879 relationships: vec![],
880 operations: vec![],
881 generated_at: "2024-01-01T00:00:00Z".to_string(),
882 },
883 };
884 let debug_str = format!("{:?}", system);
885 assert!(debug_str.contains("GeneratedSystem"));
886 }
887
888 #[test]
889 fn test_applied_system_clone() {
890 let applied1 = AppliedSystem {
891 system_id: "system-123".to_string(),
892 version: "v1".to_string(),
893 applied_artifacts: vec!["artifact-1".to_string()],
894 frozen: true,
895 };
896 let applied2 = applied1.clone();
897 assert_eq!(applied1.system_id, applied2.system_id);
898 assert_eq!(applied1.frozen, applied2.frozen);
899 }
900
901 #[test]
902 fn test_applied_system_debug() {
903 let applied = AppliedSystem {
904 system_id: "system-123".to_string(),
905 version: "v1".to_string(),
906 applied_artifacts: vec![],
907 frozen: false,
908 };
909 let debug_str = format!("{:?}", applied);
910 assert!(debug_str.contains("AppliedSystem"));
911 }
912
913 #[test]
914 fn test_system_artifact_clone() {
915 let artifact1 = SystemArtifact {
916 artifact_type: "openapi".to_string(),
917 content: serde_json::json!({}),
918 format: "json".to_string(),
919 artifact_id: "artifact-1".to_string(),
920 };
921 let artifact2 = artifact1.clone();
922 assert_eq!(artifact1.artifact_type, artifact2.artifact_type);
923 assert_eq!(artifact1.artifact_id, artifact2.artifact_id);
924 }
925
926 #[test]
927 fn test_system_artifact_debug() {
928 let artifact = SystemArtifact {
929 artifact_type: "openapi".to_string(),
930 content: serde_json::json!({}),
931 format: "json".to_string(),
932 artifact_id: "artifact-1".to_string(),
933 };
934 let debug_str = format!("{:?}", artifact);
935 assert!(debug_str.contains("SystemArtifact"));
936 }
937
938 #[test]
939 fn test_system_metadata_clone() {
940 let metadata1 = SystemMetadata {
941 description: "Test".to_string(),
942 entities: vec!["User".to_string()],
943 relationships: vec![],
944 operations: vec![],
945 generated_at: "2024-01-01T00:00:00Z".to_string(),
946 };
947 let metadata2 = metadata1.clone();
948 assert_eq!(metadata1.description, metadata2.description);
949 assert_eq!(metadata1.entities, metadata2.entities);
950 }
951
952 #[test]
953 fn test_system_metadata_debug() {
954 let metadata = SystemMetadata {
955 description: "Test".to_string(),
956 entities: vec![],
957 relationships: vec![],
958 operations: vec![],
959 generated_at: "2024-01-01T00:00:00Z".to_string(),
960 };
961 let debug_str = format!("{:?}", metadata);
962 assert!(debug_str.contains("SystemMetadata"));
963 }
964
965 #[test]
966 fn test_system_generation_request_with_all_fields() {
967 let request = SystemGenerationRequest {
968 description: "Complete e-commerce system".to_string(),
969 output_formats: vec![
970 "openapi".to_string(),
971 "graphql".to_string(),
972 "personas".to_string(),
973 "lifecycles".to_string(),
974 ],
975 workspace_id: Some("workspace-789".to_string()),
976 system_id: Some("system-999".to_string()),
977 };
978 assert_eq!(request.output_formats.len(), 4);
979 assert!(request.output_formats.contains(&"openapi".to_string()));
980 assert!(request.output_formats.contains(&"graphql".to_string()));
981 }
982
983 #[test]
984 fn test_generated_system_with_all_fields() {
985 let mut artifacts = HashMap::new();
986 artifacts.insert(
987 "openapi".to_string(),
988 SystemArtifact {
989 artifact_type: "openapi".to_string(),
990 content: serde_json::json!({"openapi": "3.0.0"}),
991 format: "json".to_string(),
992 artifact_id: "artifact-1".to_string(),
993 },
994 );
995 artifacts.insert(
996 "personas".to_string(),
997 SystemArtifact {
998 artifact_type: "personas".to_string(),
999 content: serde_json::json!({"personas": []}),
1000 format: "json".to_string(),
1001 artifact_id: "artifact-2".to_string(),
1002 },
1003 );
1004
1005 let system = GeneratedSystem {
1006 system_id: "system-123".to_string(),
1007 version: "v2".to_string(),
1008 artifacts: artifacts.clone(),
1009 workspace_id: Some("workspace-456".to_string()),
1010 status: "frozen".to_string(),
1011 tokens_used: Some(5000),
1012 cost_usd: Some(0.05),
1013 metadata: SystemMetadata {
1014 description: "Ride-sharing app".to_string(),
1015 entities: vec!["Driver".to_string(), "Rider".to_string()],
1016 relationships: vec!["Driver-Trip".to_string()],
1017 operations: vec!["create_trip".to_string()],
1018 generated_at: "2024-01-01T00:00:00Z".to_string(),
1019 },
1020 };
1021
1022 assert_eq!(system.artifacts.len(), 2);
1023 assert_eq!(system.version, "v2");
1024 assert_eq!(system.status, "frozen");
1025 assert_eq!(system.tokens_used, Some(5000));
1026 assert_eq!(system.cost_usd, Some(0.05));
1027 }
1028
1029 #[test]
1030 fn test_applied_system_with_multiple_artifacts() {
1031 let applied = AppliedSystem {
1032 system_id: "system-123".to_string(),
1033 version: "v1".to_string(),
1034 applied_artifacts: vec![
1035 "artifact-1".to_string(),
1036 "artifact-2".to_string(),
1037 "artifact-3".to_string(),
1038 ],
1039 frozen: true,
1040 };
1041 assert_eq!(applied.applied_artifacts.len(), 3);
1042 assert!(applied.frozen);
1043 }
1044
1045 #[test]
1046 fn test_system_artifact_with_yaml_format() {
1047 let artifact = SystemArtifact {
1048 artifact_type: "openapi".to_string(),
1049 content: serde_json::json!({"openapi": "3.0.0"}),
1050 format: "yaml".to_string(),
1051 artifact_id: "artifact-yaml".to_string(),
1052 };
1053 assert_eq!(artifact.format, "yaml");
1054 }
1055
1056 #[test]
1057 fn test_system_metadata_with_all_fields() {
1058 let metadata = SystemMetadata {
1059 description: "Complete system description".to_string(),
1060 entities: vec![
1061 "User".to_string(),
1062 "Order".to_string(),
1063 "Product".to_string(),
1064 ],
1065 relationships: vec!["User-Order".to_string(), "Order-Product".to_string()],
1066 operations: vec![
1067 "GET /users".to_string(),
1068 "POST /orders".to_string(),
1069 "PUT /products".to_string(),
1070 ],
1071 generated_at: "2024-01-01T12:00:00Z".to_string(),
1072 };
1073 assert_eq!(metadata.entities.len(), 3);
1074 assert_eq!(metadata.relationships.len(), 2);
1075 assert_eq!(metadata.operations.len(), 3);
1076 }
1077}