1use serde::{Deserialize, Serialize};
37use std::collections::{HashMap, HashSet};
38use std::sync::Arc;
39use std::time::{Duration, Instant};
40use tokio::sync::{mpsc, RwLock};
41
42#[derive(Debug, Clone)]
44pub struct IncrementalConfig {
45 pub enable_defer: bool,
47 pub enable_stream: bool,
49 pub default_stream_initial_count: usize,
51 pub max_concurrent_defers: usize,
53 pub fragment_timeout: Duration,
55 pub enable_parallel_execution: bool,
57}
58
59impl Default for IncrementalConfig {
60 fn default() -> Self {
61 Self {
62 enable_defer: true,
63 enable_stream: true,
64 default_stream_initial_count: 10,
65 max_concurrent_defers: 10,
66 fragment_timeout: Duration::from_secs(30),
67 enable_parallel_execution: true,
68 }
69 }
70}
71
72impl IncrementalConfig {
73 pub fn new() -> Self {
75 Self::default()
76 }
77
78 pub fn with_defer(mut self, enabled: bool) -> Self {
80 self.enable_defer = enabled;
81 self
82 }
83
84 pub fn with_stream(mut self, enabled: bool) -> Self {
86 self.enable_stream = enabled;
87 self
88 }
89
90 pub fn with_stream_initial_count(mut self, count: usize) -> Self {
92 self.default_stream_initial_count = count;
93 self
94 }
95
96 pub fn with_max_concurrent_defers(mut self, max: usize) -> Self {
98 self.max_concurrent_defers = max;
99 self
100 }
101
102 pub fn with_fragment_timeout(mut self, timeout: Duration) -> Self {
104 self.fragment_timeout = timeout;
105 self
106 }
107
108 pub fn with_parallel_execution(mut self, enabled: bool) -> Self {
110 self.enable_parallel_execution = enabled;
111 self
112 }
113}
114
115#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117pub enum ExecutionPhase {
118 Initial,
120 Deferred,
122 Streaming,
124}
125
126#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
128pub struct FragmentId {
129 pub path: String,
131 pub label: Option<String>,
133}
134
135impl FragmentId {
136 pub fn new(path: String) -> Self {
138 Self { path, label: None }
139 }
140
141 pub fn with_label(mut self, label: String) -> Self {
143 self.label = Some(label);
144 self
145 }
146}
147
148#[derive(Debug, Clone)]
150pub struct DeferredFragment {
151 pub id: FragmentId,
153 pub fields: Vec<String>,
155 pub priority: u8,
157 pub dependencies: Vec<FragmentId>,
159 pub if_condition: bool,
161}
162
163impl DeferredFragment {
164 pub fn new(id: FragmentId, fields: Vec<String>) -> Self {
166 Self {
167 id,
168 fields,
169 priority: 0,
170 dependencies: Vec::new(),
171 if_condition: true,
172 }
173 }
174
175 pub fn with_priority(mut self, priority: u8) -> Self {
177 self.priority = priority;
178 self
179 }
180
181 pub fn with_dependency(mut self, dep: FragmentId) -> Self {
183 self.dependencies.push(dep);
184 self
185 }
186
187 pub fn with_if_condition(mut self, condition: bool) -> Self {
189 self.if_condition = condition;
190 self
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct StreamConfig {
197 pub id: FragmentId,
199 pub field: String,
201 pub initial_count: usize,
203 pub if_condition: bool,
205 pub label: Option<String>,
207}
208
209impl StreamConfig {
210 pub fn new(id: FragmentId, field: String, initial_count: usize) -> Self {
212 Self {
213 id,
214 field,
215 initial_count,
216 if_condition: true,
217 label: None,
218 }
219 }
220
221 pub fn with_if_condition(mut self, condition: bool) -> Self {
223 self.if_condition = condition;
224 self
225 }
226
227 pub fn with_label(mut self, label: String) -> Self {
229 self.label = Some(label);
230 self
231 }
232}
233
234#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct IncrementalPayload {
237 pub data: Option<serde_json::Value>,
239 pub path: Vec<String>,
241 pub label: Option<String>,
243 pub errors: Vec<IncrementalError>,
245 pub has_next: bool,
247}
248
249#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct IncrementalError {
252 pub message: String,
254 pub path: Vec<String>,
256 pub extensions: HashMap<String, serde_json::Value>,
258}
259
260#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct InitialResult {
263 pub data: serde_json::Value,
265 pub errors: Vec<IncrementalError>,
267 pub has_next: bool,
269}
270
271#[derive(Debug, Clone)]
273pub struct ExecutionPlan {
274 pub immediate_fields: Vec<String>,
276 pub deferred_fragments: Vec<DeferredFragment>,
278 pub streams: Vec<StreamConfig>,
280 pub dependencies: HashMap<FragmentId, Vec<FragmentId>>,
282}
283
284impl ExecutionPlan {
285 pub fn new() -> Self {
287 Self {
288 immediate_fields: Vec::new(),
289 deferred_fragments: Vec::new(),
290 streams: Vec::new(),
291 dependencies: HashMap::new(),
292 }
293 }
294
295 pub fn add_immediate_field(&mut self, field: String) {
297 self.immediate_fields.push(field);
298 }
299
300 pub fn add_deferred_fragment(&mut self, fragment: DeferredFragment) {
302 for dep in &fragment.dependencies {
304 self.dependencies
305 .entry(dep.clone())
306 .or_default()
307 .push(fragment.id.clone());
308 }
309 self.deferred_fragments.push(fragment);
310 }
311
312 pub fn add_stream(&mut self, stream: StreamConfig) {
314 self.streams.push(stream);
315 }
316
317 pub fn get_execution_order(&self) -> Vec<FragmentId> {
319 let mut order = Vec::new();
320 let mut visited = HashSet::new();
321 let mut in_progress = HashSet::new();
322
323 for fragment in &self.deferred_fragments {
324 self.visit_fragment(&fragment.id, &mut visited, &mut in_progress, &mut order);
325 }
326
327 order
328 }
329
330 fn visit_fragment(
331 &self,
332 id: &FragmentId,
333 visited: &mut HashSet<FragmentId>,
334 in_progress: &mut HashSet<FragmentId>,
335 order: &mut Vec<FragmentId>,
336 ) {
337 if visited.contains(id) {
338 return;
339 }
340
341 if in_progress.contains(id) {
342 return;
344 }
345
346 in_progress.insert(id.clone());
347
348 if let Some(fragment) = self.deferred_fragments.iter().find(|f| &f.id == id) {
350 for dep in &fragment.dependencies {
351 self.visit_fragment(dep, visited, in_progress, order);
352 }
353 }
354
355 in_progress.remove(id);
356 visited.insert(id.clone());
357 order.push(id.clone());
358 }
359}
360
361impl Default for ExecutionPlan {
362 fn default() -> Self {
363 Self::new()
364 }
365}
366
367pub struct IncrementalExecutor {
369 config: IncrementalConfig,
370 stats: Arc<RwLock<ExecutionStats>>,
371}
372
373impl IncrementalExecutor {
374 pub fn new(config: IncrementalConfig) -> Self {
376 Self {
377 config,
378 stats: Arc::new(RwLock::new(ExecutionStats::new())),
379 }
380 }
381
382 pub async fn execute(
384 &self,
385 plan: ExecutionPlan,
386 initial_data: serde_json::Value,
387 ) -> Result<IncrementalResultStream, ExecutionError> {
388 let (tx, rx) = mpsc::channel(100);
389 let config = self.config.clone();
390 let stats = self.stats.clone();
391
392 let has_deferred = !plan.deferred_fragments.is_empty();
394 let has_streams = !plan.streams.is_empty();
395
396 let initial = InitialResult {
397 data: initial_data,
398 errors: Vec::new(),
399 has_next: has_deferred || has_streams,
400 };
401
402 if tx
403 .send(Ok(IncrementalEvent::Initial(initial)))
404 .await
405 .is_err()
406 {
407 return Err(ExecutionError::ChannelError(
408 "Failed to send initial result".to_string(),
409 ));
410 }
411
412 if has_deferred && config.enable_defer {
414 let plan_clone = plan.clone();
415 let tx_clone = tx.clone();
416 let stats_clone = stats.clone();
417 let config_clone = config.clone();
418 tokio::spawn(async move {
419 Self::execute_deferred_fragments(plan_clone, tx_clone, config_clone, stats_clone)
420 .await;
421 });
422 }
423
424 if has_streams && config.enable_stream {
426 let plan_clone = plan;
427 let tx_clone = tx;
428 let stats_clone = stats.clone();
429 tokio::spawn(async move {
430 Self::execute_streams(plan_clone, tx_clone, stats_clone).await;
431 });
432 } else if !has_deferred {
433 let _ = tx.send(Ok(IncrementalEvent::Complete)).await;
435 }
436
437 Ok(IncrementalResultStream::new(rx, self.stats.clone()))
438 }
439
440 async fn execute_deferred_fragments(
442 plan: ExecutionPlan,
443 tx: mpsc::Sender<IncrementalResult>,
444 config: IncrementalConfig,
445 stats: Arc<RwLock<ExecutionStats>>,
446 ) {
447 let execution_order = plan.get_execution_order();
448 let mut completed = HashSet::new();
449
450 for fragment_id in execution_order {
451 let fragment = match plan.deferred_fragments.iter().find(|f| f.id == fragment_id) {
453 Some(f) => f,
454 None => continue,
455 };
456
457 if !fragment.if_condition {
459 completed.insert(fragment_id.clone());
460 continue;
461 }
462
463 for dep in &fragment.dependencies {
465 while !completed.contains(dep) {
466 tokio::time::sleep(Duration::from_millis(10)).await;
467 }
468 }
469
470 let start_time = Instant::now();
471
472 let data = serde_json::json!({
474 "result": format!("Deferred data for {}", fragment_id.path)
475 });
476
477 let path: Vec<String> = fragment_id.path.split('.').map(String::from).collect();
478
479 let payload = IncrementalPayload {
480 data: Some(data),
481 path,
482 label: fragment_id.label.clone(),
483 errors: Vec::new(),
484 has_next: true,
485 };
486
487 {
489 let mut s = stats.write().await;
490 s.deferred_fragments_executed += 1;
491 s.total_fragment_execution_time += start_time.elapsed();
492 }
493
494 if tx
495 .send(Ok(IncrementalEvent::Deferred(payload)))
496 .await
497 .is_err()
498 {
499 break;
500 }
501
502 completed.insert(fragment_id);
503
504 if completed.len() >= config.max_concurrent_defers {
506 tokio::time::sleep(Duration::from_millis(1)).await;
507 }
508 }
509
510 let _ = tx.send(Ok(IncrementalEvent::Complete)).await;
512 }
513
514 async fn execute_streams(
516 plan: ExecutionPlan,
517 tx: mpsc::Sender<IncrementalResult>,
518 stats: Arc<RwLock<ExecutionStats>>,
519 ) {
520 for stream_config in plan.streams {
521 if !stream_config.if_condition {
522 continue;
523 }
524
525 let total_items = 50; let mut sent = stream_config.initial_count;
528
529 while sent < total_items {
530 let chunk_size = 10.min(total_items - sent);
531 let items: Vec<serde_json::Value> = (sent..sent + chunk_size)
532 .map(|i| serde_json::json!({ "id": i, "data": format!("item_{}", i) }))
533 .collect();
534
535 let path: Vec<String> =
536 stream_config.id.path.split('.').map(String::from).collect();
537
538 let payload = IncrementalPayload {
539 data: Some(serde_json::json!(items)),
540 path,
541 label: stream_config.label.clone(),
542 errors: Vec::new(),
543 has_next: sent + chunk_size < total_items,
544 };
545
546 {
548 let mut s = stats.write().await;
549 s.stream_chunks_sent += 1;
550 s.total_stream_items += chunk_size;
551 }
552
553 if tx
554 .send(Ok(IncrementalEvent::Stream(payload)))
555 .await
556 .is_err()
557 {
558 break;
559 }
560
561 sent += chunk_size;
562 tokio::time::sleep(Duration::from_millis(50)).await;
563 }
564 }
565
566 let _ = tx.send(Ok(IncrementalEvent::Complete)).await;
567 }
568
569 pub async fn get_stats(&self) -> ExecutionStats {
571 self.stats.read().await.clone()
572 }
573}
574
575#[derive(Debug, Clone, Serialize, Deserialize)]
577pub enum IncrementalEvent {
578 Initial(InitialResult),
580 Deferred(IncrementalPayload),
582 Stream(IncrementalPayload),
584 Complete,
586 Error(IncrementalError),
588}
589
590pub type IncrementalResult = Result<IncrementalEvent, ExecutionError>;
592
593pub struct IncrementalResultStream {
595 receiver: mpsc::Receiver<IncrementalResult>,
596 stats: Arc<RwLock<ExecutionStats>>,
597}
598
599impl IncrementalResultStream {
600 pub fn new(
602 receiver: mpsc::Receiver<IncrementalResult>,
603 stats: Arc<RwLock<ExecutionStats>>,
604 ) -> Self {
605 Self { receiver, stats }
606 }
607
608 pub async fn next(&mut self) -> Option<IncrementalResult> {
610 self.receiver.recv().await
611 }
612
613 pub async fn get_stats(&self) -> ExecutionStats {
615 self.stats.read().await.clone()
616 }
617}
618
619#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ExecutionStats {
622 pub deferred_fragments_executed: usize,
624 pub stream_chunks_sent: usize,
626 pub total_stream_items: usize,
628 pub total_fragment_execution_time: Duration,
630 pub errors: usize,
632}
633
634impl ExecutionStats {
635 pub fn new() -> Self {
637 Self {
638 deferred_fragments_executed: 0,
639 stream_chunks_sent: 0,
640 total_stream_items: 0,
641 total_fragment_execution_time: Duration::ZERO,
642 errors: 0,
643 }
644 }
645
646 pub fn record_error(&mut self) {
648 self.errors += 1;
649 }
650}
651
652impl Default for ExecutionStats {
653 fn default() -> Self {
654 Self::new()
655 }
656}
657
658#[derive(Debug, thiserror::Error)]
660pub enum ExecutionError {
661 #[error("Channel error: {0}")]
663 ChannelError(String),
664
665 #[error("Fragment execution timeout")]
667 Timeout,
668
669 #[error("Circular dependency detected in fragment: {0}")]
671 CircularDependency(String),
672
673 #[error("Invalid configuration: {0}")]
675 InvalidConfig(String),
676
677 #[error("Execution error: {0}")]
679 ExecutionError(String),
680}
681
682#[cfg(test)]
683mod tests {
684 use super::*;
685
686 #[test]
687 fn test_incremental_config_builder() {
688 let config = IncrementalConfig::new()
689 .with_defer(true)
690 .with_stream(true)
691 .with_stream_initial_count(20)
692 .with_max_concurrent_defers(5)
693 .with_parallel_execution(true);
694
695 assert!(config.enable_defer);
696 assert!(config.enable_stream);
697 assert_eq!(config.default_stream_initial_count, 20);
698 assert_eq!(config.max_concurrent_defers, 5);
699 assert!(config.enable_parallel_execution);
700 }
701
702 #[test]
703 fn test_fragment_id_creation() {
704 let id = FragmentId::new("user.posts".to_string()).with_label("userPosts".to_string());
705
706 assert_eq!(id.path, "user.posts");
707 assert_eq!(id.label, Some("userPosts".to_string()));
708 }
709
710 #[test]
711 fn test_deferred_fragment_creation() {
712 let id = FragmentId::new("user.friends".to_string());
713 let dep_id = FragmentId::new("user.profile".to_string());
714
715 let fragment =
716 DeferredFragment::new(id.clone(), vec!["name".to_string(), "email".to_string()])
717 .with_priority(5)
718 .with_dependency(dep_id)
719 .with_if_condition(true);
720
721 assert_eq!(fragment.id, id);
722 assert_eq!(fragment.fields.len(), 2);
723 assert_eq!(fragment.priority, 5);
724 assert_eq!(fragment.dependencies.len(), 1);
725 assert!(fragment.if_condition);
726 }
727
728 #[test]
729 fn test_stream_config_creation() {
730 let id = FragmentId::new("user.posts".to_string());
731 let stream = StreamConfig::new(id.clone(), "posts".to_string(), 10)
732 .with_if_condition(true)
733 .with_label("postStream".to_string());
734
735 assert_eq!(stream.id, id);
736 assert_eq!(stream.field, "posts");
737 assert_eq!(stream.initial_count, 10);
738 assert!(stream.if_condition);
739 assert_eq!(stream.label, Some("postStream".to_string()));
740 }
741
742 #[test]
743 fn test_execution_plan_immediate_fields() {
744 let mut plan = ExecutionPlan::new();
745 plan.add_immediate_field("id".to_string());
746 plan.add_immediate_field("name".to_string());
747
748 assert_eq!(plan.immediate_fields.len(), 2);
749 }
750
751 #[test]
752 fn test_execution_plan_deferred_fragments() {
753 let mut plan = ExecutionPlan::new();
754
755 let id1 = FragmentId::new("user.profile".to_string());
756 let fragment1 = DeferredFragment::new(id1, vec!["bio".to_string()]);
757
758 plan.add_deferred_fragment(fragment1);
759 assert_eq!(plan.deferred_fragments.len(), 1);
760 }
761
762 #[test]
763 fn test_execution_plan_streams() {
764 let mut plan = ExecutionPlan::new();
765
766 let id = FragmentId::new("user.posts".to_string());
767 let stream = StreamConfig::new(id, "posts".to_string(), 10);
768
769 plan.add_stream(stream);
770 assert_eq!(plan.streams.len(), 1);
771 }
772
773 #[test]
774 fn test_execution_plan_dependency_graph() {
775 let mut plan = ExecutionPlan::new();
776
777 let id1 = FragmentId::new("fragment1".to_string());
778 let id2 = FragmentId::new("fragment2".to_string());
779
780 let fragment2 = DeferredFragment::new(id2.clone(), vec!["field".to_string()])
781 .with_dependency(id1.clone());
782
783 plan.add_deferred_fragment(fragment2);
784
785 assert!(plan.dependencies.contains_key(&id1));
786 assert_eq!(
787 plan.dependencies.get(&id1).expect("should succeed"),
788 &vec![id2]
789 );
790 }
791
792 #[test]
793 fn test_execution_order_simple() {
794 let mut plan = ExecutionPlan::new();
795
796 let id1 = FragmentId::new("fragment1".to_string());
797 let id2 = FragmentId::new("fragment2".to_string());
798 let id3 = FragmentId::new("fragment3".to_string());
799
800 plan.add_deferred_fragment(DeferredFragment::new(id1.clone(), vec![]));
801 plan.add_deferred_fragment(
802 DeferredFragment::new(id2.clone(), vec![]).with_dependency(id1.clone()),
803 );
804 plan.add_deferred_fragment(
805 DeferredFragment::new(id3.clone(), vec![]).with_dependency(id2.clone()),
806 );
807
808 let order = plan.get_execution_order();
809 assert_eq!(order.len(), 3);
810 assert_eq!(order[0], id1);
811 assert_eq!(order[1], id2);
812 assert_eq!(order[2], id3);
813 }
814
815 #[test]
816 fn test_execution_order_complex() {
817 let mut plan = ExecutionPlan::new();
818
819 let id1 = FragmentId::new("a".to_string());
820 let id2 = FragmentId::new("b".to_string());
821 let id3 = FragmentId::new("c".to_string());
822 let id4 = FragmentId::new("d".to_string());
823
824 plan.add_deferred_fragment(DeferredFragment::new(id1.clone(), vec![]));
827 plan.add_deferred_fragment(
828 DeferredFragment::new(id2.clone(), vec![]).with_dependency(id1.clone()),
829 );
830 plan.add_deferred_fragment(
831 DeferredFragment::new(id3.clone(), vec![]).with_dependency(id1.clone()),
832 );
833 plan.add_deferred_fragment(
834 DeferredFragment::new(id4.clone(), vec![])
835 .with_dependency(id2.clone())
836 .with_dependency(id3.clone()),
837 );
838
839 let order = plan.get_execution_order();
840 assert_eq!(order.len(), 4);
841 assert_eq!(order[0], id1);
842 assert!(order[3] == id4); }
845
846 #[test]
847 fn test_execution_stats_creation() {
848 let stats = ExecutionStats::new();
849
850 assert_eq!(stats.deferred_fragments_executed, 0);
851 assert_eq!(stats.stream_chunks_sent, 0);
852 assert_eq!(stats.total_stream_items, 0);
853 assert_eq!(stats.total_fragment_execution_time, Duration::ZERO);
854 assert_eq!(stats.errors, 0);
855 }
856
857 #[test]
858 fn test_execution_stats_error_recording() {
859 let mut stats = ExecutionStats::new();
860
861 stats.record_error();
862 assert_eq!(stats.errors, 1);
863
864 stats.record_error();
865 assert_eq!(stats.errors, 2);
866 }
867
868 #[tokio::test]
869 async fn test_simple_incremental_execution() {
870 let config = IncrementalConfig::new();
871 let executor = IncrementalExecutor::new(config);
872
873 let mut plan = ExecutionPlan::new();
874 plan.add_immediate_field("id".to_string());
875 plan.add_immediate_field("name".to_string());
876
877 let initial_data = serde_json::json!({ "id": 1, "name": "Test" });
878
879 let mut stream = executor
880 .execute(plan, initial_data)
881 .await
882 .expect("should succeed");
883
884 let mut event_count = 0;
885 while let Some(event) = stream.next().await {
886 match event.expect("should succeed") {
887 IncrementalEvent::Initial(result) => {
888 event_count += 1;
889 assert!(!result.has_next);
890 }
891 IncrementalEvent::Complete => {
892 event_count += 1;
893 break;
894 }
895 _ => {}
896 }
897 }
898
899 assert!(event_count >= 1);
900 }
901
902 #[tokio::test]
903 async fn test_deferred_execution() {
904 let config = IncrementalConfig::new().with_defer(true);
905 let executor = IncrementalExecutor::new(config);
906
907 let mut plan = ExecutionPlan::new();
908 plan.add_immediate_field("id".to_string());
909
910 let id = FragmentId::new("user.posts".to_string());
911 let fragment = DeferredFragment::new(id, vec!["title".to_string(), "content".to_string()]);
912 plan.add_deferred_fragment(fragment);
913
914 let initial_data = serde_json::json!({ "id": 1 });
915
916 let mut stream = executor
917 .execute(plan, initial_data)
918 .await
919 .expect("should succeed");
920
921 let mut received_initial = false;
922 let mut received_deferred = false;
923
924 while let Some(event) = stream.next().await {
925 match event.expect("should succeed") {
926 IncrementalEvent::Initial(result) => {
927 received_initial = true;
928 assert!(result.has_next);
929 }
930 IncrementalEvent::Deferred(_) => {
931 received_deferred = true;
932 }
933 IncrementalEvent::Complete => break,
934 _ => {}
935 }
936 }
937
938 assert!(received_initial);
939 assert!(received_deferred);
940 }
941
942 #[tokio::test]
943 async fn test_stream_execution() {
944 let config = IncrementalConfig::new().with_stream(true);
945 let executor = IncrementalExecutor::new(config);
946
947 let mut plan = ExecutionPlan::new();
948 plan.add_immediate_field("id".to_string());
949
950 let id = FragmentId::new("user.posts".to_string());
951 let stream_config = StreamConfig::new(id, "posts".to_string(), 10);
952 plan.add_stream(stream_config);
953
954 let initial_data = serde_json::json!({ "id": 1 });
955
956 let mut stream = executor
957 .execute(plan, initial_data)
958 .await
959 .expect("should succeed");
960
961 let mut received_initial = false;
962 let mut stream_chunks = 0;
963
964 while let Some(event) = stream.next().await {
965 match event.expect("should succeed") {
966 IncrementalEvent::Initial(result) => {
967 received_initial = true;
968 assert!(result.has_next);
969 }
970 IncrementalEvent::Stream(_) => {
971 stream_chunks += 1;
972 }
973 IncrementalEvent::Complete => break,
974 _ => {}
975 }
976 }
977
978 assert!(received_initial);
979 assert!(stream_chunks > 0);
980 }
981
982 #[tokio::test]
983 async fn test_deferred_with_dependencies() {
984 let config = IncrementalConfig::new();
985 let executor = IncrementalExecutor::new(config);
986
987 let mut plan = ExecutionPlan::new();
988
989 let id1 = FragmentId::new("user.profile".to_string());
990 let id2 = FragmentId::new("user.posts".to_string());
991
992 let fragment1 = DeferredFragment::new(id1.clone(), vec!["bio".to_string()]);
993 let fragment2 = DeferredFragment::new(id2, vec!["posts".to_string()]).with_dependency(id1);
994
995 plan.add_deferred_fragment(fragment1);
996 plan.add_deferred_fragment(fragment2);
997
998 let initial_data = serde_json::json!({ "id": 1 });
999
1000 let mut stream = executor
1001 .execute(plan, initial_data)
1002 .await
1003 .expect("should succeed");
1004
1005 let mut deferred_count = 0;
1006 while let Some(event) = stream.next().await {
1007 match event.expect("should succeed") {
1008 IncrementalEvent::Deferred(_) => {
1009 deferred_count += 1;
1010 }
1011 IncrementalEvent::Complete => break,
1012 _ => {}
1013 }
1014 }
1015
1016 assert_eq!(deferred_count, 2);
1017 }
1018
1019 #[tokio::test]
1020 async fn test_conditional_defer() {
1021 let config = IncrementalConfig::new();
1022 let executor = IncrementalExecutor::new(config);
1023
1024 let mut plan = ExecutionPlan::new();
1025
1026 let id = FragmentId::new("user.posts".to_string());
1027 let fragment =
1028 DeferredFragment::new(id, vec!["posts".to_string()]).with_if_condition(false); plan.add_deferred_fragment(fragment);
1031
1032 let initial_data = serde_json::json!({ "id": 1 });
1033
1034 let mut stream = executor
1035 .execute(plan, initial_data)
1036 .await
1037 .expect("should succeed");
1038
1039 let mut deferred_count = 0;
1040 while let Some(event) = stream.next().await {
1041 match event.expect("should succeed") {
1042 IncrementalEvent::Deferred(_) => {
1043 deferred_count += 1;
1044 }
1045 IncrementalEvent::Complete => break,
1046 _ => {}
1047 }
1048 }
1049
1050 assert_eq!(deferred_count, 0);
1051 }
1052
1053 #[tokio::test]
1054 async fn test_execution_statistics() {
1055 let config = IncrementalConfig::new();
1056 let executor = IncrementalExecutor::new(config);
1057
1058 let mut plan = ExecutionPlan::new();
1059
1060 let id = FragmentId::new("user.posts".to_string());
1061 let fragment = DeferredFragment::new(id, vec!["posts".to_string()]);
1062 plan.add_deferred_fragment(fragment);
1063
1064 let initial_data = serde_json::json!({ "id": 1 });
1065
1066 let mut stream = executor
1067 .execute(plan, initial_data)
1068 .await
1069 .expect("should succeed");
1070
1071 while let Some(event) = stream.next().await {
1072 if matches!(event.expect("should succeed"), IncrementalEvent::Complete) {
1073 break;
1074 }
1075 }
1076
1077 let stats = stream.get_stats().await;
1078 assert!(stats.deferred_fragments_executed > 0);
1079 }
1080
1081 #[tokio::test]
1082 async fn test_parallel_execution_flag() {
1083 let config = IncrementalConfig::new().with_parallel_execution(true);
1084
1085 assert!(config.enable_parallel_execution);
1086 }
1087
1088 #[test]
1089 fn test_incremental_payload_creation() {
1090 let payload = IncrementalPayload {
1091 data: Some(serde_json::json!({"test": "data"})),
1092 path: vec!["user".to_string(), "posts".to_string()],
1093 label: Some("postData".to_string()),
1094 errors: Vec::new(),
1095 has_next: true,
1096 };
1097
1098 assert!(payload.data.is_some());
1099 assert_eq!(payload.path.len(), 2);
1100 assert_eq!(payload.label, Some("postData".to_string()));
1101 assert!(payload.has_next);
1102 }
1103
1104 #[test]
1105 fn test_incremental_error_creation() {
1106 let error = IncrementalError {
1107 message: "Test error".to_string(),
1108 path: vec!["user".to_string(), "posts".to_string()],
1109 extensions: HashMap::new(),
1110 };
1111
1112 assert_eq!(error.message, "Test error");
1113 assert_eq!(error.path.len(), 2);
1114 }
1115}