mockforge_vbr/
handlers.rs

1//! Runtime request handlers
2//!
3//! This module provides runtime request handlers for generated CRUD operations,
4//! including request validation, response formatting, and error handling.
5
6use crate::constraints::ConstraintValidator;
7use crate::Result;
8use axum::extract::{Path, Query};
9use axum::http::StatusCode;
10use axum::response::Json;
11use axum::Extension;
12use serde_json::{json, Value};
13use std::collections::HashMap;
14use std::sync::Arc;
15
16/// Validates that a field name is a safe SQL identifier.
17/// Only allows alphanumeric characters and underscores.
18/// This prevents SQL injection through field names.
19fn is_safe_sql_identifier(name: &str) -> bool {
20    !name.is_empty()
21        && name.len() <= 64
22        && name.chars().all(|c| c.is_ascii_alphanumeric() || c == '_')
23        && !name.chars().next().map(|c| c.is_ascii_digit()).unwrap_or(true)
24}
25
26/// Validates a field name against the entity schema AND ensures it's a safe SQL identifier.
27/// Returns the field name if valid, or an error otherwise.
28fn validate_field_name<'a>(
29    field_name: &'a str,
30    entity: &crate::entities::Entity,
31) -> std::result::Result<&'a str, (StatusCode, Json<Value>)> {
32    // First check if it's a safe SQL identifier
33    if !is_safe_sql_identifier(field_name) {
34        return Err((
35            StatusCode::BAD_REQUEST,
36            Json(json!({
37                "error": format!("Invalid field name: '{}'. Field names must contain only alphanumeric characters and underscores.", field_name)
38            })),
39        ));
40    }
41
42    // Then check if the field exists in the schema
43    if !entity.schema.base.fields.iter().any(|f| f.name == field_name) {
44        return Err((
45            StatusCode::BAD_REQUEST,
46            Json(json!({
47                "error": format!("Unknown field: '{}'. Field does not exist in entity schema.", field_name)
48            })),
49        ));
50    }
51
52    Ok(field_name)
53}
54
55/// Generic handler context
56///
57/// This context is shared across all handlers via Axum Extension.
58/// The database is wrapped in Arc to allow sharing across async tasks.
59#[derive(Clone)]
60pub struct HandlerContext {
61    /// Database instance (shared via Arc)
62    pub database: Arc<dyn crate::database::VirtualDatabase + Send + Sync>,
63    /// Entity registry
64    pub registry: crate::entities::EntityRegistry,
65    /// Session data manager (optional, for session-scoped data)
66    pub session_manager: Option<std::sync::Arc<crate::session::SessionDataManager>>,
67    /// Snapshots directory (optional, for snapshot operations)
68    pub snapshots_dir: Option<std::path::PathBuf>,
69}
70
71/// Helper function to get entity and table name
72fn get_entity_info<'a>(
73    registry: &'a crate::entities::EntityRegistry,
74    entity_name: &str,
75) -> std::result::Result<(&'a crate::entities::Entity, &'a str), (StatusCode, Json<Value>)> {
76    let entity = registry.get(entity_name).ok_or_else(|| {
77        (
78            StatusCode::NOT_FOUND,
79            Json(json!({
80                "error": format!("Entity '{}' not found", entity_name)
81            })),
82        )
83    })?;
84
85    Ok((entity, entity.table_name()))
86}
87
88/// Helper function to apply auto-generation rules
89async fn apply_auto_generation(
90    data: &mut Value,
91    schema: &crate::schema::VbrSchemaDefinition,
92    entity_name: &str,
93    database: &dyn crate::database::VirtualDatabase,
94) -> Result<()> {
95    if let Value::Object(obj) = data {
96        for (field_name, rule) in &schema.auto_generation {
97            if !obj.contains_key(field_name) {
98                let generated_value = match rule {
99                    crate::schema::AutoGenerationRule::Uuid => {
100                        Value::String(uuid::Uuid::new_v4().to_string())
101                    }
102                    crate::schema::AutoGenerationRule::Timestamp => {
103                        Value::String(chrono::Utc::now().to_rfc3339())
104                    }
105                    crate::schema::AutoGenerationRule::Date => {
106                        Value::String(chrono::Utc::now().date_naive().to_string())
107                    }
108                    crate::schema::AutoGenerationRule::AutoIncrement => {
109                        // Auto-increment is handled by database
110                        continue;
111                    }
112                    crate::schema::AutoGenerationRule::Pattern(pattern) => {
113                        // Get counter for pattern-based IDs that use increment
114                        let counter = if pattern.contains("increment") {
115                            Some(
116                                crate::id_generation::get_and_increment_counter(
117                                    database,
118                                    entity_name,
119                                    field_name,
120                                )
121                                .await?,
122                            )
123                        } else {
124                            None
125                        };
126                        let id = crate::id_generation::generate_id(
127                            rule,
128                            entity_name,
129                            field_name,
130                            counter,
131                        )?;
132                        Value::String(id)
133                    }
134                    crate::schema::AutoGenerationRule::Realistic { .. } => {
135                        let id =
136                            crate::id_generation::generate_id(rule, entity_name, field_name, None)?;
137                        Value::String(id)
138                    }
139                    crate::schema::AutoGenerationRule::Custom(_) => {
140                        // Custom rules would need evaluation engine
141                        continue;
142                    }
143                };
144                obj.insert(field_name.clone(), generated_value);
145            }
146        }
147    }
148    Ok(())
149}
150
151/// Helper function to build WHERE clause from query parameters
152/// Returns an error if any field name is invalid (SQL injection prevention)
153fn build_where_clause(
154    params: &HashMap<String, String>,
155    entity: &crate::entities::Entity,
156) -> std::result::Result<(String, Vec<Value>), (StatusCode, Json<Value>)> {
157    let mut conditions = Vec::new();
158    let mut bind_values = Vec::new();
159
160    for (key, value) in params {
161        // Skip pagination and sorting parameters
162        if matches!(key.as_str(), "limit" | "offset" | "sort" | "order") {
163            continue;
164        }
165
166        // Validate field name is safe AND exists in schema
167        if is_safe_sql_identifier(key) && entity.schema.base.fields.iter().any(|f| f.name == *key) {
168            conditions.push(format!("{} = ?", key));
169            bind_values.push(Value::String(value.clone()));
170        }
171        // Silently ignore fields that don't exist or are invalid
172        // This maintains backwards compatibility while being safe
173    }
174
175    let where_clause = if conditions.is_empty() {
176        String::new()
177    } else {
178        format!("WHERE {}", conditions.join(" AND "))
179    };
180
181    Ok((where_clause, bind_values))
182}
183
184/// Helper function to build ORDER BY clause
185/// Validates sort field is safe and exists in schema (SQL injection prevention)
186fn build_order_by(params: &HashMap<String, String>, entity: &crate::entities::Entity) -> String {
187    if let Some(sort_field) = params.get("sort") {
188        // Validate sort field is a safe SQL identifier AND exists in schema
189        if is_safe_sql_identifier(sort_field)
190            && entity.schema.base.fields.iter().any(|f| f.name == *sort_field)
191        {
192            let order = params
193                .get("order")
194                .map(|o| o.to_uppercase())
195                .unwrap_or_else(|| "ASC".to_string());
196            if order == "ASC" || order == "DESC" {
197                return format!("ORDER BY {} {}", sort_field, order);
198            }
199        }
200    }
201    String::new()
202}
203
204/// Helper function to get pagination parameters
205fn get_pagination(params: &HashMap<String, String>) -> (Option<usize>, Option<usize>) {
206    let limit = params.get("limit").and_then(|v| v.parse().ok());
207    let offset = params.get("offset").and_then(|v| v.parse().ok());
208    (limit, offset)
209}
210
211/// List all entities (GET /api/{entity})
212pub async fn list_handler(
213    Path(entity_name): Path<String>,
214    Query(params): Query<HashMap<String, String>>,
215    Extension(context): Extension<HandlerContext>,
216) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
217    let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
218
219    // Build query with validated field names
220    let (where_clause, bind_values) = build_where_clause(&params, entity)?;
221    let order_by = build_order_by(&params, entity);
222    let (limit, offset) = get_pagination(&params);
223
224    // Build SELECT query
225    let mut query = format!("SELECT * FROM {} {}", table_name, where_clause);
226    if !order_by.is_empty() {
227        query.push_str(&format!(" {}", order_by));
228    }
229
230    // Add LIMIT and OFFSET (directly in query, not as parameters)
231    if let Some(limit_val) = limit {
232        query.push_str(&format!(" LIMIT {}", limit_val));
233    }
234    if let Some(offset_val) = offset {
235        query.push_str(&format!(" OFFSET {}", offset_val));
236    }
237
238    // Execute query
239    let results = context.database.query(&query, &bind_values).await.map_err(|e| {
240        (
241            StatusCode::INTERNAL_SERVER_ERROR,
242            Json(json!({"error": format!("Database query failed: {}", e)})),
243        )
244    })?;
245
246    // Get total count for pagination
247    let count_query = format!("SELECT COUNT(*) as total FROM {} {}", table_name, where_clause);
248    let count_results = context.database.query(&count_query, &bind_values).await.map_err(|e| {
249        (
250            StatusCode::INTERNAL_SERVER_ERROR,
251            Json(json!({"error": format!("Count query failed: {}", e)})),
252        )
253    })?;
254
255    let total = count_results
256        .first()
257        .and_then(|r| r.get("total"))
258        .and_then(|v| v.as_u64())
259        .unwrap_or(0);
260
261    Ok(Json(json!({
262        "data": results,
263        "total": total,
264        "limit": limit,
265        "offset": offset
266    })))
267}
268
269/// Get entity by ID (GET /api/{entity}/{id})
270pub async fn get_handler(
271    Path((entity_name, id)): Path<(String, String)>,
272    Extension(context): Extension<HandlerContext>,
273) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
274    let (_entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
275
276    // Get primary key field (default to "id")
277    let primary_key = "id";
278
279    // Build SELECT query
280    let query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
281    let params = vec![Value::String(id.clone())];
282
283    // Execute query
284    let results = context.database.query(&query, &params).await.map_err(|e| {
285        (
286            StatusCode::INTERNAL_SERVER_ERROR,
287            Json(json!({"error": format!("Database query failed: {}", e)})),
288        )
289    })?;
290
291    // Return first result or 404
292    if let Some(result) = results.into_iter().next() {
293        Ok(Json(Value::Object(
294            result.into_iter().collect::<serde_json::Map<String, Value>>(),
295        )))
296    } else {
297        Err((
298            StatusCode::NOT_FOUND,
299            Json(json!({
300                "error": format!("{} with id '{}' not found", entity_name, id)
301            })),
302        ))
303    }
304}
305
306/// Create entity (POST /api/{entity})
307pub async fn create_handler(
308    Path(entity_name): Path<String>,
309    Extension(context): Extension<HandlerContext>,
310    Json(mut body): Json<Value>,
311) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
312    let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
313
314    // Apply auto-generation rules
315    apply_auto_generation(&mut body, &entity.schema, &entity_name, context.database.as_ref())
316        .await
317        .map_err(|e| {
318            (
319                StatusCode::INTERNAL_SERVER_ERROR,
320                Json(json!({"error": format!("Auto-generation failed: {}", e)})),
321            )
322        })?;
323
324    // Validate foreign key constraints
325    let validator = ConstraintValidator;
326    if let Value::Object(obj) = &body {
327        for fk in &entity.schema.foreign_keys {
328            if let Some(fk_value) = obj.get(&fk.field) {
329                validator
330                    .validate_foreign_key(
331                        context.database.as_ref(),
332                        table_name,
333                        &fk.field,
334                        fk_value,
335                        &(fk.target_entity.to_lowercase() + "s"),
336                        &fk.target_field,
337                    )
338                    .await
339                    .map_err(|e| {
340                        (StatusCode::BAD_REQUEST, Json(json!({"error": e.to_string()})))
341                    })?;
342            }
343        }
344    }
345
346    // Build INSERT query
347    if let Value::Object(obj) = &body {
348        // Validate all field names are safe SQL identifiers (SQL injection prevention)
349        let mut validated_fields: Vec<String> = Vec::new();
350        let mut values: Vec<Value> = Vec::new();
351
352        for (field_name, value) in obj.iter() {
353            // Validate field name is safe
354            if !is_safe_sql_identifier(field_name) {
355                return Err((
356                    StatusCode::BAD_REQUEST,
357                    Json(json!({
358                        "error": format!("Invalid field name: '{}'. Field names must contain only alphanumeric characters and underscores.", field_name)
359                    })),
360                ));
361            }
362            validated_fields.push(field_name.clone());
363            values.push(value.clone());
364        }
365
366        let placeholders: Vec<String> =
367            (0..validated_fields.len()).map(|_| "?".to_string()).collect();
368
369        let query = format!(
370            "INSERT INTO {} ({}) VALUES ({})",
371            table_name,
372            validated_fields.join(", "),
373            placeholders.join(", ")
374        );
375
376        // Execute insert
377        let inserted_id = context.database.execute_with_id(&query, &values).await.map_err(|e| {
378            (
379                StatusCode::INTERNAL_SERVER_ERROR,
380                Json(json!({"error": format!("Insert failed: {}", e)})),
381            )
382        })?;
383
384        // Fetch the created record
385        let primary_key = "id";
386        let select_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
387        let select_results = context
388            .database
389            .query(&select_query, &[Value::String(inserted_id)])
390            .await
391            .map_err(|e| {
392                (
393                    StatusCode::INTERNAL_SERVER_ERROR,
394                    Json(json!({"error": format!("Failed to fetch created record: {}", e)})),
395                )
396            })?;
397
398        if let Some(result) = select_results.into_iter().next() {
399            Ok(Json(Value::Object(
400                result.into_iter().collect::<serde_json::Map<String, Value>>(),
401            )))
402        } else {
403            Err((
404                StatusCode::INTERNAL_SERVER_ERROR,
405                Json(json!({"error": "Failed to retrieve created record"})),
406            ))
407        }
408    } else {
409        Err((
410            StatusCode::BAD_REQUEST,
411            Json(json!({"error": "Request body must be a JSON object"})),
412        ))
413    }
414}
415
416/// Update entity (PUT /api/{entity}/{id})
417pub async fn update_handler(
418    Path((entity_name, id)): Path<(String, String)>,
419    Extension(context): Extension<HandlerContext>,
420    Json(body): Json<Value>,
421) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
422    let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
423    let primary_key = "id";
424
425    // Check if record exists
426    let check_query =
427        format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
428    let check_results = context
429        .database
430        .query(&check_query, &[Value::String(id.clone())])
431        .await
432        .map_err(|e| {
433            (
434                StatusCode::INTERNAL_SERVER_ERROR,
435                Json(json!({"error": format!("Database query failed: {}", e)})),
436            )
437        })?;
438
439    let exists = check_results
440        .first()
441        .and_then(|r| r.get("count"))
442        .and_then(|v| v.as_u64())
443        .map(|v| v > 0)
444        .unwrap_or(false);
445
446    if !exists {
447        return Err((
448            StatusCode::NOT_FOUND,
449            Json(json!({
450                "error": format!("{} with id '{}' not found", entity_name, id)
451            })),
452        ));
453    }
454
455    // Validate foreign key constraints
456    let validator = ConstraintValidator;
457    if let Value::Object(obj) = &body {
458        for fk in &entity.schema.foreign_keys {
459            if let Some(fk_value) = obj.get(&fk.field) {
460                validator
461                    .validate_foreign_key(
462                        context.database.as_ref(),
463                        table_name,
464                        &fk.field,
465                        fk_value,
466                        &(fk.target_entity.to_lowercase() + "s"),
467                        &fk.target_field,
468                    )
469                    .await
470                    .map_err(|e| {
471                        (StatusCode::BAD_REQUEST, Json(json!({"error": e.to_string()})))
472                    })?;
473            }
474        }
475    }
476
477    // Build UPDATE query
478    if let Value::Object(obj) = &body {
479        let mut set_clauses = Vec::new();
480        let mut values = Vec::new();
481
482        for (field, value) in obj.iter() {
483            if field != primary_key {
484                // Validate field name is safe (SQL injection prevention)
485                if !is_safe_sql_identifier(field) {
486                    return Err((
487                        StatusCode::BAD_REQUEST,
488                        Json(json!({
489                            "error": format!("Invalid field name: '{}'. Field names must contain only alphanumeric characters and underscores.", field)
490                        })),
491                    ));
492                }
493                set_clauses.push(format!("{} = ?", field));
494                values.push(value.clone());
495            }
496        }
497
498        if set_clauses.is_empty() {
499            return Err((StatusCode::BAD_REQUEST, Json(json!({"error": "No fields to update"}))));
500        }
501
502        values.push(Value::String(id.clone()));
503
504        let query = format!(
505            "UPDATE {} SET {} WHERE {} = ?",
506            table_name,
507            set_clauses.join(", "),
508            primary_key
509        );
510
511        // Execute update
512        context.database.execute(&query, &values).await.map_err(|e| {
513            (
514                StatusCode::INTERNAL_SERVER_ERROR,
515                Json(json!({"error": format!("Update failed: {}", e)})),
516            )
517        })?;
518
519        // Fetch updated record
520        let select_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
521        let select_results =
522            context.database.query(&select_query, &[Value::String(id)]).await.map_err(|e| {
523                (
524                    StatusCode::INTERNAL_SERVER_ERROR,
525                    Json(json!({"error": format!("Failed to fetch updated record: {}", e)})),
526                )
527            })?;
528
529        if let Some(result) = select_results.into_iter().next() {
530            Ok(Json(Value::Object(
531                result.into_iter().collect::<serde_json::Map<String, Value>>(),
532            )))
533        } else {
534            Err((
535                StatusCode::INTERNAL_SERVER_ERROR,
536                Json(json!({"error": "Failed to retrieve updated record"})),
537            ))
538        }
539    } else {
540        Err((
541            StatusCode::BAD_REQUEST,
542            Json(json!({"error": "Request body must be a JSON object"})),
543        ))
544    }
545}
546
547/// Partial update entity (PATCH /api/{entity}/{id})
548pub async fn patch_handler(
549    Path((entity_name, id)): Path<(String, String)>,
550    Extension(context): Extension<HandlerContext>,
551    Json(body): Json<Value>,
552) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
553    // PATCH is similar to PUT but only updates provided fields
554    // For now, we'll use the same logic as PUT
555    // In a full implementation, we'd fetch the existing record first and merge
556    update_handler(Path((entity_name.clone(), id.clone())), Extension(context), Json(body)).await
557}
558
559/// Delete entity (DELETE /api/{entity}/{id})
560pub async fn delete_handler(
561    Path((entity_name, id)): Path<(String, String)>,
562    Extension(context): Extension<HandlerContext>,
563) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
564    let (_entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
565    let primary_key = "id";
566
567    // Check if record exists
568    let check_query =
569        format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
570    let check_results = context
571        .database
572        .query(&check_query, &[Value::String(id.clone())])
573        .await
574        .map_err(|e| {
575            (
576                StatusCode::INTERNAL_SERVER_ERROR,
577                Json(json!({"error": format!("Database query failed: {}", e)})),
578            )
579        })?;
580
581    let exists = check_results
582        .first()
583        .and_then(|r| r.get("count"))
584        .and_then(|v| v.as_u64())
585        .map(|v| v > 0)
586        .unwrap_or(false);
587
588    if !exists {
589        return Err((
590            StatusCode::NOT_FOUND,
591            Json(json!({
592                "error": format!("{} with id '{}' not found", entity_name, id)
593            })),
594        ));
595    }
596
597    // Build DELETE query
598    let query = format!("DELETE FROM {} WHERE {} = ?", table_name, primary_key);
599    let params = vec![Value::String(id.clone())];
600
601    // Execute delete
602    let rows_affected = context.database.execute(&query, &params).await.map_err(|e| {
603        (
604            StatusCode::INTERNAL_SERVER_ERROR,
605            Json(json!({"error": format!("Delete failed: {}", e)})),
606        )
607    })?;
608
609    if rows_affected > 0 {
610        Ok(Json(json!({
611            "message": format!("{} with id '{}' deleted successfully", entity_name, id),
612            "id": id
613        })))
614    } else {
615        Err((
616            StatusCode::NOT_FOUND,
617            Json(json!({
618                "error": format!("{} with id '{}' not found", entity_name, id)
619            })),
620        ))
621    }
622}
623
624/// Get related entities (GET /api/{entity}/{id}/{relationship})
625///
626/// This handler supports relationship traversal:
627/// - Forward relationships: Get child entities (one-to-many)
628///   Example: GET /api/users/123/orders -> Get all orders where user_id = 123
629/// - Reverse relationships: Get parent entity (many-to-one)
630///   Example: GET /api/orders/456/user -> Get the user for order 456
631pub async fn get_relationship_handler(
632    Path((entity_name, id, relationship_name)): Path<(String, String, String)>,
633    Query(params): Query<HashMap<String, String>>,
634    Extension(context): Extension<HandlerContext>,
635) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
636    let (entity, table_name) = get_entity_info(&context.registry, &entity_name)?;
637    let primary_key = "id";
638
639    // First, verify the parent entity exists
640    let check_query =
641        format!("SELECT COUNT(*) as count FROM {} WHERE {} = ?", table_name, primary_key);
642    let check_results = context
643        .database
644        .query(&check_query, &[Value::String(id.clone())])
645        .await
646        .map_err(|e| {
647            (
648                StatusCode::INTERNAL_SERVER_ERROR,
649                Json(json!({"error": format!("Database query failed: {}", e)})),
650            )
651        })?;
652
653    let exists = check_results
654        .first()
655        .and_then(|r| r.get("count"))
656        .and_then(|v| v.as_u64())
657        .map(|v| v > 0)
658        .unwrap_or(false);
659
660    if !exists {
661        return Err((
662            StatusCode::NOT_FOUND,
663            Json(json!({
664                "error": format!("{} with id '{}' not found", entity_name, id)
665            })),
666        ));
667    }
668
669    // Find the relationship definition
670    // Strategy 1: Forward relationship (one-to-many)
671    // Example: GET /api/users/123/orders
672    // - Look for an entity named "orders" that has a FK pointing to "users"
673    if let Some(target_entity) = context.registry.get(&relationship_name) {
674        // Check if this target entity has a FK pointing to the current entity
675        if let Some(fk) = target_entity
676            .schema
677            .foreign_keys
678            .iter()
679            .find(|fk| fk.target_entity == entity_name)
680        {
681            // Forward relationship: Get child entities
682            // Example: GET /api/users/123/orders -> Get orders where user_id = 123
683            let target_table = target_entity.table_name();
684
685            // Build query to get related entities
686            let (where_clause, mut bind_values) = build_where_clause(&params, target_entity)?;
687            let order_by = build_order_by(&params, target_entity);
688            let (limit, offset) = get_pagination(&params);
689
690            // Add the foreign key condition
691            let fk_condition = if where_clause.is_empty() {
692                format!("WHERE {} = ?", fk.field)
693            } else {
694                format!("{} AND {} = ?", where_clause, fk.field)
695            };
696            bind_values.push(Value::String(id.clone()));
697
698            let mut query = format!("SELECT * FROM {} {}", target_table, fk_condition);
699            if !order_by.is_empty() {
700                query.push_str(&format!(" {}", order_by));
701            }
702            if let Some(limit_val) = limit {
703                query.push_str(&format!(" LIMIT {}", limit_val));
704            }
705            if let Some(offset_val) = offset {
706                query.push_str(&format!(" OFFSET {}", offset_val));
707            }
708
709            let results = context.database.query(&query, &bind_values).await.map_err(|e| {
710                (
711                    StatusCode::INTERNAL_SERVER_ERROR,
712                    Json(json!({"error": format!("Database query failed: {}", e)})),
713                )
714            })?;
715
716            // Get total count
717            let count_query =
718                format!("SELECT COUNT(*) as total FROM {} {}", target_table, fk_condition);
719            let count_results =
720                context.database.query(&count_query, &bind_values).await.map_err(|e| {
721                    (
722                        StatusCode::INTERNAL_SERVER_ERROR,
723                        Json(json!({"error": format!("Count query failed: {}", e)})),
724                    )
725                })?;
726
727            let total = count_results
728                .first()
729                .and_then(|r| r.get("total"))
730                .and_then(|v| v.as_u64())
731                .unwrap_or(0);
732
733            return Ok(Json(json!({
734                "data": results,
735                "total": total,
736                "relationship": relationship_name,
737                "parent_entity": entity_name,
738                "parent_id": id
739            })));
740        }
741    }
742
743    // Strategy 2: Reverse relationship (many-to-one)
744    // Example: GET /api/orders/456/user
745    // - The current entity (orders) has a FK field pointing to "user" entity
746    // Try reverse relationship: Find if current entity has a FK pointing to the relationship
747    // Example: GET /api/orders/456/user -> orders table has user_id field pointing to users
748    if let Some(fk) = entity.schema.foreign_keys.iter().find(|fk| {
749        // Relationship name might match the target entity or the FK field
750        fk.target_entity.to_lowercase() == relationship_name.to_lowercase()
751            || fk.field == relationship_name
752            || fk.field == format!("{}_id", relationship_name)
753    }) {
754        // Reverse relationship: Get the parent entity
755        // Get the current entity record to find the FK value
756        let current_query = format!("SELECT * FROM {} WHERE {} = ?", table_name, primary_key);
757        let current_results = context
758            .database
759            .query(&current_query, &[Value::String(id.clone())])
760            .await
761            .map_err(|e| {
762                (
763                    StatusCode::INTERNAL_SERVER_ERROR,
764                    Json(json!({"error": format!("Database query failed: {}", e)})),
765                )
766            })?;
767
768        if let Some(current_record) = current_results.into_iter().next() {
769            // Find the FK value
770            let fk_value = current_record.get(&fk.field).ok_or_else(|| {
771                (
772                    StatusCode::BAD_REQUEST,
773                    Json(json!({
774                        "error": format!("Foreign key field '{}' not found in record", fk.field)
775                    })),
776                )
777            })?;
778
779            // Get the target entity
780            let target_entity = context.registry.get(&fk.target_entity).ok_or_else(|| {
781                (
782                    StatusCode::NOT_FOUND,
783                    Json(json!({
784                        "error": format!("Target entity '{}' not found", fk.target_entity)
785                    })),
786                )
787            })?;
788
789            let target_table = target_entity.table_name();
790            let target_primary_key = "id";
791
792            // Query the target entity
793            let target_query =
794                format!("SELECT * FROM {} WHERE {} = ?", target_table, target_primary_key);
795            let target_results =
796                context.database.query(&target_query, &[fk_value.clone()]).await.map_err(|e| {
797                    (
798                        StatusCode::INTERNAL_SERVER_ERROR,
799                        Json(json!({"error": format!("Database query failed: {}", e)})),
800                    )
801                })?;
802
803            if let Some(target_record) = target_results.into_iter().next() {
804                return Ok(Json(Value::Object(
805                    target_record.into_iter().collect::<serde_json::Map<String, Value>>(),
806                )));
807            } else {
808                return Err((
809                    StatusCode::NOT_FOUND,
810                    Json(json!({
811                        "error": format!("Related {} not found", relationship_name)
812                    })),
813                ));
814            }
815        } else {
816            return Err((
817                StatusCode::NOT_FOUND,
818                Json(json!({
819                    "error": format!("{} with id '{}' not found", entity_name, id)
820                })),
821            ));
822        }
823    }
824
825    // Strategy 3: Many-to-many relationship
826    // Example: GET /api/users/123/roles -> Get all roles for user 123 via user_roles junction table
827    // Check if there's a many-to-many relationship between current entity and relationship name
828    for m2m in &entity.schema.many_to_many {
829        // Check if relationship_name matches entity_a or entity_b
830        let is_entity_a = m2m.entity_a.to_lowercase() == relationship_name.to_lowercase();
831        let is_entity_b = m2m.entity_b.to_lowercase() == relationship_name.to_lowercase();
832
833        if is_entity_a || is_entity_b {
834            // Get the target entity
835            let target_entity_name = if is_entity_a {
836                &m2m.entity_a
837            } else {
838                &m2m.entity_b
839            };
840
841            let target_entity = context.registry.get(target_entity_name).ok_or_else(|| {
842                (
843                    StatusCode::NOT_FOUND,
844                    Json(json!({
845                        "error": format!("Target entity '{}' not found", target_entity_name)
846                    })),
847                )
848            })?;
849
850            let junction_table = m2m.junction_table.as_ref().ok_or_else(|| {
851                (
852                    StatusCode::INTERNAL_SERVER_ERROR,
853                    Json(json!({
854                        "error": "Junction table name not specified for many-to-many relationship"
855                    })),
856                )
857            })?;
858
859            // Determine which field to use based on which entity we're querying from
860            let fk_field = if entity_name.to_lowercase() == m2m.entity_a.to_lowercase() {
861                &m2m.entity_a_field
862            } else {
863                &m2m.entity_b_field
864            };
865
866            let target_fk_field = if is_entity_a {
867                &m2m.entity_b_field
868            } else {
869                &m2m.entity_a_field
870            };
871
872            let target_table = target_entity.table_name();
873
874            // Build query with JOIN through junction table
875            let (where_clause, mut bind_values) = build_where_clause(&params, target_entity)?;
876            let order_by = build_order_by(&params, target_entity);
877            let (limit, offset) = get_pagination(&params);
878
879            // Build JOIN query
880            let mut query = format!(
881                "SELECT t.* FROM {} t INNER JOIN {} j ON t.id = j.{} WHERE j.{} = ?",
882                target_table, junction_table, target_fk_field, fk_field
883            );
884
885            bind_values.push(Value::String(id.clone()));
886
887            if !where_clause.is_empty() {
888                query.push_str(&format!(" AND {}", where_clause));
889            }
890
891            if !order_by.is_empty() {
892                query.push_str(&format!(" {}", order_by));
893            }
894
895            if let Some(limit_val) = limit {
896                query.push_str(&format!(" LIMIT {}", limit_val));
897            }
898
899            if let Some(offset_val) = offset {
900                query.push_str(&format!(" OFFSET {}", offset_val));
901            }
902
903            let results = context.database.query(&query, &bind_values).await.map_err(|e| {
904                (
905                    StatusCode::INTERNAL_SERVER_ERROR,
906                    Json(json!({"error": format!("Database query failed: {}", e)})),
907                )
908            })?;
909
910            // Get total count
911            let count_query = format!(
912                "SELECT COUNT(*) as total FROM {} t INNER JOIN {} j ON t.id = j.{} WHERE j.{} = ?",
913                target_table, junction_table, target_fk_field, fk_field
914            );
915            let count_results =
916                context.database.query(&count_query, &bind_values).await.map_err(|e| {
917                    (
918                        StatusCode::INTERNAL_SERVER_ERROR,
919                        Json(json!({"error": format!("Count query failed: {}", e)})),
920                    )
921                })?;
922
923            let total = count_results
924                .first()
925                .and_then(|r| r.get("total"))
926                .and_then(|v| v.as_u64())
927                .unwrap_or(0);
928
929            return Ok(Json(json!({
930                "data": results,
931                "total": total,
932                "relationship": relationship_name,
933                "parent_entity": entity_name,
934                "parent_id": id,
935                "relationship_type": "many_to_many"
936            })));
937        }
938    }
939
940    // No relationship found - return error
941    Err((
942        StatusCode::NOT_FOUND,
943        Json(json!({
944            "error": format!("Relationship '{}' not found for entity '{}'", relationship_name, entity_name)
945        })),
946    ))
947}
948
949/// Create snapshot (POST /vbr-api/snapshots)
950#[derive(serde::Deserialize)]
951pub struct CreateSnapshotRequest {
952    pub name: String,
953    pub description: Option<String>,
954}
955
956pub async fn create_snapshot_handler(
957    Extension(context): Extension<HandlerContext>,
958    Json(body): Json<CreateSnapshotRequest>,
959) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
960    let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
961        (
962            StatusCode::BAD_REQUEST,
963            Json(json!({"error": "Snapshots directory not configured"})),
964        )
965    })?;
966
967    let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
968    let metadata = manager
969        .create_snapshot(&body.name, body.description, context.database.as_ref(), &context.registry)
970        .await
971        .map_err(|e| {
972            (
973                StatusCode::INTERNAL_SERVER_ERROR,
974                Json(json!({"error": format!("Failed to create snapshot: {}", e)})),
975            )
976        })?;
977
978    Ok(Json(serde_json::to_value(&metadata).unwrap()))
979}
980
981/// List snapshots (GET /vbr-api/snapshots)
982pub async fn list_snapshots_handler(
983    Extension(context): Extension<HandlerContext>,
984) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
985    let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
986        (
987            StatusCode::BAD_REQUEST,
988            Json(json!({"error": "Snapshots directory not configured"})),
989        )
990    })?;
991
992    let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
993    let snapshots = manager.list_snapshots().await.map_err(|e| {
994        (
995            StatusCode::INTERNAL_SERVER_ERROR,
996            Json(json!({"error": format!("Failed to list snapshots: {}", e)})),
997        )
998    })?;
999
1000    Ok(Json(serde_json::to_value(&snapshots).unwrap()))
1001}
1002
1003/// Restore snapshot (POST /vbr-api/snapshots/{name}/restore)
1004pub async fn restore_snapshot_handler(
1005    Path(name): Path<String>,
1006    Extension(context): Extension<HandlerContext>,
1007) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
1008    let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
1009        (
1010            StatusCode::BAD_REQUEST,
1011            Json(json!({"error": "Snapshots directory not configured"})),
1012        )
1013    })?;
1014
1015    let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
1016    manager
1017        .restore_snapshot(&name, context.database.as_ref(), &context.registry)
1018        .await
1019        .map_err(|e| {
1020            (
1021                StatusCode::INTERNAL_SERVER_ERROR,
1022                Json(json!({"error": format!("Failed to restore snapshot: {}", e)})),
1023            )
1024        })?;
1025
1026    Ok(Json(json!({"message": format!("Snapshot '{}' restored successfully", name)})))
1027}
1028
1029/// Delete snapshot (DELETE /vbr-api/snapshots/{name})
1030pub async fn delete_snapshot_handler(
1031    Path(name): Path<String>,
1032    Extension(context): Extension<HandlerContext>,
1033) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
1034    let snapshots_dir = context.snapshots_dir.as_ref().ok_or_else(|| {
1035        (
1036            StatusCode::BAD_REQUEST,
1037            Json(json!({"error": "Snapshots directory not configured"})),
1038        )
1039    })?;
1040
1041    let manager = crate::snapshots::SnapshotManager::new(snapshots_dir);
1042    manager.delete_snapshot(&name).await.map_err(|e| {
1043        (
1044            StatusCode::INTERNAL_SERVER_ERROR,
1045            Json(json!({"error": format!("Failed to delete snapshot: {}", e)})),
1046        )
1047    })?;
1048
1049    Ok(Json(json!({"message": format!("Snapshot '{}' deleted successfully", name)})))
1050}
1051
1052/// Reset database (POST /vbr-api/reset)
1053pub async fn reset_handler(
1054    Extension(context): Extension<HandlerContext>,
1055) -> std::result::Result<Json<Value>, (StatusCode, Json<Value>)> {
1056    crate::snapshots::reset_database(context.database.as_ref(), &context.registry)
1057        .await
1058        .map_err(|e| {
1059            (
1060                StatusCode::INTERNAL_SERVER_ERROR,
1061                Json(json!({"error": format!("Failed to reset database: {}", e)})),
1062            )
1063        })?;
1064
1065    Ok(Json(json!({"message": "Database reset successfully"})))
1066}
1067
1068#[cfg(test)]
1069mod tests {
1070    use super::*;
1071    use crate::database::{InMemoryDatabase, VirtualDatabase};
1072    use crate::entities::{Entity, EntityRegistry};
1073    use crate::migration::MigrationManager;
1074    use crate::schema::VbrSchemaDefinition;
1075    use mockforge_data::{FieldDefinition, SchemaDefinition};
1076    use std::sync::Arc;
1077
1078    async fn setup_test_database() -> (Arc<dyn VirtualDatabase + Send + Sync>, EntityRegistry) {
1079        let mut db = InMemoryDatabase::new().await.unwrap();
1080        db.initialize().await.unwrap();
1081        let registry = EntityRegistry::new();
1082        (Arc::new(db), registry)
1083    }
1084
1085    async fn create_test_entity(
1086        database: &dyn VirtualDatabase,
1087        registry: &mut EntityRegistry,
1088        entity_name: &str,
1089    ) {
1090        let base_schema = SchemaDefinition::new(entity_name.to_string())
1091            .with_field(FieldDefinition::new("id".to_string(), "string".to_string()))
1092            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1093
1094        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1095        let entity = Entity::new(entity_name.to_string(), vbr_schema);
1096
1097        let manager = MigrationManager::new();
1098        let create_sql = manager.generate_create_table(&entity).unwrap();
1099        database.create_table(&create_sql).await.unwrap();
1100
1101        registry.register(entity).unwrap();
1102    }
1103
1104    fn create_test_context(
1105        database: Arc<dyn VirtualDatabase + Send + Sync>,
1106        registry: EntityRegistry,
1107    ) -> HandlerContext {
1108        HandlerContext {
1109            database,
1110            registry,
1111            session_manager: None,
1112            snapshots_dir: None,
1113        }
1114    }
1115
1116    // SQL identifier validation tests
1117    #[test]
1118    fn test_is_safe_sql_identifier_valid() {
1119        assert!(is_safe_sql_identifier("name"));
1120        assert!(is_safe_sql_identifier("user_id"));
1121        assert!(is_safe_sql_identifier("firstName"));
1122        assert!(is_safe_sql_identifier("field123"));
1123        assert!(is_safe_sql_identifier("a"));
1124    }
1125
1126    #[test]
1127    fn test_is_safe_sql_identifier_invalid() {
1128        assert!(!is_safe_sql_identifier("")); // Empty
1129        assert!(!is_safe_sql_identifier("123abc")); // Starts with digit
1130        assert!(!is_safe_sql_identifier("field-name")); // Contains hyphen
1131        assert!(!is_safe_sql_identifier("field.name")); // Contains dot
1132        assert!(!is_safe_sql_identifier("field; DROP TABLE users")); // SQL injection
1133        assert!(!is_safe_sql_identifier("field'name")); // Contains quote
1134        assert!(!is_safe_sql_identifier("field\"name")); // Contains double quote
1135        assert!(!is_safe_sql_identifier("field name")); // Contains space
1136        assert!(!is_safe_sql_identifier(&"a".repeat(65))); // Too long
1137    }
1138
1139    // Helper function tests
1140    #[test]
1141    fn test_build_where_clause_empty() {
1142        let params = HashMap::new();
1143        let base_schema = SchemaDefinition::new("Test".to_string());
1144        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1145        let entity = Entity::new("Test".to_string(), vbr_schema);
1146
1147        let result = build_where_clause(&params, &entity);
1148        assert!(result.is_ok());
1149        let (where_clause, bind_values) = result.unwrap();
1150        assert_eq!(where_clause, "");
1151        assert_eq!(bind_values.len(), 0);
1152    }
1153
1154    #[test]
1155    fn test_build_where_clause_with_params() {
1156        let mut params = HashMap::new();
1157        params.insert("name".to_string(), "John".to_string());
1158
1159        let base_schema = SchemaDefinition::new("User".to_string())
1160            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1161        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1162        let entity = Entity::new("User".to_string(), vbr_schema);
1163
1164        let result = build_where_clause(&params, &entity);
1165        assert!(result.is_ok());
1166        let (where_clause, bind_values) = result.unwrap();
1167        assert!(where_clause.contains("WHERE"));
1168        assert!(where_clause.contains("name = ?"));
1169        assert_eq!(bind_values.len(), 1);
1170    }
1171
1172    #[test]
1173    fn test_build_where_clause_ignores_pagination() {
1174        let mut params = HashMap::new();
1175        params.insert("limit".to_string(), "10".to_string());
1176        params.insert("offset".to_string(), "5".to_string());
1177        params.insert("sort".to_string(), "name".to_string());
1178
1179        let base_schema = SchemaDefinition::new("Test".to_string());
1180        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1181        let entity = Entity::new("Test".to_string(), vbr_schema);
1182
1183        let result = build_where_clause(&params, &entity);
1184        assert!(result.is_ok());
1185        let (where_clause, bind_values) = result.unwrap();
1186        assert_eq!(where_clause, "");
1187        assert_eq!(bind_values.len(), 0);
1188    }
1189
1190    #[test]
1191    fn test_build_where_clause_ignores_invalid_field_names() {
1192        let mut params = HashMap::new();
1193        params.insert("valid_field".to_string(), "value".to_string());
1194        params.insert("invalid-field".to_string(), "value".to_string()); // Invalid: contains hyphen
1195        params.insert("name; DROP TABLE".to_string(), "value".to_string()); // SQL injection attempt
1196
1197        let base_schema = SchemaDefinition::new("Test".to_string())
1198            .with_field(FieldDefinition::new("valid_field".to_string(), "string".to_string()));
1199        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1200        let entity = Entity::new("Test".to_string(), vbr_schema);
1201
1202        let result = build_where_clause(&params, &entity);
1203        assert!(result.is_ok());
1204        let (where_clause, bind_values) = result.unwrap();
1205        // Only valid_field should be included
1206        assert!(where_clause.contains("valid_field = ?"));
1207        assert!(!where_clause.contains("invalid-field"));
1208        assert!(!where_clause.contains("DROP"));
1209        assert_eq!(bind_values.len(), 1);
1210    }
1211
1212    #[test]
1213    fn test_build_order_by_no_sort() {
1214        let params = HashMap::new();
1215        let base_schema = SchemaDefinition::new("Test".to_string());
1216        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1217        let entity = Entity::new("Test".to_string(), vbr_schema);
1218
1219        let order_by = build_order_by(&params, &entity);
1220        assert_eq!(order_by, "");
1221    }
1222
1223    #[test]
1224    fn test_build_order_by_with_sort() {
1225        let mut params = HashMap::new();
1226        params.insert("sort".to_string(), "name".to_string());
1227
1228        let base_schema = SchemaDefinition::new("User".to_string())
1229            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1230        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1231        let entity = Entity::new("User".to_string(), vbr_schema);
1232
1233        let order_by = build_order_by(&params, &entity);
1234        assert_eq!(order_by, "ORDER BY name ASC");
1235    }
1236
1237    #[test]
1238    fn test_build_order_by_with_sort_desc() {
1239        let mut params = HashMap::new();
1240        params.insert("sort".to_string(), "name".to_string());
1241        params.insert("order".to_string(), "DESC".to_string());
1242
1243        let base_schema = SchemaDefinition::new("User".to_string())
1244            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1245        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1246        let entity = Entity::new("User".to_string(), vbr_schema);
1247
1248        let order_by = build_order_by(&params, &entity);
1249        assert_eq!(order_by, "ORDER BY name DESC");
1250    }
1251
1252    #[test]
1253    fn test_build_order_by_invalid_field() {
1254        let mut params = HashMap::new();
1255        params.insert("sort".to_string(), "invalid_field".to_string());
1256
1257        let base_schema = SchemaDefinition::new("User".to_string())
1258            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1259        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1260        let entity = Entity::new("User".to_string(), vbr_schema);
1261
1262        let order_by = build_order_by(&params, &entity);
1263        assert_eq!(order_by, "");
1264    }
1265
1266    #[test]
1267    fn test_build_order_by_invalid_order() {
1268        let mut params = HashMap::new();
1269        params.insert("sort".to_string(), "name".to_string());
1270        params.insert("order".to_string(), "INVALID".to_string());
1271
1272        let base_schema = SchemaDefinition::new("User".to_string())
1273            .with_field(FieldDefinition::new("name".to_string(), "string".to_string()));
1274        let vbr_schema = VbrSchemaDefinition::new(base_schema);
1275        let entity = Entity::new("User".to_string(), vbr_schema);
1276
1277        let order_by = build_order_by(&params, &entity);
1278        assert_eq!(order_by, "");
1279    }
1280
1281    #[test]
1282    fn test_get_pagination_no_params() {
1283        let params = HashMap::new();
1284        let (limit, offset) = get_pagination(&params);
1285        assert_eq!(limit, None);
1286        assert_eq!(offset, None);
1287    }
1288
1289    #[test]
1290    fn test_get_pagination_with_limit() {
1291        let mut params = HashMap::new();
1292        params.insert("limit".to_string(), "10".to_string());
1293
1294        let (limit, offset) = get_pagination(&params);
1295        assert_eq!(limit, Some(10));
1296        assert_eq!(offset, None);
1297    }
1298
1299    #[test]
1300    fn test_get_pagination_with_limit_and_offset() {
1301        let mut params = HashMap::new();
1302        params.insert("limit".to_string(), "20".to_string());
1303        params.insert("offset".to_string(), "5".to_string());
1304
1305        let (limit, offset) = get_pagination(&params);
1306        assert_eq!(limit, Some(20));
1307        assert_eq!(offset, Some(5));
1308    }
1309
1310    #[test]
1311    fn test_get_pagination_invalid_values() {
1312        let mut params = HashMap::new();
1313        params.insert("limit".to_string(), "abc".to_string());
1314        params.insert("offset".to_string(), "xyz".to_string());
1315
1316        let (limit, offset) = get_pagination(&params);
1317        assert_eq!(limit, None);
1318        assert_eq!(offset, None);
1319    }
1320
1321    // HandlerContext tests
1322    #[test]
1323    fn test_handler_context_clone() {
1324        let (database, registry) = tokio::runtime::Runtime::new()
1325            .unwrap()
1326            .block_on(async { setup_test_database().await });
1327
1328        let context = create_test_context(database, registry);
1329        let cloned = context.clone();
1330
1331        assert!(Arc::ptr_eq(&context.database, &cloned.database));
1332    }
1333
1334    // get_entity_info tests
1335    #[tokio::test]
1336    async fn test_get_entity_info_success() {
1337        let (database, mut registry) = setup_test_database().await;
1338        create_test_entity(database.as_ref(), &mut registry, "User").await;
1339
1340        let result = get_entity_info(&registry, "User");
1341        assert!(result.is_ok());
1342        let (entity, table_name) = result.unwrap();
1343        assert_eq!(entity.name(), "User");
1344        assert_eq!(table_name, "users");
1345    }
1346
1347    #[tokio::test]
1348    async fn test_get_entity_info_not_found() {
1349        let (_database, registry) = setup_test_database().await;
1350
1351        let result = get_entity_info(&registry, "NonExistent");
1352        assert!(result.is_err());
1353        let (status, _) = result.unwrap_err();
1354        assert_eq!(status, StatusCode::NOT_FOUND);
1355    }
1356
1357    // apply_auto_generation tests
1358    #[tokio::test]
1359    async fn test_apply_auto_generation_uuid() {
1360        let (database, _registry) = setup_test_database().await;
1361        let mut data = json!({});
1362        let base_schema = SchemaDefinition::new("Test".to_string());
1363        let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1364        vbr_schema
1365            .auto_generation
1366            .insert("id".to_string(), crate::schema::AutoGenerationRule::Uuid);
1367
1368        apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1369            .await
1370            .unwrap();
1371
1372        assert!(data.as_object().unwrap().contains_key("id"));
1373        assert!(data["id"].is_string());
1374    }
1375
1376    #[tokio::test]
1377    async fn test_apply_auto_generation_timestamp() {
1378        let (database, _registry) = setup_test_database().await;
1379        let mut data = json!({});
1380        let base_schema = SchemaDefinition::new("Test".to_string());
1381        let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1382        vbr_schema
1383            .auto_generation
1384            .insert("created_at".to_string(), crate::schema::AutoGenerationRule::Timestamp);
1385
1386        apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1387            .await
1388            .unwrap();
1389
1390        assert!(data.as_object().unwrap().contains_key("created_at"));
1391        assert!(data["created_at"].is_string());
1392    }
1393
1394    #[tokio::test]
1395    async fn test_apply_auto_generation_date() {
1396        let (database, _registry) = setup_test_database().await;
1397        let mut data = json!({});
1398        let base_schema = SchemaDefinition::new("Test".to_string());
1399        let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1400        vbr_schema
1401            .auto_generation
1402            .insert("date_field".to_string(), crate::schema::AutoGenerationRule::Date);
1403
1404        apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1405            .await
1406            .unwrap();
1407
1408        assert!(data.as_object().unwrap().contains_key("date_field"));
1409        assert!(data["date_field"].is_string());
1410    }
1411
1412    #[tokio::test]
1413    async fn test_apply_auto_generation_skips_existing() {
1414        let (database, _registry) = setup_test_database().await;
1415        let mut data = json!({"id": "existing-id"});
1416        let base_schema = SchemaDefinition::new("Test".to_string());
1417        let mut vbr_schema = VbrSchemaDefinition::new(base_schema);
1418        vbr_schema
1419            .auto_generation
1420            .insert("id".to_string(), crate::schema::AutoGenerationRule::Uuid);
1421
1422        apply_auto_generation(&mut data, &vbr_schema, "Test", database.as_ref())
1423            .await
1424            .unwrap();
1425
1426        assert_eq!(data["id"], "existing-id");
1427    }
1428
1429    // CreateSnapshotRequest tests
1430    #[test]
1431    fn test_create_snapshot_request_deserialize() {
1432        let json = r#"{"name": "test-snapshot", "description": "Test description"}"#;
1433        let request: CreateSnapshotRequest = serde_json::from_str(json).unwrap();
1434        assert_eq!(request.name, "test-snapshot");
1435        assert_eq!(request.description, Some("Test description".to_string()));
1436    }
1437
1438    #[test]
1439    fn test_create_snapshot_request_deserialize_no_description() {
1440        let json = r#"{"name": "test-snapshot"}"#;
1441        let request: CreateSnapshotRequest = serde_json::from_str(json).unwrap();
1442        assert_eq!(request.name, "test-snapshot");
1443        assert_eq!(request.description, None);
1444    }
1445
1446    // Integration tests for handlers
1447    #[tokio::test]
1448    async fn test_list_handler_empty() {
1449        let (database, mut registry) = setup_test_database().await;
1450        create_test_entity(database.as_ref(), &mut registry, "User").await;
1451
1452        let context = create_test_context(database, registry);
1453        let params = HashMap::new();
1454
1455        let result =
1456            list_handler(Path("User".to_string()), Query(params), Extension(context)).await;
1457
1458        assert!(result.is_ok());
1459        let json_value = result.unwrap().0;
1460        assert!(json_value["data"].is_array());
1461        assert_eq!(json_value["total"], 0);
1462    }
1463
1464    #[tokio::test]
1465    async fn test_list_handler_entity_not_found() {
1466        let (database, registry) = setup_test_database().await;
1467        let context = create_test_context(database, registry);
1468        let params = HashMap::new();
1469
1470        let result =
1471            list_handler(Path("NonExistent".to_string()), Query(params), Extension(context)).await;
1472
1473        assert!(result.is_err());
1474        let (status, _) = result.unwrap_err();
1475        assert_eq!(status, StatusCode::NOT_FOUND);
1476    }
1477
1478    #[tokio::test]
1479    async fn test_get_handler_not_found() {
1480        let (database, mut registry) = setup_test_database().await;
1481        create_test_entity(database.as_ref(), &mut registry, "User").await;
1482
1483        let context = create_test_context(database, registry);
1484
1485        let result = get_handler(
1486            Path(("User".to_string(), "nonexistent-id".to_string())),
1487            Extension(context),
1488        )
1489        .await;
1490
1491        assert!(result.is_err());
1492        let (status, _) = result.unwrap_err();
1493        assert_eq!(status, StatusCode::NOT_FOUND);
1494    }
1495
1496    #[tokio::test]
1497    async fn test_create_handler_invalid_body() {
1498        let (database, mut registry) = setup_test_database().await;
1499        create_test_entity(database.as_ref(), &mut registry, "User").await;
1500
1501        let context = create_test_context(database, registry);
1502        let body = json!("not an object");
1503
1504        let result = create_handler(Path("User".to_string()), Extension(context), Json(body)).await;
1505
1506        assert!(result.is_err());
1507        let (status, _) = result.unwrap_err();
1508        assert_eq!(status, StatusCode::BAD_REQUEST);
1509    }
1510
1511    #[tokio::test]
1512    async fn test_update_handler_not_found() {
1513        let (database, mut registry) = setup_test_database().await;
1514        create_test_entity(database.as_ref(), &mut registry, "User").await;
1515
1516        let context = create_test_context(database, registry);
1517        let body = json!({"name": "Updated"});
1518
1519        let result = update_handler(
1520            Path(("User".to_string(), "nonexistent-id".to_string())),
1521            Extension(context),
1522            Json(body),
1523        )
1524        .await;
1525
1526        assert!(result.is_err());
1527        let (status, _) = result.unwrap_err();
1528        assert_eq!(status, StatusCode::NOT_FOUND);
1529    }
1530
1531    #[tokio::test]
1532    async fn test_update_handler_invalid_body() {
1533        let (database, mut registry) = setup_test_database().await;
1534        create_test_entity(database.as_ref(), &mut registry, "User").await;
1535
1536        let context = create_test_context(database, registry);
1537        let body = json!("not an object");
1538
1539        let result = update_handler(
1540            Path(("User".to_string(), "some-id".to_string())),
1541            Extension(context),
1542            Json(body),
1543        )
1544        .await;
1545
1546        assert!(result.is_err());
1547        let (status, _) = result.unwrap_err();
1548        assert_eq!(status, StatusCode::BAD_REQUEST);
1549    }
1550
1551    #[tokio::test]
1552    async fn test_delete_handler_not_found() {
1553        let (database, mut registry) = setup_test_database().await;
1554        create_test_entity(database.as_ref(), &mut registry, "User").await;
1555
1556        let context = create_test_context(database, registry);
1557
1558        let result = delete_handler(
1559            Path(("User".to_string(), "nonexistent-id".to_string())),
1560            Extension(context),
1561        )
1562        .await;
1563
1564        assert!(result.is_err());
1565        let (status, _) = result.unwrap_err();
1566        assert_eq!(status, StatusCode::NOT_FOUND);
1567    }
1568
1569    #[tokio::test]
1570    async fn test_get_relationship_handler_entity_not_found() {
1571        let (database, registry) = setup_test_database().await;
1572        let context = create_test_context(database, registry);
1573        let params = HashMap::new();
1574
1575        let result = get_relationship_handler(
1576            Path(("User".to_string(), "123".to_string(), "orders".to_string())),
1577            Query(params),
1578            Extension(context),
1579        )
1580        .await;
1581
1582        assert!(result.is_err());
1583        let (status, _) = result.unwrap_err();
1584        assert_eq!(status, StatusCode::NOT_FOUND);
1585    }
1586
1587    #[tokio::test]
1588    async fn test_snapshot_handlers_no_directory() {
1589        let (database, registry) = setup_test_database().await;
1590        let context = create_test_context(database, registry);
1591
1592        // Test create_snapshot_handler without snapshots_dir
1593        let body = CreateSnapshotRequest {
1594            name: "test".to_string(),
1595            description: None,
1596        };
1597        let result = create_snapshot_handler(Extension(context.clone()), Json(body)).await;
1598        assert!(result.is_err());
1599
1600        // Test list_snapshots_handler without snapshots_dir
1601        let result = list_snapshots_handler(Extension(context.clone())).await;
1602        assert!(result.is_err());
1603
1604        // Test restore_snapshot_handler without snapshots_dir
1605        let result =
1606            restore_snapshot_handler(Path("test".to_string()), Extension(context.clone())).await;
1607        assert!(result.is_err());
1608
1609        // Test delete_snapshot_handler without snapshots_dir
1610        let result = delete_snapshot_handler(Path("test".to_string()), Extension(context)).await;
1611        assert!(result.is_err());
1612    }
1613
1614    #[tokio::test]
1615    async fn test_reset_handler_success() {
1616        let (database, mut registry) = setup_test_database().await;
1617        create_test_entity(database.as_ref(), &mut registry, "User").await;
1618
1619        let context = create_test_context(database, registry);
1620
1621        let result = reset_handler(Extension(context)).await;
1622        assert!(result.is_ok());
1623        let json_value = result.unwrap().0;
1624        assert!(json_value["message"].as_str().unwrap().contains("reset successfully"));
1625    }
1626}