mockforge_data/
schema.rs

1//! Schema definitions for data generation
2
3use crate::faker::EnhancedFaker;
4use crate::{Error, Result};
5use serde::{Deserialize, Serialize};
6use serde_json::Value;
7use std::collections::HashMap;
8
9/// Field definition for data generation
10#[derive(Debug, Clone, Serialize, Deserialize)]
11pub struct FieldDefinition {
12    /// Field name
13    pub name: String,
14    /// Field type
15    pub field_type: String,
16    /// Whether the field is required
17    pub required: bool,
18    /// Default value (optional)
19    pub default: Option<Value>,
20    /// Additional constraints
21    pub constraints: HashMap<String, Value>,
22    /// Faker template (optional)
23    pub faker_template: Option<String>,
24    /// Field description for RAG
25    pub description: Option<String>,
26}
27
28impl FieldDefinition {
29    /// Create a new field definition
30    pub fn new(name: String, field_type: String) -> Self {
31        Self {
32            name,
33            field_type,
34            required: true,
35            default: None,
36            constraints: HashMap::new(),
37            faker_template: None,
38            description: None,
39        }
40    }
41
42    /// Mark field as optional
43    pub fn optional(mut self) -> Self {
44        self.required = false;
45        self
46    }
47
48    /// Set default value
49    pub fn with_default(mut self, default: Value) -> Self {
50        self.default = Some(default);
51        self
52    }
53
54    /// Add a constraint
55    pub fn with_constraint(mut self, key: String, value: Value) -> Self {
56        self.constraints.insert(key, value);
57        self
58    }
59
60    /// Set faker template
61    pub fn with_faker_template(mut self, template: String) -> Self {
62        self.faker_template = Some(template);
63        self
64    }
65
66    /// Set description
67    pub fn with_description(mut self, description: String) -> Self {
68        self.description = Some(description);
69        self
70    }
71
72    /// Generate a value for this field
73    pub fn generate_value(&self, faker: &mut EnhancedFaker) -> Value {
74        // Use faker template if provided
75        if let Some(template) = &self.faker_template {
76            return faker.generate_by_type(template);
77        }
78
79        // Use default value if available and field is not required
80        if !self.required {
81            if let Some(default) = &self.default {
82                return default.clone();
83            }
84        }
85
86        // Generate based on field type
87        faker.generate_by_type(&self.field_type)
88    }
89
90    /// Validate a generated value against constraints
91    pub fn validate_value(&self, value: &Value) -> Result<()> {
92        // Check required constraint
93        if self.required && value.is_null() {
94            return Err(Error::generic(format!("Required field '{}' is null", self.name)));
95        }
96
97        // Check type constraints - use field_type as primary, fall back to constraints
98        let expected_type = self
99            .constraints
100            .get("type")
101            .and_then(|v| v.as_str())
102            .unwrap_or(&self.field_type);
103
104        let actual_type = match value {
105            Value::String(_) => "string",
106            Value::Number(_) => match expected_type {
107                "integer" => "integer",
108                _ => "number",
109            },
110            Value::Bool(_) => "boolean",
111            Value::Object(_) => "object",
112            Value::Array(_) => "array",
113            Value::Null => "null",
114        };
115
116        // Use expected_type directly - all expected values should match actual values
117        let normalized_expected = expected_type;
118
119        if normalized_expected != actual_type
120            && !(normalized_expected == "number" && actual_type == "integer")
121            && !(normalized_expected == "float" && actual_type == "number")
122            && !(normalized_expected == "uuid" && actual_type == "string")
123            && !(normalized_expected == "email" && actual_type == "string")
124            && !(normalized_expected == "name" && actual_type == "string")
125            && !(normalized_expected == "address" && actual_type == "string")
126            && !(normalized_expected == "phone" && actual_type == "string")
127            && !(normalized_expected == "company" && actual_type == "string")
128            && !(normalized_expected == "url" && actual_type == "string")
129            && !(normalized_expected == "ip" && actual_type == "string")
130            && !(normalized_expected == "color" && actual_type == "string")
131            && !(normalized_expected == "date" && actual_type == "string")
132            && !(normalized_expected == "datetime" && actual_type == "string")
133            && !(normalized_expected == "paragraph" && actual_type == "string")
134        {
135            return Err(Error::generic(format!(
136                "Field '{}' type mismatch: expected {}, got {}",
137                self.name, normalized_expected, actual_type
138            )));
139        }
140
141        // Check min/max constraints for numbers
142        if let Value::Number(num) = value {
143            if let Some(min_val) = self.constraints.get("minimum") {
144                if let Some(min_num) = min_val.as_f64() {
145                    if num.as_f64().unwrap_or(0.0) < min_num {
146                        return Err(Error::generic(format!(
147                            "Field '{}' value {} is less than minimum {}",
148                            self.name, num, min_num
149                        )));
150                    }
151                }
152            }
153
154            if let Some(max_val) = self.constraints.get("maximum") {
155                if let Some(max_num) = max_val.as_f64() {
156                    if num.as_f64().unwrap_or(0.0) > max_num {
157                        return Err(Error::generic(format!(
158                            "Field '{}' value {} is greater than maximum {}",
159                            self.name, num, max_num
160                        )));
161                    }
162                }
163            }
164        }
165
166        // Check string constraints
167        if let Value::String(s) = value {
168            if let Some(min_len) = self.constraints.get("minLength") {
169                if let Some(min_len_num) = min_len.as_u64() {
170                    if s.len() < min_len_num as usize {
171                        return Err(Error::generic(format!(
172                            "Field '{}' length {} is less than minimum {}",
173                            self.name,
174                            s.len(),
175                            min_len_num
176                        )));
177                    }
178                }
179            }
180
181            if let Some(max_len) = self.constraints.get("maxLength") {
182                if let Some(max_len_num) = max_len.as_u64() {
183                    if s.len() > max_len_num as usize {
184                        return Err(Error::generic(format!(
185                            "Field '{}' length {} is greater than maximum {}",
186                            self.name,
187                            s.len(),
188                            max_len_num
189                        )));
190                    }
191                }
192            }
193        }
194
195        Ok(())
196    }
197}
198
199/// Schema definition for data generation
200#[derive(Debug, Clone, Serialize, Deserialize)]
201pub struct SchemaDefinition {
202    /// Schema name
203    pub name: String,
204    /// Schema description
205    pub description: Option<String>,
206    /// Field definitions
207    pub fields: Vec<FieldDefinition>,
208    /// Relationships to other schemas
209    pub relationships: HashMap<String, Relationship>,
210    /// Additional metadata
211    pub metadata: HashMap<String, Value>,
212}
213
214impl SchemaDefinition {
215    /// Create a new schema definition
216    pub fn new(name: String) -> Self {
217        Self {
218            name,
219            description: None,
220            fields: Vec::new(),
221            relationships: HashMap::new(),
222            metadata: HashMap::new(),
223        }
224    }
225
226    /// Add a field to the schema
227    pub fn with_field(mut self, field: FieldDefinition) -> Self {
228        self.fields.push(field);
229        self
230    }
231
232    /// Add multiple fields to the schema
233    pub fn with_fields(mut self, fields: Vec<FieldDefinition>) -> Self {
234        self.fields.extend(fields);
235        self
236    }
237
238    /// Set description
239    pub fn with_description(mut self, description: String) -> Self {
240        self.description = Some(description);
241        self
242    }
243
244    /// Add a relationship
245    pub fn with_relationship(mut self, name: String, relationship: Relationship) -> Self {
246        self.relationships.insert(name, relationship);
247        self
248    }
249
250    /// Add metadata
251    pub fn with_metadata(mut self, key: String, value: Value) -> Self {
252        self.metadata.insert(key, value);
253        self
254    }
255
256    /// Generate a single row of data
257    pub fn generate_row(&self, faker: &mut EnhancedFaker) -> Result<Value> {
258        let mut row = serde_json::Map::new();
259
260        for field in &self.fields {
261            let value = field.generate_value(faker);
262            field.validate_value(&value)?;
263            row.insert(field.name.clone(), value);
264        }
265
266        Ok(Value::Object(row))
267    }
268
269    /// Get field by name
270    pub fn get_field(&self, name: &str) -> Option<&FieldDefinition> {
271        self.fields.iter().find(|field| field.name == name)
272    }
273
274    /// Create schema from JSON Schema
275    pub fn from_json_schema(json_schema: &Value) -> Result<Self> {
276        let title = json_schema
277            .get("title")
278            .and_then(|v| v.as_str())
279            .unwrap_or("GeneratedSchema")
280            .to_string();
281
282        let description =
283            json_schema.get("description").and_then(|v| v.as_str()).map(|s| s.to_string());
284
285        let mut schema = Self::new(title);
286        if let Some(desc) = description {
287            schema = schema.with_description(desc);
288        }
289
290        if let Some(properties) = json_schema.get("properties") {
291            if let Some(props_obj) = properties.as_object() {
292                for (name, prop_def) in props_obj {
293                    let field_type = extract_type_from_json_schema(prop_def);
294                    let mut field = FieldDefinition::new(name.clone(), field_type);
295
296                    // Check if required
297                    if let Some(required) = json_schema.get("required") {
298                        if let Some(required_arr) = required.as_array() {
299                            let is_required = required_arr.iter().any(|v| v.as_str() == Some(name));
300                            if !is_required {
301                                field = field.optional();
302                            }
303                        }
304                    }
305
306                    // Add description
307                    if let Some(desc) = prop_def.get("description").and_then(|v| v.as_str()) {
308                        field = field.with_description(desc.to_string());
309                    }
310
311                    // Add constraints
312                    if let Some(minimum) = prop_def.get("minimum") {
313                        field = field.with_constraint("minimum".to_string(), minimum.clone());
314                    }
315                    if let Some(maximum) = prop_def.get("maximum") {
316                        field = field.with_constraint("maximum".to_string(), maximum.clone());
317                    }
318                    if let Some(min_length) = prop_def.get("minLength") {
319                        field = field.with_constraint("minLength".to_string(), min_length.clone());
320                    }
321                    if let Some(max_length) = prop_def.get("maxLength") {
322                        field = field.with_constraint("maxLength".to_string(), max_length.clone());
323                    }
324
325                    schema = schema.with_field(field);
326                }
327            }
328        }
329
330        Ok(schema)
331    }
332
333    /// Create schema from OpenAPI spec
334    pub fn from_openapi_spec(openapi_spec: &Value) -> Result<Self> {
335        // Validate that it's a valid OpenAPI spec
336        if !openapi_spec.is_object() {
337            return Err(Error::generic("OpenAPI spec must be a JSON object"));
338        }
339
340        let spec = openapi_spec.as_object().unwrap();
341
342        // Extract API title
343        let title = spec
344            .get("info")
345            .and_then(|info| info.get("title"))
346            .and_then(|title| title.as_str())
347            .unwrap_or("OpenAPI Generated Schema")
348            .to_string();
349
350        // Extract description
351        let description = spec
352            .get("info")
353            .and_then(|info| info.get("description"))
354            .and_then(|desc| desc.as_str())
355            .map(|s| s.to_string());
356
357        let mut schema = Self::new(title);
358        if let Some(desc) = description {
359            schema = schema.with_description(desc);
360        }
361
362        // Parse paths and extract schemas
363        if let Some(paths) = spec.get("paths").and_then(|p| p.as_object()) {
364            for (path, path_item) in paths {
365                if let Some(path_obj) = path_item.as_object() {
366                    // Extract schemas from all operations on this path
367                    for (method, operation) in path_obj {
368                        if let Some(op_obj) = operation.as_object() {
369                            // Extract request body schema
370                            if let Some(request_body) = op_obj.get("requestBody") {
371                                if let Some(rb_obj) = request_body.as_object() {
372                                    if let Some(content) = rb_obj.get("content") {
373                                        if let Some(json_content) = content.get("application/json")
374                                        {
375                                            if let Some(schema_obj) = json_content.get("schema") {
376                                                let field_name = format!(
377                                                    "{}_{}_request",
378                                                    path.replace("/", "_").trim_start_matches("_"),
379                                                    method
380                                                );
381                                                if let Some(field) =
382                                                    Self::create_field_from_openapi_schema(
383                                                        &field_name,
384                                                        schema_obj,
385                                                    )
386                                                {
387                                                    schema = schema.with_field(field);
388                                                }
389                                            }
390                                        }
391                                    }
392                                }
393                            }
394
395                            // Extract response schemas
396                            if let Some(responses) = op_obj.get("responses") {
397                                if let Some(resp_obj) = responses.as_object() {
398                                    // Focus on success responses (200, 201, etc.)
399                                    for (status_code, response) in resp_obj {
400                                        if status_code == "200"
401                                            || status_code == "201"
402                                            || status_code.starts_with("2")
403                                        {
404                                            if let Some(resp_obj) = response.as_object() {
405                                                if let Some(content) = resp_obj.get("content") {
406                                                    if let Some(json_content) =
407                                                        content.get("application/json")
408                                                    {
409                                                        if let Some(schema_obj) =
410                                                            json_content.get("schema")
411                                                        {
412                                                            let field_name = format!(
413                                                                "{}_{}_response_{}",
414                                                                path.replace("/", "_")
415                                                                    .trim_start_matches("_"),
416                                                                method,
417                                                                status_code
418                                                            );
419                                                            if let Some(field) = Self::create_field_from_openapi_schema(&field_name, schema_obj) {
420                                                                schema = schema.with_field(field);
421                                                            }
422                                                        }
423                                                    }
424                                                }
425                                            }
426                                        }
427                                    }
428                                }
429                            }
430                        }
431                    }
432                }
433            }
434        }
435
436        // Also extract component schemas if they exist
437        if let Some(components) = spec.get("components") {
438            if let Some(comp_obj) = components.as_object() {
439                if let Some(schemas) = comp_obj.get("schemas") {
440                    if let Some(schema_obj) = schemas.as_object() {
441                        for (name, schema_def) in schema_obj {
442                            if let Some(field) =
443                                Self::create_field_from_openapi_schema(name, schema_def)
444                            {
445                                schema = schema.with_field(field);
446                            }
447                        }
448                    }
449                }
450            }
451        }
452
453        Ok(schema)
454    }
455
456    /// Create a field definition from an OpenAPI schema
457    fn create_field_from_openapi_schema(name: &str, schema: &Value) -> Option<FieldDefinition> {
458        if !schema.is_object() {
459            return None;
460        }
461
462        let schema_obj = schema.as_object().unwrap();
463
464        // Determine field type
465        let field_type = if let Some(type_val) = schema_obj.get("type") {
466            if let Some(type_str) = type_val.as_str() {
467                match type_str {
468                    "string" => "string".to_string(),
469                    "number" => "float".to_string(),
470                    "integer" => "int".to_string(),
471                    "boolean" => "boolean".to_string(),
472                    "object" => "object".to_string(),
473                    "array" => "array".to_string(),
474                    _ => "string".to_string(),
475                }
476            } else {
477                "string".to_string()
478            }
479        } else {
480            "string".to_string()
481        };
482
483        let mut field = FieldDefinition::new(name.to_string(), field_type);
484
485        // Set description
486        if let Some(desc) = schema_obj.get("description").and_then(|d| d.as_str()) {
487            field = field.with_description(desc.to_string());
488        }
489
490        // Mark as required if not explicitly optional
491        if let Some(required) = schema_obj.get("required") {
492            if let Some(required_arr) = required.as_array() {
493                if !required_arr.iter().any(|v| v.as_str() == Some(name)) {
494                    field = field.optional();
495                }
496            }
497        }
498
499        // Add constraints
500        if let Some(minimum) = schema_obj.get("minimum") {
501            field = field.with_constraint("minimum".to_string(), minimum.clone());
502        }
503        if let Some(maximum) = schema_obj.get("maximum") {
504            field = field.with_constraint("maximum".to_string(), maximum.clone());
505        }
506        if let Some(min_length) = schema_obj.get("minLength") {
507            field = field.with_constraint("minLength".to_string(), min_length.clone());
508        }
509        if let Some(max_length) = schema_obj.get("maxLength") {
510            field = field.with_constraint("maxLength".to_string(), max_length.clone());
511        }
512
513        // Handle enum values
514        if let Some(enum_vals) = schema_obj.get("enum") {
515            if let Some(_enum_arr) = enum_vals.as_array() {
516                field = field.with_constraint("enum".to_string(), enum_vals.clone());
517            }
518        }
519
520        Some(field)
521    }
522}
523
524/// Relationship definition between schemas
525#[derive(Debug, Clone, Serialize, Deserialize)]
526pub struct Relationship {
527    /// Target schema name
528    pub target_schema: String,
529    /// Relationship type
530    pub relationship_type: RelationshipType,
531    /// Foreign key field name
532    pub foreign_key: String,
533    /// Whether this is a required relationship
534    pub required: bool,
535}
536
537impl Relationship {
538    /// Create a new relationship
539    pub fn new(
540        target_schema: String,
541        relationship_type: RelationshipType,
542        foreign_key: String,
543    ) -> Self {
544        Self {
545            target_schema,
546            relationship_type,
547            foreign_key,
548            required: true,
549        }
550    }
551
552    /// Mark relationship as optional
553    pub fn optional(mut self) -> Self {
554        self.required = false;
555        self
556    }
557}
558
559/// Type of relationship between schemas
560#[derive(Debug, Clone, Serialize, Deserialize)]
561#[serde(rename_all = "lowercase")]
562pub enum RelationshipType {
563    /// One-to-one relationship
564    OneToOne,
565    /// One-to-many relationship
566    OneToMany,
567    /// Many-to-one relationship
568    ManyToOne,
569    /// Many-to-many relationship
570    ManyToMany,
571}
572
573/// Extract type from JSON Schema property definition
574fn extract_type_from_json_schema(prop_def: &Value) -> String {
575    if let Some(type_val) = prop_def.get("type") {
576        if let Some(type_str) = type_val.as_str() {
577            return match type_str {
578                "string" => "string".to_string(),
579                "number" => "float".to_string(),
580                "integer" => "int".to_string(),
581                "boolean" => "boolean".to_string(),
582                "object" => "object".to_string(),
583                "array" => "array".to_string(),
584                "null" => "null".to_string(),
585                _ => "string".to_string(),
586            };
587        }
588    }
589
590    // Default to string if type is not specified
591    "string".to_string()
592}
593
594/// Common schema templates
595pub mod templates {
596    use super::*;
597
598    /// Create a user schema
599    pub fn user_schema() -> SchemaDefinition {
600        SchemaDefinition::new("User".to_string())
601            .with_description("User account information".to_string())
602            .with_fields(vec![
603                FieldDefinition::new("id".to_string(), "uuid".to_string()),
604                FieldDefinition::new("email".to_string(), "email".to_string()),
605                FieldDefinition::new("name".to_string(), "name".to_string()),
606                FieldDefinition::new("created_at".to_string(), "date".to_string()),
607                FieldDefinition::new("active".to_string(), "boolean".to_string()),
608            ])
609    }
610
611    /// Create a product schema
612    pub fn product_schema() -> SchemaDefinition {
613        SchemaDefinition::new("Product".to_string())
614            .with_description("Product catalog item".to_string())
615            .with_fields(vec![
616                FieldDefinition::new("id".to_string(), "uuid".to_string()),
617                FieldDefinition::new("name".to_string(), "string".to_string()),
618                FieldDefinition::new("description".to_string(), "paragraph".to_string()),
619                FieldDefinition::new("price".to_string(), "float".to_string())
620                    .with_constraint("minimum".to_string(), Value::Number(0.into())),
621                FieldDefinition::new("category".to_string(), "string".to_string()),
622                FieldDefinition::new("in_stock".to_string(), "boolean".to_string()),
623            ])
624    }
625
626    /// Create an order schema with relationship to user
627    pub fn order_schema() -> SchemaDefinition {
628        SchemaDefinition::new("Order".to_string())
629            .with_description("Customer order".to_string())
630            .with_fields(vec![
631                FieldDefinition::new("id".to_string(), "uuid".to_string()),
632                FieldDefinition::new("user_id".to_string(), "uuid".to_string()),
633                FieldDefinition::new("total_amount".to_string(), "float".to_string())
634                    .with_constraint("minimum".to_string(), Value::Number(0.into())),
635                FieldDefinition::new("status".to_string(), "string".to_string()),
636                FieldDefinition::new("created_at".to_string(), "date".to_string()),
637            ])
638            .with_relationship(
639                "user".to_string(),
640                Relationship::new(
641                    "User".to_string(),
642                    RelationshipType::ManyToOne,
643                    "user_id".to_string(),
644                ),
645            )
646    }
647}
648
649#[cfg(test)]
650mod tests {
651    use super::*;
652
653    #[test]
654    fn test_field_definition_new() {
655        let field = FieldDefinition::new("test".to_string(), "string".to_string());
656
657        assert_eq!(field.name, "test");
658        assert_eq!(field.field_type, "string");
659        assert!(field.required);
660        assert!(field.default.is_none());
661    }
662
663    #[test]
664    fn test_field_definition_optional() {
665        let field = FieldDefinition::new("test".to_string(), "string".to_string()).optional();
666
667        assert!(!field.required);
668    }
669
670    #[test]
671    fn test_field_definition_with_default() {
672        let field = FieldDefinition::new("test".to_string(), "string".to_string())
673            .with_default(Value::String("default".to_string()));
674
675        assert_eq!(field.default, Some(Value::String("default".to_string())));
676    }
677
678    #[test]
679    fn test_field_definition_with_constraint() {
680        let field = FieldDefinition::new("age".to_string(), "int".to_string())
681            .with_constraint("minimum".to_string(), Value::Number(0.into()));
682
683        assert!(field.constraints.contains_key("minimum"));
684    }
685
686    #[test]
687    fn test_field_definition_with_faker_template() {
688        let field = FieldDefinition::new("email".to_string(), "string".to_string())
689            .with_faker_template("email".to_string());
690
691        assert_eq!(field.faker_template, Some("email".to_string()));
692    }
693
694    #[test]
695    fn test_field_definition_with_description() {
696        let field = FieldDefinition::new("test".to_string(), "string".to_string())
697            .with_description("Test field".to_string());
698
699        assert_eq!(field.description, Some("Test field".to_string()));
700    }
701
702    #[test]
703    fn test_schema_definition_new() {
704        let schema = SchemaDefinition::new("TestSchema".to_string());
705
706        assert_eq!(schema.name, "TestSchema");
707        assert!(schema.description.is_none());
708        assert_eq!(schema.fields.len(), 0);
709    }
710
711    #[test]
712    fn test_schema_definition_with_field() {
713        let field = FieldDefinition::new("id".to_string(), "uuid".to_string());
714        let schema = SchemaDefinition::new("Test".to_string()).with_field(field);
715
716        assert_eq!(schema.fields.len(), 1);
717        assert_eq!(schema.fields[0].name, "id");
718    }
719
720    #[test]
721    fn test_schema_definition_with_fields() {
722        let fields = vec![
723            FieldDefinition::new("id".to_string(), "uuid".to_string()),
724            FieldDefinition::new("name".to_string(), "string".to_string()),
725        ];
726        let schema = SchemaDefinition::new("Test".to_string()).with_fields(fields);
727
728        assert_eq!(schema.fields.len(), 2);
729    }
730
731    #[test]
732    fn test_schema_definition_with_description() {
733        let schema =
734            SchemaDefinition::new("Test".to_string()).with_description("Test schema".to_string());
735
736        assert_eq!(schema.description, Some("Test schema".to_string()));
737    }
738
739    #[test]
740    fn test_schema_definition_with_metadata() {
741        let schema = SchemaDefinition::new("Test".to_string())
742            .with_metadata("version".to_string(), Value::String("1.0".to_string()));
743
744        assert!(schema.metadata.contains_key("version"));
745    }
746
747    #[test]
748    fn test_schema_definition_get_field() {
749        let field = FieldDefinition::new("email".to_string(), "email".to_string());
750        let schema = SchemaDefinition::new("Test".to_string()).with_field(field);
751
752        assert!(schema.get_field("email").is_some());
753        assert!(schema.get_field("unknown").is_none());
754    }
755
756    #[test]
757    fn test_relationship_new() {
758        let rel = Relationship::new(
759            "User".to_string(),
760            RelationshipType::ManyToOne,
761            "user_id".to_string(),
762        );
763
764        assert_eq!(rel.target_schema, "User");
765        assert_eq!(rel.foreign_key, "user_id");
766        assert!(rel.required);
767    }
768
769    #[test]
770    fn test_relationship_optional() {
771        let rel = Relationship::new(
772            "User".to_string(),
773            RelationshipType::ManyToOne,
774            "user_id".to_string(),
775        )
776        .optional();
777
778        assert!(!rel.required);
779    }
780
781    #[test]
782    fn test_relationship_types() {
783        let one_to_one = RelationshipType::OneToOne;
784        let one_to_many = RelationshipType::OneToMany;
785        let many_to_one = RelationshipType::ManyToOne;
786        let many_to_many = RelationshipType::ManyToMany;
787
788        assert!(matches!(one_to_one, RelationshipType::OneToOne));
789        assert!(matches!(one_to_many, RelationshipType::OneToMany));
790        assert!(matches!(many_to_one, RelationshipType::ManyToOne));
791        assert!(matches!(many_to_many, RelationshipType::ManyToMany));
792    }
793
794    #[test]
795    fn test_extract_type_from_json_schema_string() {
796        let prop = serde_json::json!({"type": "string"});
797        let type_str = extract_type_from_json_schema(&prop);
798
799        assert_eq!(type_str, "string");
800    }
801
802    #[test]
803    fn test_extract_type_from_json_schema_number() {
804        let prop = serde_json::json!({"type": "number"});
805        let type_str = extract_type_from_json_schema(&prop);
806
807        assert_eq!(type_str, "float");
808    }
809
810    #[test]
811    fn test_extract_type_from_json_schema_integer() {
812        let prop = serde_json::json!({"type": "integer"});
813        let type_str = extract_type_from_json_schema(&prop);
814
815        assert_eq!(type_str, "int");
816    }
817
818    #[test]
819    fn test_extract_type_from_json_schema_boolean() {
820        let prop = serde_json::json!({"type": "boolean"});
821        let type_str = extract_type_from_json_schema(&prop);
822
823        assert_eq!(type_str, "boolean");
824    }
825
826    #[test]
827    fn test_extract_type_from_json_schema_default() {
828        let prop = serde_json::json!({});
829        let type_str = extract_type_from_json_schema(&prop);
830
831        assert_eq!(type_str, "string");
832    }
833
834    #[test]
835    fn test_user_schema_template() {
836        let schema = templates::user_schema();
837
838        assert_eq!(schema.name, "User");
839        assert_eq!(schema.fields.len(), 5);
840        assert!(schema.get_field("id").is_some());
841        assert!(schema.get_field("email").is_some());
842    }
843
844    #[test]
845    fn test_product_schema_template() {
846        let schema = templates::product_schema();
847
848        assert_eq!(schema.name, "Product");
849        assert!(schema.get_field("price").is_some());
850    }
851
852    #[test]
853    fn test_order_schema_template() {
854        let schema = templates::order_schema();
855
856        assert_eq!(schema.name, "Order");
857        assert_eq!(schema.relationships.len(), 1);
858        assert!(schema.relationships.contains_key("user"));
859    }
860}