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