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