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