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