1use crate::consistency::ConsistencyStore;
8use crate::domains::Domain;
9use crate::faker::EnhancedFaker;
10use crate::persona::PersonaRegistry;
11use crate::persona_backstory::BackstoryGenerator;
12use crate::persona_templates::PersonaTemplateRegistry;
13use crate::schema::{FieldDefinition, SchemaDefinition};
14use crate::{Error, Result};
15use serde_json::{json, Value};
16use std::collections::HashMap;
17use std::sync::Arc;
18use tracing::{debug, info, warn};
19
20#[derive(Debug, Clone)]
22pub struct MockGeneratorConfig {
23 pub realistic_mode: bool,
25 pub default_array_size: usize,
27 pub max_array_size: usize,
29 pub include_optional_fields: bool,
31 pub field_mappings: HashMap<String, String>,
33 pub validate_generated_data: bool,
35 pub enable_backstories: bool,
37}
38
39impl Default for MockGeneratorConfig {
40 fn default() -> Self {
41 Self {
42 realistic_mode: true,
43 default_array_size: 3,
44 max_array_size: 10,
45 include_optional_fields: true,
46 field_mappings: HashMap::new(),
47 validate_generated_data: true,
48 enable_backstories: false,
49 }
50 }
51}
52
53impl MockGeneratorConfig {
54 pub fn new() -> Self {
56 Self::default()
57 }
58
59 pub fn realistic_mode(mut self, enabled: bool) -> Self {
61 self.realistic_mode = enabled;
62 self
63 }
64
65 pub fn default_array_size(mut self, size: usize) -> Self {
67 self.default_array_size = size;
68 self
69 }
70
71 pub fn max_array_size(mut self, size: usize) -> Self {
73 self.max_array_size = size;
74 self
75 }
76
77 pub fn include_optional_fields(mut self, include: bool) -> Self {
79 self.include_optional_fields = include;
80 self
81 }
82
83 pub fn field_mapping(mut self, field_name: String, faker_type: String) -> Self {
85 self.field_mappings.insert(field_name, faker_type);
86 self
87 }
88
89 pub fn validate_generated_data(mut self, validate: bool) -> Self {
91 self.validate_generated_data = validate;
92 self
93 }
94
95 pub fn enable_backstories(mut self, enable: bool) -> Self {
97 self.enable_backstories = enable;
98 self
99 }
100}
101
102#[derive(Debug)]
104pub struct MockDataGenerator {
105 config: MockGeneratorConfig,
107 faker: EnhancedFaker,
109 #[allow(dead_code)]
111 schema_registry: HashMap<String, SchemaDefinition>,
112 field_patterns: HashMap<String, String>,
114 persona_registry: Option<Arc<PersonaRegistry>>,
116 consistency_store: Option<Arc<ConsistencyStore>>,
118 active_domain: Option<Domain>,
120}
121
122impl MockDataGenerator {
123 pub fn new() -> Self {
125 Self::with_config(MockGeneratorConfig::new())
126 }
127
128 pub fn with_config(config: MockGeneratorConfig) -> Self {
130 let mut generator = Self {
131 config,
132 faker: EnhancedFaker::new(),
133 schema_registry: HashMap::new(),
134 field_patterns: Self::create_field_patterns(),
135 persona_registry: None,
136 consistency_store: None,
137 active_domain: None,
138 };
139
140 generator.initialize_common_schemas();
142 generator
143 }
144
145 pub fn with_persona_support(config: MockGeneratorConfig, domain: Option<Domain>) -> Self {
147 let persona_registry = Arc::new(PersonaRegistry::new());
148 let consistency_store =
149 Arc::new(ConsistencyStore::with_registry_and_domain(persona_registry.clone(), domain));
150
151 let mut generator = Self {
152 config,
153 faker: EnhancedFaker::new(),
154 schema_registry: HashMap::new(),
155 field_patterns: Self::create_field_patterns(),
156 persona_registry: Some(persona_registry),
157 consistency_store: Some(consistency_store),
158 active_domain: domain,
159 };
160
161 generator.initialize_common_schemas();
163 generator
164 }
165
166 pub fn set_active_domain(&mut self, domain: Option<Domain>) {
168 self.active_domain = domain;
169 }
172
173 pub fn persona_registry(&self) -> Option<&Arc<PersonaRegistry>> {
175 self.persona_registry.as_ref()
176 }
177
178 pub fn consistency_store(&self) -> Option<&Arc<ConsistencyStore>> {
180 self.consistency_store.as_ref()
181 }
182
183 pub fn generate_from_openapi_spec(&mut self, spec: &Value) -> Result<MockDataResult> {
185 info!("Generating mock data from OpenAPI specification");
186
187 let openapi_spec = self.parse_openapi_spec(spec)?;
189
190 let schemas = self.extract_schemas_from_spec(spec)?;
192
193 let mut generated_data = HashMap::new();
195 let mut warnings = Vec::new();
196
197 for (schema_name, schema_def) in schemas {
198 debug!("Generating data for schema: {}", schema_name);
199
200 match self.generate_schema_data(&schema_def) {
201 Ok(data) => {
202 generated_data.insert(schema_name, data);
203 }
204 Err(e) => {
205 let warning =
206 format!("Failed to generate data for schema '{}': {}", schema_name, e);
207 warn!("{}", warning);
208 warnings.push(warning);
209 }
210 }
211 }
212
213 let mut mock_responses = HashMap::new();
216 if let Some(paths) = spec.get("paths") {
217 if let Some(paths_obj) = paths.as_object() {
218 for (path, path_item) in paths_obj {
219 if let Some(path_obj) = path_item.as_object() {
220 for (method, operation) in path_obj {
221 if let Some(op_obj) = operation.as_object() {
222 let endpoint_key = format!("{} {}", method.to_uppercase(), path);
223
224 if let Some(responses) = op_obj.get("responses") {
226 if let Some(resp_obj) = responses.as_object() {
227 let mut response_schema = None;
229
230 if let Some(response) = resp_obj.get("200") {
232 response_schema = self
233 .extract_response_schema_from_json(response)
234 .ok()
235 .flatten();
236 }
237
238 if response_schema.is_none() {
240 if let Some(response) = resp_obj.get("201") {
241 response_schema = self
242 .extract_response_schema_from_json(response)
243 .ok()
244 .flatten();
245 }
246 }
247
248 if response_schema.is_none() {
250 for (status_code, response) in resp_obj {
251 if let Ok(code) = status_code.parse::<u16>() {
252 if (200..300).contains(&code) {
253 if let Some(schema) = self
254 .extract_response_schema_from_json(
255 response,
256 )
257 .ok()
258 .flatten()
259 {
260 response_schema = Some(schema);
261 break;
262 }
263 }
264 }
265 }
266 }
267
268 if let Some(schema) = response_schema {
270 let resolved_schema = if let Some(ref_path) =
272 schema.get("$ref").and_then(|r| r.as_str())
273 {
274 self.resolve_schema_ref(spec, ref_path)?
275 } else {
276 Some(schema)
277 };
278
279 if let Some(resolved) = resolved_schema {
280 if let Ok(mock_data) =
281 self.generate_from_json_schema(&resolved)
282 {
283 mock_responses.insert(
284 endpoint_key,
285 MockResponse {
286 status: 200,
287 headers: HashMap::new(),
288 body: mock_data,
289 },
290 );
291 }
292 }
293 }
294 }
295 }
296 }
297 }
298 }
299 }
300 }
301 }
302
303 Ok(MockDataResult {
304 schemas: generated_data,
305 responses: mock_responses,
306 warnings,
307 spec_info: openapi_spec.info,
308 })
309 }
310
311 pub fn generate_from_json_schema(&mut self, schema: &Value) -> Result<Value> {
313 debug!("Generating mock data from JSON Schema");
314
315 let schema_def = SchemaDefinition::from_json_schema(schema)?;
317
318 self.generate_schema_data(&schema_def)
320 }
321
322 fn generate_schema_data(&mut self, schema: &SchemaDefinition) -> Result<Value> {
324 let mut object = serde_json::Map::new();
325
326 for field in &schema.fields {
327 if !field.required && !self.config.include_optional_fields {
329 continue;
330 }
331
332 let faker_type = self.determine_faker_type(field);
334
335 let value = self.generate_field_value(field, &faker_type)?;
337
338 if self.config.validate_generated_data {
340 field.validate_value(&value)?;
341 }
342
343 object.insert(field.name.clone(), value);
344 }
345
346 Ok(Value::Object(object))
347 }
348
349 #[allow(dead_code)]
351 fn generate_endpoint_response(
352 &mut self,
353 operation: &openapiv3::Operation,
354 ) -> Result<Option<MockResponse>> {
355 let response_schema = self.find_best_response_schema(operation)?;
357
358 if let Some(schema) = response_schema {
359 let mock_data = self.generate_from_json_schema(&schema)?;
360
361 Ok(Some(MockResponse {
362 status: 200, headers: HashMap::new(),
364 body: mock_data,
365 }))
366 } else {
367 Ok(None)
368 }
369 }
370
371 #[allow(dead_code)]
373 fn find_best_response_schema(&self, operation: &openapiv3::Operation) -> Result<Option<Value>> {
374 let responses = &operation.responses;
375
376 if let Some(response) = responses.responses.get(&openapiv3::StatusCode::Code(200)) {
378 if let Some(schema) = self.extract_response_schema(response)? {
379 return Ok(Some(schema));
380 }
381 }
382
383 if let Some(response) = responses.responses.get(&openapiv3::StatusCode::Code(201)) {
385 if let Some(schema) = self.extract_response_schema(response)? {
386 return Ok(Some(schema));
387 }
388 }
389
390 for (code, response) in &responses.responses {
392 if let openapiv3::StatusCode::Code(status_code) = code {
393 if *status_code >= 200 && *status_code < 300 {
394 if let Some(schema) = self.extract_response_schema(response)? {
395 return Ok(Some(schema));
396 }
397 }
398 }
399 }
400
401 Ok(None)
402 }
403
404 fn extract_response_schema_from_json(&self, response: &Value) -> Result<Option<Value>> {
406 if let Some(content) = response.get("content") {
408 if let Some(json_content) = content.get("application/json") {
409 if let Some(schema) = json_content.get("schema") {
410 if let Some(ref_path) = schema.get("$ref").and_then(|r| r.as_str()) {
412 if let Some(schema_name) = ref_path.split('/').next_back() {
414 return Ok(Some(json!({
417 "$ref": ref_path,
418 "schema_name": schema_name
419 })));
420 }
421 }
422 return Ok(Some(schema.clone()));
423 }
424 }
425 }
426 Ok(None)
427 }
428
429 fn resolve_schema_ref(&self, spec: &Value, ref_path: &str) -> Result<Option<Value>> {
431 if ref_path.starts_with("#/components/schemas/") {
433 let schema_name = ref_path.strip_prefix("#/components/schemas/").unwrap();
434 if let Some(components) = spec.get("components") {
435 if let Some(schemas) = components.get("schemas") {
436 if let Some(schema) = schemas.get(schema_name) {
437 return Ok(Some(schema.clone()));
438 }
439 }
440 }
441 }
442 Ok(None)
443 }
444
445 #[allow(dead_code)]
447 fn extract_response_schema(
448 &self,
449 response: &openapiv3::ReferenceOr<openapiv3::Response>,
450 ) -> Result<Option<Value>> {
451 match response {
452 openapiv3::ReferenceOr::Item(response) => {
453 let content = &response.content;
454 if let Some(json_content) = content.get("application/json") {
456 if let Some(schema) = &json_content.schema {
457 return Ok(Some(serde_json::to_value(schema)?));
458 }
459 }
460
461 for (_, media_type) in content {
463 if let Some(schema) = &media_type.schema {
464 return Ok(Some(serde_json::to_value(schema)?));
465 }
466 }
467
468 Ok(None)
469 }
470 openapiv3::ReferenceOr::Reference { .. } => {
471 Ok(None)
473 }
474 }
475 }
476
477 fn determine_faker_type(&self, field: &FieldDefinition) -> String {
479 let field_name = field.name.to_lowercase();
480
481 if let Some(mapped_type) = self.config.field_mappings.get(&field_name) {
483 return mapped_type.clone();
484 }
485
486 let mut best_match: Option<(&String, &String)> = None;
490 let priority_patterns = ["email", "mail"]; for (pattern, faker_type) in &self.field_patterns {
493 if field_name.contains(pattern) {
494 let is_priority = priority_patterns.contains(&pattern.as_str());
496
497 if let Some((best_pattern, _best_faker_type)) = best_match {
498 let best_is_priority = priority_patterns.contains(&best_pattern.as_str());
499
500 if is_priority && !best_is_priority {
502 best_match = Some((pattern, faker_type));
503 } else if !is_priority && best_is_priority {
504 } else if pattern.len() > best_pattern.len() {
506 best_match = Some((pattern, faker_type));
507 }
508 } else {
509 best_match = Some((pattern, faker_type));
510 }
511 }
512 }
513
514 if let Some((_, faker_type)) = best_match {
515 return faker_type.clone();
516 }
517
518 field.field_type.clone()
520 }
521
522 fn generate_field_value(&mut self, field: &FieldDefinition, faker_type: &str) -> Result<Value> {
524 if let Some(template) = &field.faker_template {
531 return Ok(self.faker.generate_by_type(template));
532 }
533
534 if field.field_type == "array" {
536 return self.generate_array_value(field);
537 }
538
539 if field.field_type == "object" && field.constraints.contains_key("properties") {
541 return self.generate_object_value(field);
542 }
543
544 let value = self.faker.generate_by_type(faker_type);
546
547 self.apply_constraints(&value, field)
549 }
550
551 fn generate_array_value(&mut self, field: &FieldDefinition) -> Result<Value> {
553 let min_items =
555 field.constraints.get("minItems").and_then(|v| v.as_u64()).unwrap_or(0) as usize;
556 let max_items = field
557 .constraints
558 .get("maxItems")
559 .and_then(|v| v.as_u64())
560 .unwrap_or(self.config.max_array_size as u64) as usize;
561
562 let array_size = if min_items > 0 || max_items < self.config.max_array_size {
564 let size = if min_items > 0 {
566 min_items.max(self.config.default_array_size)
567 } else {
568 self.config.default_array_size
569 };
570 size.min(max_items.max(min_items))
571 } else {
572 self.config.default_array_size
573 };
574
575 let mut array = Vec::new();
577
578 if let Some(items_schema) = field.constraints.get("itemsSchema") {
580 let items_schema_def = SchemaDefinition::from_json_schema(items_schema)?;
582 for _ in 0..array_size {
583 let item = self.generate_schema_data(&items_schema_def)?;
584 array.push(item);
585 }
586 } else {
587 let items_type =
589 field.constraints.get("itemsType").and_then(|v| v.as_str()).unwrap_or("string");
590
591 for _ in 0..array_size {
592 let item = self.faker.generate_by_type(items_type);
593 array.push(item);
594 }
595 }
596
597 Ok(Value::Array(array))
598 }
599
600 fn generate_object_value(&mut self, field: &FieldDefinition) -> Result<Value> {
602 let properties = field
604 .constraints
605 .get("properties")
606 .ok_or_else(|| Error::generic("Object field missing properties constraint"))?;
607
608 let required_fields: Vec<String> = field
610 .constraints
611 .get("required")
612 .and_then(|v| v.as_array())
613 .map(|arr| arr.iter().filter_map(|v| v.as_str().map(|s| s.to_string())).collect())
614 .unwrap_or_default();
615
616 let nested_schema = SchemaDefinition::from_json_schema(&json!({
618 "type": "object",
619 "properties": properties,
620 "required": required_fields
621 }))?;
622
623 self.generate_schema_data(&nested_schema)
625 }
626
627 pub fn generate_with_persona(
633 &mut self,
634 entity_id: &str,
635 domain: Domain,
636 schema: &SchemaDefinition,
637 ) -> Result<Value> {
638 let store = self.consistency_store.as_ref().ok_or_else(|| {
640 Error::generic("Persona support not enabled. Use with_persona_support() to create generator with persona support.")
641 })?;
642
643 if self.config.enable_backstories {
645 self.ensure_persona_backstory(store, entity_id, domain)?;
646 }
647
648 let mut object = serde_json::Map::new();
649
650 for field in &schema.fields {
651 if !field.required && !self.config.include_optional_fields {
653 continue;
654 }
655
656 let faker_type = self.determine_faker_type(field);
658
659 let value = match store.generate_consistent_value(entity_id, &faker_type, Some(domain))
661 {
662 Ok(v) => v,
663 Err(_) => {
664 self.faker.generate_by_type(&faker_type)
666 }
667 };
668
669 if self.config.validate_generated_data {
671 field.validate_value(&value)?;
672 }
673
674 object.insert(field.name.clone(), value);
675 }
676
677 Ok(Value::Object(object))
678 }
679
680 fn ensure_persona_backstory(
685 &self,
686 store: &ConsistencyStore,
687 entity_id: &str,
688 domain: Domain,
689 ) -> Result<()> {
690 let persona_registry = store.persona_registry();
691 let persona = store.get_entity_persona(entity_id, Some(domain));
692
693 if persona.has_backstory() {
695 return Ok(());
696 }
697
698 let mut persona_mut = persona.clone();
700 if persona_mut.traits.is_empty() {
701 let template_registry = PersonaTemplateRegistry::new();
702 template_registry.apply_template_to_persona(&mut persona_mut)?;
703 }
704
705 let backstory_generator = BackstoryGenerator::new();
707 match backstory_generator.generate_backstory(&persona_mut) {
708 Ok(backstory) => {
709 let mut traits = HashMap::new();
711 for (key, value) in &persona_mut.traits {
712 traits.insert(key.clone(), value.clone());
713 }
714
715 if !traits.is_empty() {
717 persona_registry.update_persona(entity_id, traits)?;
718 }
719
720 persona_registry.update_persona_backstory(entity_id, backstory)?;
722 }
723 Err(e) => {
724 warn!("Failed to generate backstory for persona {}: {}", entity_id, e);
725 }
726 }
727
728 Ok(())
729 }
730
731 fn apply_constraints(&mut self, value: &Value, field: &FieldDefinition) -> Result<Value> {
733 let mut constrained_value = value.clone();
734
735 if let Value::Number(num) = value {
737 let is_integer_field = field.field_type == "int" || field.field_type == "integer";
739
740 if let Some(minimum) = field.constraints.get("minimum") {
741 if let Some(min_val) = minimum.as_f64() {
742 if num.as_f64().unwrap_or(0.0) < min_val {
743 if is_integer_field {
745 constrained_value = json!(min_val as i64);
746 } else {
747 constrained_value = json!(min_val);
748 }
749 }
750 }
751 }
752
753 if let Some(maximum) = field.constraints.get("maximum") {
754 if let Some(max_val) = maximum.as_f64() {
755 if num.as_f64().unwrap_or(0.0) > max_val {
756 if is_integer_field {
758 constrained_value = json!(max_val as i64);
759 } else {
760 constrained_value = json!(max_val);
761 }
762 }
763 }
764 }
765 }
766
767 if let Value::String(s) = value {
769 let mut constrained_string = s.clone();
770
771 if let Some(min_length) = field.constraints.get("minLength") {
773 if let Some(min_len) = min_length.as_u64() {
774 if constrained_string.len() < min_len as usize {
775 let padding_needed = min_len as usize - constrained_string.len();
777 let padding = self.faker.string(padding_needed);
778 constrained_string = format!("{}{}", constrained_string, padding);
779 }
780 }
781 }
782
783 if let Some(max_length) = field.constraints.get("maxLength") {
784 if let Some(max_len) = max_length.as_u64() {
785 if constrained_string.len() > max_len as usize {
786 constrained_string.truncate(max_len as usize);
787 }
788 }
789 }
790
791 constrained_value = json!(constrained_string);
792 }
793
794 if let Some(enum_values) = field.constraints.get("enum") {
796 if let Some(enum_array) = enum_values.as_array() {
797 if !enum_array.is_empty() {
798 if let Some(random_value) = self.faker.random_element(enum_array) {
799 constrained_value = random_value.clone();
800 }
801 }
802 }
803 }
804
805 Ok(constrained_value)
806 }
807
808 fn parse_openapi_spec(&self, spec: &Value) -> Result<OpenApiSpec> {
810 let spec_obj = spec
813 .as_object()
814 .ok_or_else(|| Error::generic("Invalid OpenAPI specification"))?;
815
816 let info = spec_obj
817 .get("info")
818 .ok_or_else(|| Error::generic("Missing 'info' section in OpenAPI spec"))?;
819
820 let title = info.get("title").and_then(|t| t.as_str()).unwrap_or("Unknown API").to_string();
821
822 let version = info.get("version").and_then(|v| v.as_str()).unwrap_or("1.0.0").to_string();
823
824 let description = info.get("description").and_then(|d| d.as_str()).map(|s| s.to_string());
825
826 Ok(OpenApiSpec {
827 info: OpenApiInfo {
828 title,
829 version,
830 description,
831 },
832 paths: HashMap::new(), })
834 }
835
836 fn extract_schemas_from_spec(
838 &mut self,
839 spec: &Value,
840 ) -> Result<HashMap<String, SchemaDefinition>> {
841 let mut schemas = HashMap::new();
842
843 if let Some(components) = spec.get("components") {
845 if let Some(schemas_section) = components.get("schemas") {
846 if let Some(schema_obj) = schemas_section.as_object() {
847 for (name, schema_def) in schema_obj {
848 let schema = SchemaDefinition::from_json_schema(schema_def)?;
849 schemas.insert(name.clone(), schema);
850 }
851 }
852 }
853 }
854
855 if let Some(paths) = spec.get("paths") {
857 if let Some(paths_obj) = paths.as_object() {
858 for (path, path_item) in paths_obj {
859 if let Some(path_obj) = path_item.as_object() {
860 for (method, operation) in path_obj {
861 if let Some(op_obj) = operation.as_object() {
862 if let Some(request_body) = op_obj.get("requestBody") {
864 if let Some(content) = request_body.get("content") {
865 if let Some(json_content) = content.get("application/json")
866 {
867 if let Some(schema) = json_content.get("schema") {
868 let schema_name = format!(
869 "{}_{}_request",
870 path.replace("/", "_").trim_start_matches("_"),
871 method
872 );
873 let schema_def =
874 SchemaDefinition::from_json_schema(schema)?;
875 schemas.insert(schema_name, schema_def);
876 }
877 }
878 }
879 }
880
881 if let Some(responses) = op_obj.get("responses") {
883 if let Some(resp_obj) = responses.as_object() {
884 for (status_code, response) in resp_obj {
885 if let Some(content) = response.get("content") {
886 if let Some(json_content) =
887 content.get("application/json")
888 {
889 if let Some(schema) = json_content.get("schema")
890 {
891 let schema_name = format!(
892 "{}_{}_response_{}",
893 path.replace("/", "_")
894 .trim_start_matches("_"),
895 method,
896 status_code
897 );
898 let schema_def =
899 SchemaDefinition::from_json_schema(
900 schema,
901 )?;
902 schemas.insert(schema_name, schema_def);
903 }
904 }
905 }
906 }
907 }
908 }
909 }
910 }
911 }
912 }
913 }
914 }
915
916 Ok(schemas)
917 }
918
919 fn create_field_patterns() -> HashMap<String, String> {
921 let mut patterns = HashMap::new();
922
923 patterns.insert("email".to_string(), "email".to_string());
925 patterns.insert("mail".to_string(), "email".to_string());
926
927 patterns.insert("name".to_string(), "name".to_string());
929 patterns.insert("firstname".to_string(), "name".to_string());
930 patterns.insert("lastname".to_string(), "name".to_string());
931 patterns.insert("username".to_string(), "name".to_string());
932
933 patterns.insert("phone".to_string(), "phone".to_string());
935 patterns.insert("mobile".to_string(), "phone".to_string());
936 patterns.insert("telephone".to_string(), "phone".to_string());
937
938 patterns.insert("address".to_string(), "address".to_string());
940 patterns.insert("street".to_string(), "address".to_string());
941 patterns.insert("city".to_string(), "string".to_string());
942 patterns.insert("state".to_string(), "string".to_string());
943 patterns.insert("zip".to_string(), "string".to_string());
944 patterns.insert("postal".to_string(), "string".to_string());
945
946 patterns.insert("company".to_string(), "company".to_string());
948 patterns.insert("organization".to_string(), "company".to_string());
949 patterns.insert("corp".to_string(), "company".to_string());
950
951 patterns.insert("url".to_string(), "url".to_string());
953 patterns.insert("website".to_string(), "url".to_string());
954 patterns.insert("link".to_string(), "url".to_string());
955
956 patterns.insert("date".to_string(), "date".to_string());
958 patterns.insert("created".to_string(), "date".to_string());
959 patterns.insert("updated".to_string(), "date".to_string());
960 patterns.insert("timestamp".to_string(), "date".to_string());
961
962 patterns.insert("id".to_string(), "uuid".to_string());
964 patterns.insert("uuid".to_string(), "uuid".to_string());
965 patterns.insert("guid".to_string(), "uuid".to_string());
966
967 patterns.insert("ip".to_string(), "ip".to_string());
969 patterns.insert("ipv4".to_string(), "ip".to_string());
970 patterns.insert("ipv6".to_string(), "ip".to_string());
971
972 patterns
973 }
974
975 fn initialize_common_schemas(&mut self) {
977 }
980}
981
982impl Default for MockDataGenerator {
983 fn default() -> Self {
984 Self::new()
985 }
986}
987
988#[derive(Debug, Clone, serde::Serialize)]
990pub struct MockDataResult {
991 pub schemas: HashMap<String, Value>,
993 pub responses: HashMap<String, MockResponse>,
995 pub warnings: Vec<String>,
997 pub spec_info: OpenApiInfo,
999}
1000
1001#[derive(Debug, Clone, serde::Serialize)]
1003pub struct MockResponse {
1004 pub status: u16,
1006 pub headers: HashMap<String, String>,
1008 pub body: Value,
1010}
1011
1012#[derive(Debug, Clone, serde::Serialize)]
1014pub struct OpenApiInfo {
1015 pub title: String,
1017 pub version: String,
1019 pub description: Option<String>,
1021}
1022
1023#[derive(Debug)]
1025#[allow(dead_code)]
1026struct OpenApiSpec {
1027 info: OpenApiInfo,
1028 paths: HashMap<String, PathItem>,
1029}
1030
1031#[derive(Debug)]
1033#[allow(dead_code)]
1034struct PathItem {
1035 operations: HashMap<String, openapiv3::Operation>,
1036}
1037
1038impl PathItem {
1039 #[allow(dead_code)]
1040 fn operations(&self) -> &HashMap<String, openapiv3::Operation> {
1041 &self.operations
1042 }
1043}
1044
1045#[cfg(test)]
1046mod tests {
1047 use super::*;
1048
1049 #[test]
1050 fn test_mock_generator_config_default() {
1051 let config = MockGeneratorConfig::default();
1052
1053 assert!(config.realistic_mode);
1054 assert_eq!(config.default_array_size, 3);
1055 assert_eq!(config.max_array_size, 10);
1056 assert!(config.include_optional_fields);
1057 assert!(config.validate_generated_data);
1058 }
1059
1060 #[test]
1061 fn test_mock_generator_config_custom() {
1062 let config = MockGeneratorConfig::new()
1063 .realistic_mode(false)
1064 .default_array_size(5)
1065 .max_array_size(20)
1066 .include_optional_fields(false)
1067 .field_mapping("email".to_string(), "email".to_string())
1068 .validate_generated_data(false);
1069
1070 assert!(!config.realistic_mode);
1071 assert_eq!(config.default_array_size, 5);
1072 assert_eq!(config.max_array_size, 20);
1073 assert!(!config.include_optional_fields);
1074 assert!(!config.validate_generated_data);
1075 assert!(config.field_mappings.contains_key("email"));
1076 }
1077
1078 #[test]
1079 fn test_mock_data_generator_new() {
1080 let generator = MockDataGenerator::new();
1081
1082 assert!(generator.config.realistic_mode);
1083 assert!(!generator.field_patterns.is_empty());
1084 }
1085
1086 #[test]
1087 fn test_mock_data_generator_with_config() {
1088 let config = MockGeneratorConfig::new().realistic_mode(false).default_array_size(10);
1089
1090 let generator = MockDataGenerator::with_config(config);
1091
1092 assert!(!generator.config.realistic_mode);
1093 assert_eq!(generator.config.default_array_size, 10);
1094 }
1095
1096 #[test]
1097 fn test_determine_faker_type_custom_mapping() {
1098 let mut config = MockGeneratorConfig::new();
1099 config.field_mappings.insert("user_email".to_string(), "email".to_string());
1100
1101 let generator = MockDataGenerator::with_config(config);
1102
1103 let field = FieldDefinition::new("user_email".to_string(), "string".to_string());
1104 let faker_type = generator.determine_faker_type(&field);
1105
1106 assert_eq!(faker_type, "email");
1107 }
1108
1109 #[test]
1110 fn test_determine_faker_type_pattern_matching() {
1111 let generator = MockDataGenerator::new();
1112
1113 let field = FieldDefinition::new("email_address".to_string(), "string".to_string());
1114 let faker_type = generator.determine_faker_type(&field);
1115
1116 assert_eq!(faker_type, "email");
1117 }
1118
1119 #[test]
1120 fn test_determine_faker_type_fallback() {
1121 let generator = MockDataGenerator::new();
1122
1123 let field = FieldDefinition::new("unknown_field".to_string(), "integer".to_string());
1124 let faker_type = generator.determine_faker_type(&field);
1125
1126 assert_eq!(faker_type, "integer");
1127 }
1128
1129 #[test]
1130 fn test_field_patterns_creation() {
1131 let patterns = MockDataGenerator::create_field_patterns();
1132
1133 assert!(patterns.contains_key("email"));
1134 assert!(patterns.contains_key("name"));
1135 assert!(patterns.contains_key("phone"));
1136 assert!(patterns.contains_key("address"));
1137 assert!(patterns.contains_key("company"));
1138 assert!(patterns.contains_key("url"));
1139 assert!(patterns.contains_key("date"));
1140 assert!(patterns.contains_key("id"));
1141 assert!(patterns.contains_key("ip"));
1142 }
1143
1144 #[test]
1145 fn test_generate_from_json_schema_simple() {
1146 let mut generator = MockDataGenerator::new();
1147
1148 let schema = json!({
1149 "type": "object",
1150 "properties": {
1151 "name": { "type": "string" },
1152 "age": { "type": "integer" },
1153 "email": { "type": "string" }
1154 },
1155 "required": ["name", "age"]
1156 });
1157
1158 let result = generator.generate_from_json_schema(&schema).unwrap();
1159
1160 assert!(result.is_object());
1161 let obj = result.as_object().unwrap();
1162 assert!(obj.contains_key("name"));
1163 assert!(obj.contains_key("age"));
1164 assert!(obj.contains_key("email"));
1165 }
1166
1167 #[test]
1168 fn test_generate_from_json_schema_with_constraints() {
1169 let mut generator = MockDataGenerator::new();
1170
1171 let schema = json!({
1172 "type": "object",
1173 "properties": {
1174 "age": {
1175 "type": "integer",
1176 "minimum": 18,
1177 "maximum": 65
1178 },
1179 "name": {
1180 "type": "string",
1181 "minLength": 5,
1182 "maxLength": 20
1183 }
1184 }
1185 });
1186
1187 let result = generator.generate_from_json_schema(&schema).unwrap();
1188
1189 assert!(result.is_object());
1190 let obj = result.as_object().unwrap();
1191
1192 if let Some(age) = obj.get("age") {
1193 if let Some(age_num) = age.as_i64() {
1194 assert!(age_num >= 18);
1195 assert!(age_num <= 65);
1196 }
1197 }
1198 }
1199
1200 #[test]
1201 fn test_generate_from_json_schema_with_enum() {
1202 let mut generator = MockDataGenerator::new();
1203
1204 let schema = json!({
1205 "type": "object",
1206 "properties": {
1207 "status": {
1208 "type": "string",
1209 "enum": ["active", "inactive", "pending"]
1210 }
1211 }
1212 });
1213
1214 let result = generator.generate_from_json_schema(&schema).unwrap();
1215
1216 assert!(result.is_object());
1217 let obj = result.as_object().unwrap();
1218
1219 if let Some(status) = obj.get("status") {
1220 if let Some(status_str) = status.as_str() {
1221 assert!(["active", "inactive", "pending"].contains(&status_str));
1222 }
1223 }
1224 }
1225}