Skip to main content

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