Skip to main content

oxirs_gql/
incremental_execution.rs

1//! Incremental Query Execution
2//!
3//! This module provides incremental execution capabilities for GraphQL queries,
4//! allowing queries to be partially executed and results to be delivered progressively.
5//!
6//! # Features
7//!
8//! - **@defer Directive**: Defer non-critical fields for later execution
9//! - **@stream Directive**: Stream list items incrementally
10//! - **Execution Scheduling**: Priority-based execution scheduling
11//! - **Partial Results**: Deliver partial results as they become available
12//! - **Error Isolation**: Isolate errors to specific fragments without failing entire query
13//! - **Dependency Tracking**: Track field dependencies for correct execution order
14//!
15//! # Example
16//!
17//! ```graphql
18//! query {
19//!   user(id: "123") {
20//!     id
21//!     name
22//!     ... @defer {
23//!       posts {
24//!         title
25//!         content
26//!       }
27//!     }
28//!     friends @stream(initialCount: 10) {
29//!       id
30//!       name
31//!     }
32//!   }
33//! }
34//! ```
35
36use serde::{Deserialize, Serialize};
37use std::collections::{HashMap, HashSet};
38use std::sync::Arc;
39use std::time::{Duration, Instant};
40use tokio::sync::{mpsc, RwLock};
41
42/// Configuration for incremental execution
43#[derive(Debug, Clone)]
44pub struct IncrementalConfig {
45    /// Enable @defer directive support
46    pub enable_defer: bool,
47    /// Enable @stream directive support
48    pub enable_stream: bool,
49    /// Default initial count for @stream
50    pub default_stream_initial_count: usize,
51    /// Maximum concurrent deferred fragments
52    pub max_concurrent_defers: usize,
53    /// Timeout for individual fragment execution
54    pub fragment_timeout: Duration,
55    /// Enable parallel execution of independent fragments
56    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    /// Create new configuration with defaults
74    pub fn new() -> Self {
75        Self::default()
76    }
77
78    /// Set whether to enable @defer directive
79    pub fn with_defer(mut self, enabled: bool) -> Self {
80        self.enable_defer = enabled;
81        self
82    }
83
84    /// Set whether to enable @stream directive
85    pub fn with_stream(mut self, enabled: bool) -> Self {
86        self.enable_stream = enabled;
87        self
88    }
89
90    /// Set default initial count for @stream
91    pub fn with_stream_initial_count(mut self, count: usize) -> Self {
92        self.default_stream_initial_count = count;
93        self
94    }
95
96    /// Set maximum concurrent deferred fragments
97    pub fn with_max_concurrent_defers(mut self, max: usize) -> Self {
98        self.max_concurrent_defers = max;
99        self
100    }
101
102    /// Set fragment execution timeout
103    pub fn with_fragment_timeout(mut self, timeout: Duration) -> Self {
104        self.fragment_timeout = timeout;
105        self
106    }
107
108    /// Enable parallel execution
109    pub fn with_parallel_execution(mut self, enabled: bool) -> Self {
110        self.enable_parallel_execution = enabled;
111        self
112    }
113}
114
115/// Execution phase
116#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
117pub enum ExecutionPhase {
118    /// Initial synchronous execution
119    Initial,
120    /// Deferred fragment execution
121    Deferred,
122    /// Streaming execution
123    Streaming,
124}
125
126/// Fragment identifier
127#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
128pub struct FragmentId {
129    /// Fragment path (e.g., "user.posts")
130    pub path: String,
131    /// Fragment label (optional)
132    pub label: Option<String>,
133}
134
135impl FragmentId {
136    /// Create a new fragment ID
137    pub fn new(path: String) -> Self {
138        Self { path, label: None }
139    }
140
141    /// Create with label
142    pub fn with_label(mut self, label: String) -> Self {
143        self.label = Some(label);
144        self
145    }
146}
147
148/// Deferred fragment
149#[derive(Debug, Clone)]
150pub struct DeferredFragment {
151    /// Fragment identifier
152    pub id: FragmentId,
153    /// Fields to execute
154    pub fields: Vec<String>,
155    /// Priority (higher = execute sooner)
156    pub priority: u8,
157    /// Dependencies (fragment IDs that must complete first)
158    pub dependencies: Vec<FragmentId>,
159    /// If clause condition
160    pub if_condition: bool,
161}
162
163impl DeferredFragment {
164    /// Create a new deferred fragment
165    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    /// Set priority
176    pub fn with_priority(mut self, priority: u8) -> Self {
177        self.priority = priority;
178        self
179    }
180
181    /// Add dependency
182    pub fn with_dependency(mut self, dep: FragmentId) -> Self {
183        self.dependencies.push(dep);
184        self
185    }
186
187    /// Set if condition
188    pub fn with_if_condition(mut self, condition: bool) -> Self {
189        self.if_condition = condition;
190        self
191    }
192}
193
194/// Stream directive configuration
195#[derive(Debug, Clone)]
196pub struct StreamConfig {
197    /// Fragment identifier
198    pub id: FragmentId,
199    /// Field to stream
200    pub field: String,
201    /// Initial count to return synchronously
202    pub initial_count: usize,
203    /// If clause condition
204    pub if_condition: bool,
205    /// Label for this stream
206    pub label: Option<String>,
207}
208
209impl StreamConfig {
210    /// Create a new stream configuration
211    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    /// Set if condition
222    pub fn with_if_condition(mut self, condition: bool) -> Self {
223        self.if_condition = condition;
224        self
225    }
226
227    /// Set label
228    pub fn with_label(mut self, label: String) -> Self {
229        self.label = Some(label);
230        self
231    }
232}
233
234/// Incremental result payload
235#[derive(Debug, Clone, Serialize, Deserialize)]
236pub struct IncrementalPayload {
237    /// Data for this payload
238    pub data: Option<serde_json::Value>,
239    /// Path to where this data should be merged
240    pub path: Vec<String>,
241    /// Label identifying this payload
242    pub label: Option<String>,
243    /// Errors in this payload
244    pub errors: Vec<IncrementalError>,
245    /// Whether this completes the incremental delivery
246    pub has_next: bool,
247}
248
249/// Error in incremental execution
250#[derive(Debug, Clone, Serialize, Deserialize)]
251pub struct IncrementalError {
252    /// Error message
253    pub message: String,
254    /// Path where error occurred
255    pub path: Vec<String>,
256    /// Extensions
257    pub extensions: HashMap<String, serde_json::Value>,
258}
259
260/// Initial execution result
261#[derive(Debug, Clone, Serialize, Deserialize)]
262pub struct InitialResult {
263    /// Initial data
264    pub data: serde_json::Value,
265    /// Initial errors
266    pub errors: Vec<IncrementalError>,
267    /// Whether there are pending deferred/streamed results
268    pub has_next: bool,
269}
270
271/// Execution plan for incremental execution
272#[derive(Debug, Clone)]
273pub struct ExecutionPlan {
274    /// Fields to execute immediately
275    pub immediate_fields: Vec<String>,
276    /// Deferred fragments
277    pub deferred_fragments: Vec<DeferredFragment>,
278    /// Stream configurations
279    pub streams: Vec<StreamConfig>,
280    /// Fragment dependency graph
281    pub dependencies: HashMap<FragmentId, Vec<FragmentId>>,
282}
283
284impl ExecutionPlan {
285    /// Create a new execution plan
286    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    /// Add immediate field
296    pub fn add_immediate_field(&mut self, field: String) {
297        self.immediate_fields.push(field);
298    }
299
300    /// Add deferred fragment
301    pub fn add_deferred_fragment(&mut self, fragment: DeferredFragment) {
302        // Update dependency graph
303        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    /// Add stream configuration
313    pub fn add_stream(&mut self, stream: StreamConfig) {
314        self.streams.push(stream);
315    }
316
317    /// Get execution order for deferred fragments (topological sort)
318    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            // Circular dependency detected
343            return;
344        }
345
346        in_progress.insert(id.clone());
347
348        // Visit dependencies first
349        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
367/// Incremental executor
368pub struct IncrementalExecutor {
369    config: IncrementalConfig,
370    stats: Arc<RwLock<ExecutionStats>>,
371}
372
373impl IncrementalExecutor {
374    /// Create a new incremental executor
375    pub fn new(config: IncrementalConfig) -> Self {
376        Self {
377            config,
378            stats: Arc::new(RwLock::new(ExecutionStats::new())),
379        }
380    }
381
382    /// Execute a query incrementally
383    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        // Send initial result
393        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        // Spawn task to handle deferred execution
413        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        // Spawn task to handle streaming
425        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            // No deferred or streams, send complete event
434            let _ = tx.send(Ok(IncrementalEvent::Complete)).await;
435        }
436
437        Ok(IncrementalResultStream::new(rx, self.stats.clone()))
438    }
439
440    /// Execute deferred fragments
441    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            // Find the fragment
452            let fragment = match plan.deferred_fragments.iter().find(|f| f.id == fragment_id) {
453                Some(f) => f,
454                None => continue,
455            };
456
457            // Check if condition
458            if !fragment.if_condition {
459                completed.insert(fragment_id.clone());
460                continue;
461            }
462
463            // Wait for dependencies
464            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            // Execute fragment (mock execution for now)
473            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            // Update stats
488            {
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            // Check max concurrent limit
505            if completed.len() >= config.max_concurrent_defers {
506                tokio::time::sleep(Duration::from_millis(1)).await;
507            }
508        }
509
510        // Send completion
511        let _ = tx.send(Ok(IncrementalEvent::Complete)).await;
512    }
513
514    /// Execute streaming fields
515    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            // Simulate streaming items (mock implementation)
526            let total_items = 50; // Mock total
527            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                // Update stats
547                {
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    /// Get execution statistics
570    pub async fn get_stats(&self) -> ExecutionStats {
571        self.stats.read().await.clone()
572    }
573}
574
575/// Incremental event
576#[derive(Debug, Clone, Serialize, Deserialize)]
577pub enum IncrementalEvent {
578    /// Initial result
579    Initial(InitialResult),
580    /// Deferred fragment result
581    Deferred(IncrementalPayload),
582    /// Stream chunk
583    Stream(IncrementalPayload),
584    /// Execution complete
585    Complete,
586    /// Error occurred
587    Error(IncrementalError),
588}
589
590/// Result type for incremental execution
591pub type IncrementalResult = Result<IncrementalEvent, ExecutionError>;
592
593/// Incremental result stream
594pub struct IncrementalResultStream {
595    receiver: mpsc::Receiver<IncrementalResult>,
596    stats: Arc<RwLock<ExecutionStats>>,
597}
598
599impl IncrementalResultStream {
600    /// Create a new result stream
601    pub fn new(
602        receiver: mpsc::Receiver<IncrementalResult>,
603        stats: Arc<RwLock<ExecutionStats>>,
604    ) -> Self {
605        Self { receiver, stats }
606    }
607
608    /// Get next event from stream
609    pub async fn next(&mut self) -> Option<IncrementalResult> {
610        self.receiver.recv().await
611    }
612
613    /// Get current statistics
614    pub async fn get_stats(&self) -> ExecutionStats {
615        self.stats.read().await.clone()
616    }
617}
618
619/// Execution statistics
620#[derive(Debug, Clone, Serialize, Deserialize)]
621pub struct ExecutionStats {
622    /// Number of deferred fragments executed
623    pub deferred_fragments_executed: usize,
624    /// Number of stream chunks sent
625    pub stream_chunks_sent: usize,
626    /// Total items streamed
627    pub total_stream_items: usize,
628    /// Total fragment execution time
629    pub total_fragment_execution_time: Duration,
630    /// Errors encountered
631    pub errors: usize,
632}
633
634impl ExecutionStats {
635    /// Create new statistics
636    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    /// Record an error
647    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/// Errors that can occur during incremental execution
659#[derive(Debug, thiserror::Error)]
660pub enum ExecutionError {
661    /// Channel error
662    #[error("Channel error: {0}")]
663    ChannelError(String),
664
665    /// Timeout error
666    #[error("Fragment execution timeout")]
667    Timeout,
668
669    /// Dependency cycle detected
670    #[error("Circular dependency detected in fragment: {0}")]
671    CircularDependency(String),
672
673    /// Invalid configuration
674    #[error("Invalid configuration: {0}")]
675    InvalidConfig(String),
676
677    /// Execution error
678    #[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        // a -> b -> d
825        // a -> c -> d
826        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        // b and c can be in any order
843        assert!(order[3] == id4); // d must be last
844    }
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); // Should be skipped
1029
1030        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}