1use async_graphql::Value;
4use serde::{Deserialize, Serialize};
5use std::collections::HashMap;
6
7#[derive(Debug, Clone)]
9pub struct MockResolver {
10 pub field_name: String,
12 pub field_type: String,
14 pub mock_data: Value,
16 pub generator: Option<MockDataGenerator>,
18}
19
20#[derive(Debug, Clone)]
22pub struct MockDataGenerator {
23 pub generator_type: GeneratorType,
25 pub config: HashMap<String, serde_json::Value>,
27}
28
29#[derive(Debug, Clone, Serialize, Deserialize)]
31#[serde(tag = "type")]
32pub enum GeneratorType {
33 String {
35 min_length: usize,
37 max_length: usize,
39 },
40 Int {
42 min: i64,
44 max: i64,
46 },
47 Float {
49 min: f64,
51 max: f64,
53 },
54 Uuid,
56 Email,
58 Name,
60 Timestamp,
62 FromList {
64 values: Vec<serde_json::Value>,
66 },
67 Object {
69 fields: HashMap<String, Box<GeneratorType>>,
71 },
72 Array {
74 item_generator: Box<GeneratorType>,
76 min_items: usize,
78 max_items: usize,
80 },
81}
82
83pub struct ResolverRegistry {
85 resolvers: HashMap<String, HashMap<String, MockResolver>>,
86}
87
88impl ResolverRegistry {
89 pub fn new() -> Self {
91 Self {
92 resolvers: HashMap::new(),
93 }
94 }
95
96 pub fn register_resolver(&mut self, type_name: &str, resolver: MockResolver) {
98 self.resolvers
99 .entry(type_name.to_string())
100 .or_default()
101 .insert(resolver.field_name.clone(), resolver);
102 }
103
104 pub fn get_resolver(&self, type_name: &str, field_name: &str) -> Option<&MockResolver> {
106 self.resolvers
107 .get(type_name)
108 .and_then(|type_resolvers| type_resolvers.get(field_name))
109 }
110
111 pub async fn generate_mock_data(&self, type_name: &str, field_name: &str) -> Value {
113 if let Some(resolver) = self.get_resolver(type_name, field_name) {
114 if let Some(generator) = &resolver.generator {
115 return generator.generate().await;
116 }
117 return resolver.mock_data.clone();
118 }
119
120 Self::generate_default_mock_data(field_name).await
122 }
123
124 async fn generate_default_mock_data(field_name: &str) -> Value {
126 match field_name.to_lowercase().as_str() {
127 "id" => Value::String(mockforge_core::templating::expand_str("{{uuid}}")),
128 "name" | "title" => {
129 Value::String(mockforge_core::templating::expand_str("{{faker.name}}"))
130 }
131 "email" => Value::String(mockforge_core::templating::expand_str("{{faker.email}}")),
132 "description" | "content" => {
133 Value::String(mockforge_core::templating::expand_str("{{faker.sentence}}"))
134 }
135 "age" | "count" | "quantity" => Value::Number((rand::random::<u32>() % 100).into()),
136 "price" | "amount" => {
137 let val = rand::random::<f64>() * 1000.0;
138 Value::Number(
139 serde_json::Number::from_f64(val)
140 .unwrap_or_else(|| serde_json::Number::from(0)),
141 )
142 }
143 "active" | "enabled" | "is_active" => Value::Boolean(rand::random::<bool>()),
144 "created_at" | "updated_at" => {
145 Value::String(mockforge_core::templating::expand_str("{{now}}"))
146 }
147 _ => Value::String(mockforge_core::templating::expand_str("{{faker.word}}")),
148 }
149 }
150
151 pub fn create_common_resolvers() -> Self {
153 let mut registry = Self::new();
154
155 registry.register_resolver(
157 "User",
158 MockResolver {
159 field_name: "id".to_string(),
160 field_type: "ID!".to_string(),
161 mock_data: Value::Null,
162 generator: Some(MockDataGenerator {
163 generator_type: GeneratorType::Uuid,
164 config: HashMap::new(),
165 }),
166 },
167 );
168
169 registry.register_resolver(
170 "User",
171 MockResolver {
172 field_name: "name".to_string(),
173 field_type: "String!".to_string(),
174 mock_data: Value::Null,
175 generator: Some(MockDataGenerator {
176 generator_type: GeneratorType::Name,
177 config: HashMap::new(),
178 }),
179 },
180 );
181
182 registry.register_resolver(
183 "User",
184 MockResolver {
185 field_name: "email".to_string(),
186 field_type: "String!".to_string(),
187 mock_data: Value::Null,
188 generator: Some(MockDataGenerator {
189 generator_type: GeneratorType::Email,
190 config: HashMap::new(),
191 }),
192 },
193 );
194
195 registry.register_resolver(
196 "User",
197 MockResolver {
198 field_name: "createdAt".to_string(),
199 field_type: "String!".to_string(),
200 mock_data: Value::Null,
201 generator: Some(MockDataGenerator {
202 generator_type: GeneratorType::Timestamp,
203 config: HashMap::new(),
204 }),
205 },
206 );
207
208 registry
209 }
210}
211
212impl MockDataGenerator {
213 pub async fn generate(&self) -> Value {
215 match &self.generator_type {
216 GeneratorType::String {
217 min_length,
218 max_length,
219 } => {
220 use rand::Rng;
221 let mut rng = rand::thread_rng();
222 let length = rng.gen_range(*min_length..*max_length);
223 let s: String = (0..length)
224 .map(|_| {
225 let c = rng.gen_range(b'a'..=b'z');
226 c as char
227 })
228 .collect();
229 Value::String(s)
230 }
231 GeneratorType::Int { min, max } => {
232 let num = rand::random::<i64>() % (max - min) + min;
233 Value::Number(num.into())
234 }
235 GeneratorType::Float { min, max } => {
236 let num = rand::random::<f64>() * (max - min) + min;
237 Value::Number(
238 serde_json::Number::from_f64(num)
239 .unwrap_or_else(|| serde_json::Number::from(0)),
240 )
241 }
242 GeneratorType::Uuid => {
243 Value::String(mockforge_core::templating::expand_str("{{uuid}}"))
244 }
245 GeneratorType::Email => {
246 Value::String(mockforge_core::templating::expand_str("{{faker.email}}"))
247 }
248 GeneratorType::Name => {
249 Value::String(mockforge_core::templating::expand_str("{{faker.name}}"))
250 }
251 GeneratorType::Timestamp => {
252 Value::String(mockforge_core::templating::expand_str("{{now}}"))
253 }
254 GeneratorType::FromList { values } => {
255 use rand::Rng;
256 if values.is_empty() {
257 Value::Null
258 } else {
259 let mut rng = rand::thread_rng();
260 let index = rng.gen_range(0..values.len());
261 serde_json::from_value(values[index].clone()).unwrap_or(Value::Null)
262 }
263 }
264 GeneratorType::Object { fields: _ } => {
265 use indexmap::IndexMap;
271 let map = IndexMap::new();
272 Value::Object(map)
273 }
274 GeneratorType::Array {
275 item_generator: _,
276 min_items,
277 max_items,
278 } => {
279 use rand::Rng;
285 let mut rng = rand::thread_rng();
286 let count = rng.gen_range(*min_items..*max_items);
287 let items = vec![Value::Null; count];
288 Value::List(items)
289 }
290 }
291 }
292}
293
294impl Default for ResolverRegistry {
295 fn default() -> Self {
296 Self::new()
297 }
298}
299
300#[cfg(test)]
301mod tests {
302 use super::*;
303
304 #[test]
305 fn test_mock_resolver_creation() {
306 let resolver = MockResolver {
307 field_name: "id".to_string(),
308 field_type: "ID!".to_string(),
309 mock_data: Value::String("test-123".to_string()),
310 generator: None,
311 };
312
313 assert_eq!(resolver.field_name, "id");
314 assert_eq!(resolver.field_type, "ID!");
315 assert!(resolver.generator.is_none());
316 }
317
318 #[test]
319 fn test_mock_resolver_with_generator() {
320 let resolver = MockResolver {
321 field_name: "email".to_string(),
322 field_type: "String!".to_string(),
323 mock_data: Value::Null,
324 generator: Some(MockDataGenerator {
325 generator_type: GeneratorType::Email,
326 config: HashMap::new(),
327 }),
328 };
329
330 assert!(resolver.generator.is_some());
331 }
332
333 #[test]
334 fn test_generator_type_string() {
335 let gen_type = GeneratorType::String {
336 min_length: 5,
337 max_length: 10,
338 };
339
340 match gen_type {
341 GeneratorType::String {
342 min_length,
343 max_length,
344 } => {
345 assert_eq!(min_length, 5);
346 assert_eq!(max_length, 10);
347 }
348 _ => panic!("Wrong generator type"),
349 }
350 }
351
352 #[test]
353 fn test_generator_type_int() {
354 let gen_type = GeneratorType::Int { min: 1, max: 100 };
355
356 match gen_type {
357 GeneratorType::Int { min, max } => {
358 assert_eq!(min, 1);
359 assert_eq!(max, 100);
360 }
361 _ => panic!("Wrong generator type"),
362 }
363 }
364
365 #[test]
366 fn test_generator_type_float() {
367 let gen_type = GeneratorType::Float { min: 0.0, max: 1.0 };
368
369 match gen_type {
370 GeneratorType::Float { min, max } => {
371 assert_eq!(min, 0.0);
372 assert_eq!(max, 1.0);
373 }
374 _ => panic!("Wrong generator type"),
375 }
376 }
377
378 #[test]
379 fn test_generator_type_uuid() {
380 let gen_type = GeneratorType::Uuid;
381 assert!(matches!(gen_type, GeneratorType::Uuid));
382 }
383
384 #[test]
385 fn test_generator_type_email() {
386 let gen_type = GeneratorType::Email;
387 assert!(matches!(gen_type, GeneratorType::Email));
388 }
389
390 #[test]
391 fn test_generator_type_name() {
392 let gen_type = GeneratorType::Name;
393 assert!(matches!(gen_type, GeneratorType::Name));
394 }
395
396 #[test]
397 fn test_generator_type_timestamp() {
398 let gen_type = GeneratorType::Timestamp;
399 assert!(matches!(gen_type, GeneratorType::Timestamp));
400 }
401
402 #[test]
403 fn test_generator_type_from_list() {
404 let values = vec![serde_json::json!("value1"), serde_json::json!("value2")];
405 let gen_type = GeneratorType::FromList {
406 values: values.clone(),
407 };
408
409 match gen_type {
410 GeneratorType::FromList { values: v } => {
411 assert_eq!(v.len(), 2);
412 }
413 _ => panic!("Wrong generator type"),
414 }
415 }
416
417 #[test]
418 fn test_resolver_registry_new() {
419 let registry = ResolverRegistry::new();
420 assert_eq!(registry.resolvers.len(), 0);
421 }
422
423 #[test]
424 fn test_resolver_registry_default() {
425 let registry = ResolverRegistry::default();
426 assert_eq!(registry.resolvers.len(), 0);
427 }
428
429 #[test]
430 fn test_resolver_registry_register() {
431 let mut registry = ResolverRegistry::new();
432
433 let resolver = MockResolver {
434 field_name: "id".to_string(),
435 field_type: "ID!".to_string(),
436 mock_data: Value::String("test-id".to_string()),
437 generator: None,
438 };
439
440 registry.register_resolver("User", resolver);
441
442 assert!(registry.resolvers.contains_key("User"));
443 assert!(registry.resolvers.get("User").unwrap().contains_key("id"));
444 }
445
446 #[test]
447 fn test_resolver_registry_get_resolver() {
448 let mut registry = ResolverRegistry::new();
449
450 let resolver = MockResolver {
451 field_name: "email".to_string(),
452 field_type: "String!".to_string(),
453 mock_data: Value::String("test@example.com".to_string()),
454 generator: None,
455 };
456
457 registry.register_resolver("User", resolver);
458
459 let retrieved = registry.get_resolver("User", "email");
460 assert!(retrieved.is_some());
461 assert_eq!(retrieved.unwrap().field_name, "email");
462 }
463
464 #[test]
465 fn test_resolver_registry_get_resolver_not_found() {
466 let registry = ResolverRegistry::new();
467 let retrieved = registry.get_resolver("User", "unknown");
468 assert!(retrieved.is_none());
469 }
470
471 #[tokio::test]
472 async fn test_generate_mock_data_with_static_data() {
473 let mut registry = ResolverRegistry::new();
474
475 let resolver = MockResolver {
476 field_name: "name".to_string(),
477 field_type: "String!".to_string(),
478 mock_data: Value::String("John Doe".to_string()),
479 generator: None,
480 };
481
482 registry.register_resolver("User", resolver);
483
484 let result = registry.generate_mock_data("User", "name").await;
485 assert!(matches!(result, Value::String(_)));
486 }
487
488 #[tokio::test]
489 async fn test_generate_default_mock_data_id() {
490 let result = ResolverRegistry::generate_default_mock_data("id").await;
491 assert!(matches!(result, Value::String(_)));
492 }
493
494 #[tokio::test]
495 async fn test_generate_default_mock_data_name() {
496 let result = ResolverRegistry::generate_default_mock_data("name").await;
497 assert!(matches!(result, Value::String(_)));
498 }
499
500 #[tokio::test]
501 async fn test_generate_default_mock_data_email() {
502 let result = ResolverRegistry::generate_default_mock_data("email").await;
503 assert!(matches!(result, Value::String(_)));
504 }
505
506 #[tokio::test]
507 async fn test_generate_default_mock_data_age() {
508 let result = ResolverRegistry::generate_default_mock_data("age").await;
509 assert!(matches!(result, Value::Number(_)));
510 }
511
512 #[tokio::test]
513 async fn test_generate_default_mock_data_active() {
514 let result = ResolverRegistry::generate_default_mock_data("active").await;
515 assert!(matches!(result, Value::Boolean(_)));
516 }
517
518 #[test]
519 fn test_create_common_resolvers() {
520 let registry = ResolverRegistry::create_common_resolvers();
521
522 assert!(registry.get_resolver("User", "id").is_some());
523 assert!(registry.get_resolver("User", "name").is_some());
524 assert!(registry.get_resolver("User", "email").is_some());
525 assert!(registry.get_resolver("User", "createdAt").is_some());
526 }
527
528 #[tokio::test]
529 async fn test_mock_data_generator_uuid() {
530 let generator = MockDataGenerator {
531 generator_type: GeneratorType::Uuid,
532 config: HashMap::new(),
533 };
534
535 let result = generator.generate().await;
536 assert!(matches!(result, Value::String(_)));
537 }
538
539 #[tokio::test]
540 async fn test_mock_data_generator_email() {
541 let generator = MockDataGenerator {
542 generator_type: GeneratorType::Email,
543 config: HashMap::new(),
544 };
545
546 let result = generator.generate().await;
547 assert!(matches!(result, Value::String(_)));
548 }
549
550 #[tokio::test]
551 async fn test_mock_data_generator_name() {
552 let generator = MockDataGenerator {
553 generator_type: GeneratorType::Name,
554 config: HashMap::new(),
555 };
556
557 let result = generator.generate().await;
558 assert!(matches!(result, Value::String(_)));
559 }
560
561 #[tokio::test]
562 async fn test_mock_data_generator_timestamp() {
563 let generator = MockDataGenerator {
564 generator_type: GeneratorType::Timestamp,
565 config: HashMap::new(),
566 };
567
568 let result = generator.generate().await;
569 assert!(matches!(result, Value::String(_)));
570 }
571
572 #[tokio::test]
573 async fn test_mock_data_generator_int() {
574 let generator = MockDataGenerator {
575 generator_type: GeneratorType::Int { min: 1, max: 100 },
576 config: HashMap::new(),
577 };
578
579 let result = generator.generate().await;
580 assert!(matches!(result, Value::Number(_)));
581 }
582
583 #[tokio::test]
584 async fn test_mock_data_generator_float() {
585 let generator = MockDataGenerator {
586 generator_type: GeneratorType::Float {
587 min: 0.0,
588 max: 10.0,
589 },
590 config: HashMap::new(),
591 };
592
593 let result = generator.generate().await;
594 assert!(matches!(result, Value::Number(_)));
595 }
596
597 #[tokio::test]
598 async fn test_mock_data_generator_string() {
599 let generator = MockDataGenerator {
600 generator_type: GeneratorType::String {
601 min_length: 5,
602 max_length: 10,
603 },
604 config: HashMap::new(),
605 };
606
607 let result = generator.generate().await;
608 if let Value::String(s) = result {
609 assert!(s.len() >= 5);
610 assert!(s.len() <= 10);
611 } else {
612 panic!("Expected string value");
613 }
614 }
615
616 #[tokio::test]
617 async fn test_mock_data_generator_from_list() {
618 let values = vec![
619 serde_json::json!("value1"),
620 serde_json::json!("value2"),
621 serde_json::json!("value3"),
622 ];
623
624 let generator = MockDataGenerator {
625 generator_type: GeneratorType::FromList { values },
626 config: HashMap::new(),
627 };
628
629 let result = generator.generate().await;
630 assert!(matches!(result, Value::String(_)));
631 }
632
633 #[tokio::test]
634 async fn test_mock_data_generator_from_empty_list() {
635 let generator = MockDataGenerator {
636 generator_type: GeneratorType::FromList { values: vec![] },
637 config: HashMap::new(),
638 };
639
640 let result = generator.generate().await;
641 assert!(matches!(result, Value::Null));
642 }
643
644 #[tokio::test]
645 async fn test_mock_data_generator_array() {
646 let generator = MockDataGenerator {
647 generator_type: GeneratorType::Array {
648 item_generator: Box::new(GeneratorType::Uuid),
649 min_items: 2,
650 max_items: 5,
651 },
652 config: HashMap::new(),
653 };
654
655 let result = generator.generate().await;
656 if let Value::List(items) = result {
657 assert!(items.len() >= 2);
658 assert!(items.len() <= 5);
659 } else {
660 panic!("Expected list value");
661 }
662 }
663
664 #[tokio::test]
665 async fn test_resolver_registry_multiple_types() {
666 let mut registry = ResolverRegistry::new();
667
668 registry.register_resolver(
669 "User",
670 MockResolver {
671 field_name: "id".to_string(),
672 field_type: "ID!".to_string(),
673 mock_data: Value::String("user-id".to_string()),
674 generator: None,
675 },
676 );
677
678 registry.register_resolver(
679 "Post",
680 MockResolver {
681 field_name: "id".to_string(),
682 field_type: "ID!".to_string(),
683 mock_data: Value::String("post-id".to_string()),
684 generator: None,
685 },
686 );
687
688 assert!(registry.get_resolver("User", "id").is_some());
689 assert!(registry.get_resolver("Post", "id").is_some());
690 }
691
692 #[test]
693 fn test_resolver_registry_multiple_fields_same_type() {
694 let mut registry = ResolverRegistry::new();
695
696 registry.register_resolver(
697 "User",
698 MockResolver {
699 field_name: "id".to_string(),
700 field_type: "ID!".to_string(),
701 mock_data: Value::Null,
702 generator: None,
703 },
704 );
705
706 registry.register_resolver(
707 "User",
708 MockResolver {
709 field_name: "name".to_string(),
710 field_type: "String!".to_string(),
711 mock_data: Value::Null,
712 generator: None,
713 },
714 );
715
716 registry.register_resolver(
717 "User",
718 MockResolver {
719 field_name: "email".to_string(),
720 field_type: "String!".to_string(),
721 mock_data: Value::Null,
722 generator: None,
723 },
724 );
725
726 assert_eq!(registry.resolvers.get("User").unwrap().len(), 3);
727 }
728}