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