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