Skip to main content

camel_dsl/
model.rs

1pub use camel_api::{LanguageExpressionDef, StreamSplitConfig, ValueSourceDef};
2
3#[derive(Default)]
4pub struct SecurityCompileContext {
5    pub authenticator: Option<std::sync::Arc<dyn camel_auth::TokenAuthenticator>>,
6    pub registry: Option<std::sync::Arc<camel_auth::SecurityPolicyRegistry>>,
7    pub evaluator_registry: Option<std::sync::Arc<camel_auth::PermissionEvaluatorRegistry>>,
8}
9
10impl Clone for SecurityCompileContext {
11    fn clone(&self) -> Self {
12        Self {
13            authenticator: self.authenticator.clone(),
14            registry: self.registry.clone(),
15            evaluator_registry: self.evaluator_registry.clone(),
16        }
17    }
18}
19
20impl SecurityCompileContext {
21    pub fn new(
22        authenticator: Option<std::sync::Arc<dyn camel_auth::TokenAuthenticator>>,
23        registry: Option<std::sync::Arc<camel_auth::SecurityPolicyRegistry>>,
24    ) -> Self {
25        Self {
26            authenticator,
27            registry,
28            evaluator_registry: None,
29        }
30    }
31
32    pub fn with_evaluator_registry(
33        mut self,
34        registry: std::sync::Arc<camel_auth::PermissionEvaluatorRegistry>,
35    ) -> Self {
36        self.evaluator_registry = Some(registry);
37        self
38    }
39
40    pub fn with_security_policy_registry(
41        mut self,
42        registry: std::sync::Arc<camel_auth::SecurityPolicyRegistry>,
43    ) -> Self {
44        self.registry = Some(registry);
45        self
46    }
47}
48
49#[derive(Debug, Clone, PartialEq, Eq)]
50pub enum DeclarativeConcurrency {
51    Sequential,
52    Concurrent { max: Option<usize> },
53}
54
55#[derive(Debug, Clone, PartialEq, Eq)]
56pub struct DeclarativeCircuitBreaker {
57    pub failure_threshold: u32,
58    pub open_duration_ms: u64,
59}
60
61#[derive(Debug, Clone, PartialEq)]
62pub enum DeclarativeSecurityPolicy {
63    Roles {
64        roles: Vec<String>,
65        all_required: bool,
66        trust_upstream_principal: bool,
67    },
68    Scopes {
69        scopes: Vec<String>,
70        all_required: bool,
71        trust_upstream_principal: bool,
72    },
73    Ref {
74        name: String,
75    },
76    /// WASM security policy reference. The `path` field is the registry name
77    /// of a policy registered via `[security.policies.wasm.<name>]` in Camel.toml.
78    /// Per-route `config` is not supported (registry is instance-based, not
79    /// factory-based โ€” see ADR-0014 ยง4 closure bd rc-0te).
80    Wasm {
81        /// Registry name of the WASM policy (from `[security.policies.wasm.<name>]` in Camel.toml).
82        path: String,
83        /// Reserved โ€” must be empty. Use Camel.toml `[security.policies.wasm.<name>.config]` instead.
84        config: std::collections::HashMap<String, String>,
85    },
86    Permission {
87        policy: String,
88        resource: camel_auth::PermissionValueSource,
89        action: camel_auth::PermissionValueSource,
90        scopes: Vec<String>,
91        context: camel_auth::PermissionContextConfig,
92        cache_ttl_secs: Option<u64>,
93        cache_negative_ttl_secs: Option<u64>,
94    },
95}
96
97#[derive(Debug, Clone, PartialEq)]
98pub struct DeclarativeRedeliveryPolicy {
99    pub max_attempts: u32,
100    pub initial_delay_ms: u64,
101    pub multiplier: f64,
102    pub max_delay_ms: u64,
103    pub jitter_factor: f64,
104    pub handled_by: Option<String>,
105}
106
107#[derive(Debug, Clone, PartialEq)]
108pub struct DeclarativeOnException {
109    pub kind: Option<String>,
110    pub message_contains: Option<String>,
111    pub retry: Option<DeclarativeRedeliveryPolicy>,
112    pub steps: Vec<DeclarativeStep>,
113    pub handled: Option<bool>,
114    pub continued: Option<bool>,
115}
116
117#[derive(Debug, Clone, PartialEq, Default)]
118pub struct DeclarativeErrorHandler {
119    pub dead_letter_channel: Option<String>,
120    pub retry: Option<DeclarativeRedeliveryPolicy>,
121    pub on_exceptions: Option<Vec<DeclarativeOnException>>,
122    pub use_original_message: bool,
123}
124
125#[derive(Debug, Clone)]
126pub struct DeclarativeRoute {
127    pub from: String,
128    pub route_id: String,
129    pub auto_startup: bool,
130    pub startup_order: i32,
131    pub concurrency: Option<DeclarativeConcurrency>,
132    pub error_handler: Option<DeclarativeErrorHandler>,
133    pub circuit_breaker: Option<DeclarativeCircuitBreaker>,
134    pub security_policy: Option<DeclarativeSecurityPolicy>,
135    pub unit_of_work: Option<camel_api::UnitOfWorkConfig>,
136    pub steps: Vec<DeclarativeStep>,
137}
138
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct ToStepDef {
141    pub uri: String,
142}
143
144impl ToStepDef {
145    pub fn new(uri: impl Into<String>) -> Self {
146        Self { uri: uri.into() }
147    }
148}
149
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum LogLevelDef {
152    Trace,
153    Debug,
154    Info,
155    Warn,
156    Error,
157}
158
159// Note: `Eq` is not derived because `ValueSourceDef` contains `serde_json::Value`
160// which does not implement `Eq` (due to floating-point fields).
161#[derive(Debug, Clone, PartialEq)]
162pub struct LogStepDef {
163    pub message: ValueSourceDef,
164    pub level: LogLevelDef,
165}
166
167impl LogStepDef {
168    pub fn info(message: impl Into<String>) -> Self {
169        Self {
170            message: ValueSourceDef::Literal(serde_json::Value::String(message.into())),
171            level: LogLevelDef::Info,
172        }
173    }
174}
175
176#[derive(Debug, Clone, PartialEq)]
177pub struct SetHeaderStepDef {
178    pub key: String,
179    pub value: ValueSourceDef,
180}
181
182impl SetHeaderStepDef {
183    pub fn literal(key: impl Into<String>, value: impl Into<String>) -> Self {
184        Self {
185            key: key.into(),
186            value: ValueSourceDef::Literal(serde_json::Value::String(value.into())),
187        }
188    }
189}
190
191#[derive(Debug, Clone, PartialEq)]
192pub struct SetPropertyStepDef {
193    pub key: String,
194    pub value: ValueSourceDef,
195}
196
197impl SetPropertyStepDef {
198    pub fn literal(key: impl Into<String>, value: impl Into<String>) -> Self {
199        Self {
200            key: key.into(),
201            value: ValueSourceDef::Literal(serde_json::Value::String(value.into())),
202        }
203    }
204}
205
206#[derive(Debug, Clone, PartialEq)]
207pub struct SetBodyStepDef {
208    pub value: ValueSourceDef,
209}
210
211#[derive(Debug, Clone, PartialEq)]
212pub struct FilterStepDef {
213    pub predicate: LanguageExpressionDef,
214    pub steps: Vec<DeclarativeStep>,
215}
216
217#[derive(Debug, Clone, PartialEq, Eq)]
218pub struct FunctionStepDef {
219    pub runtime: String,
220    pub source: String,
221    pub timeout_ms: Option<u64>,
222}
223
224#[derive(Debug, Clone, PartialEq)]
225pub struct WhenStepDef {
226    pub predicate: LanguageExpressionDef,
227    pub steps: Vec<DeclarativeStep>,
228}
229
230#[derive(Debug, Clone, PartialEq)]
231pub struct ChoiceStepDef {
232    pub whens: Vec<WhenStepDef>,
233    pub otherwise: Option<Vec<DeclarativeStep>>,
234}
235
236#[derive(Debug, Clone, PartialEq)]
237pub enum SplitExpressionDef {
238    BodyLines,
239    BodyJsonArray,
240    Language(LanguageExpressionDef),
241    Stream(StreamSplitConfig),
242}
243
244#[derive(Debug, Clone, PartialEq, Eq)]
245pub enum SplitAggregationDef {
246    LastWins,
247    CollectAll,
248    Original,
249}
250
251#[derive(Debug, Clone, PartialEq)]
252pub struct SplitStepDef {
253    pub expression: SplitExpressionDef,
254    pub aggregation: SplitAggregationDef,
255    pub parallel: bool,
256    pub parallel_limit: Option<usize>,
257    pub stop_on_exception: bool,
258    pub steps: Vec<DeclarativeStep>,
259}
260
261#[derive(Debug, Clone, PartialEq, Eq)]
262pub enum AggregateStrategyDef {
263    CollectAll,
264}
265
266#[derive(Debug, Clone, PartialEq)]
267pub struct AggregateStepDef {
268    pub header: String,
269    pub correlation_key: Option<String>,
270    pub completion_size: Option<usize>,
271    pub completion_timeout_ms: Option<u64>,
272    pub completion_predicate: Option<LanguageExpressionDef>,
273    pub strategy: AggregateStrategyDef,
274    pub max_buckets: Option<usize>,
275    pub bucket_ttl_ms: Option<u64>,
276    pub force_completion_on_stop: Option<bool>,
277    pub discard_on_timeout: Option<bool>,
278}
279
280#[derive(Debug, Clone, PartialEq, Eq)]
281pub struct WireTapStepDef {
282    pub uri: String,
283}
284
285#[derive(Debug, Clone, PartialEq, Eq)]
286pub struct BeanStepDef {
287    pub name: String,
288    pub method: String,
289}
290
291impl BeanStepDef {
292    pub fn new(name: impl Into<String>, method: impl Into<String>) -> Self {
293        Self {
294            name: name.into(),
295            method: method.into(),
296        }
297    }
298}
299
300#[derive(Debug, Clone, PartialEq, Eq, Default)]
301pub enum ThrottleStrategyDef {
302    #[default]
303    Delay,
304    Reject,
305    Drop,
306}
307
308#[derive(Debug, Clone, PartialEq)]
309pub struct ThrottleStepDef {
310    pub max_requests: usize,
311    pub period_ms: u64,
312    pub strategy: ThrottleStrategyDef,
313    pub steps: Vec<DeclarativeStep>,
314}
315
316#[derive(Debug, Clone, PartialEq, Eq, Default)]
317pub enum LoadBalanceStrategyDef {
318    #[default]
319    RoundRobin,
320    Random,
321    Failover,
322    Weighted {
323        distribution_ratio: String,
324    },
325}
326
327#[derive(Debug, Clone, PartialEq)]
328pub struct LoadBalanceStepDef {
329    pub strategy: LoadBalanceStrategyDef,
330    pub steps: Vec<DeclarativeStep>,
331}
332
333#[derive(Debug, Clone, PartialEq, Eq)]
334pub struct DynamicRouterStepDef {
335    pub expression: LanguageExpressionDef,
336    pub uri_delimiter: String,
337    pub cache_size: i32,
338    pub ignore_invalid_endpoints: bool,
339    pub max_iterations: usize,
340}
341
342#[derive(Debug, Clone, PartialEq, Eq)]
343pub struct RoutingSlipStepDef {
344    pub expression: LanguageExpressionDef,
345    pub uri_delimiter: String,
346    pub cache_size: i32,
347    pub ignore_invalid_endpoints: bool,
348}
349
350#[derive(Debug, Clone, PartialEq, Eq)]
351pub struct RecipientListStepDef {
352    pub expression: LanguageExpressionDef,
353    pub delimiter: String,
354    pub parallel: bool,
355    pub parallel_limit: Option<usize>,
356    pub stop_on_exception: bool,
357    pub aggregation: MulticastAggregationDef,
358}
359
360#[derive(Debug, Clone, PartialEq, Eq)]
361pub enum MulticastAggregationDef {
362    LastWins,
363    CollectAll,
364    Original,
365}
366
367#[derive(Debug, Clone, PartialEq)]
368pub struct MulticastStepDef {
369    pub steps: Vec<DeclarativeStep>,
370    pub parallel: bool,
371    pub parallel_limit: Option<usize>,
372    pub stop_on_exception: bool,
373    pub timeout_ms: Option<u64>,
374    pub aggregation: MulticastAggregationDef,
375}
376
377#[derive(Debug, Clone, PartialEq, Eq)]
378pub struct ScriptStepDef {
379    pub expression: LanguageExpressionDef,
380}
381
382#[derive(Debug, Clone, PartialEq, Eq)]
383pub enum BodyTypeDef {
384    Text,
385    Json,
386    Bytes,
387    Xml,
388    Empty,
389}
390
391#[derive(Debug, Clone, PartialEq)]
392pub struct DataFormatDef {
393    pub format: String,
394    /// Optional JSON Schema for request-body validation (REST DSL
395    /// `request_schema`). When present, the compiled UnmarshalService is
396    /// wrapped with a `JsonSchemaValidateService`.
397    pub schema: Option<serde_json::Value>,
398    /// Optional per-format configuration (e.g. `{ "max_bytes": 67108864 }`).
399    /// Deserialized by the config-aware factory per ADR-0038.
400    pub config: Option<serde_json::Value>,
401}
402
403#[derive(Debug, Clone, PartialEq, Eq)]
404pub struct DelayStepDef {
405    pub delay_ms: u64,
406    pub dynamic_header: Option<String>,
407}
408
409#[derive(Debug, Clone, PartialEq)]
410pub struct LoopStepDef {
411    pub count: Option<usize>,
412    pub while_predicate: Option<LanguageExpressionDef>,
413    pub steps: Vec<DeclarativeStep>,
414    pub max_iterations: Option<usize>,
415}
416
417#[derive(Debug, Clone, PartialEq, Eq)]
418pub struct StreamCacheStepDef {
419    pub threshold: Option<usize>,
420}
421
422#[derive(Debug, Clone, PartialEq, Eq)]
423pub struct ValidateStepDef {
424    pub predicate: LanguageExpressionDef,
425}
426
427/// Claim Check EIP step definition.
428///
429/// Stashes/retrieves the message body from a `ClaimCheckRepository` by key.
430#[derive(Debug, Clone, PartialEq, Eq)]
431pub struct ClaimCheckStepDef {
432    /// Name of the registered `ClaimCheckRepository` (e.g. `"memory"`).
433    pub repository: String,
434    /// Operation: "set", "get", "get_and_remove", "push", "pop".
435    pub operation: String,
436    /// Expression that extracts the claim-check key from the exchange.
437    pub key: LanguageExpressionDef,
438    /// Optional filter string for selective merge-back during checkout operations.
439    pub filter: Option<String>,
440}
441
442/// Idempotent Consumer EIP step definition.
443///
444/// Wraps a child sub-pipeline that runs only when the exchange's message-id
445/// is NOT already present in the named `repository`. See ADR-0023.
446#[derive(Debug, Clone, PartialEq)]
447pub struct IdempotentConsumerStepDef {
448    /// Name of the registered `IdempotentRepository` (e.g. `"memory"`).
449    pub repository: String,
450    /// Expression that extracts the message-id key from the exchange.
451    pub expression: LanguageExpressionDef,
452    /// Child sub-pipeline executed on first-time (non-duplicate) exchanges.
453    pub steps: Vec<DeclarativeStep>,
454    /// If `true`, reserve the key in the repository BEFORE running the child
455    /// (eager mode). Default `false` (lazy: add only after the child completes).
456    pub eager: Option<bool>,
457    /// If `true` and `eager` is `true`, remove the key from the repository
458    /// when the child returns `Failed`. Default `false`.
459    pub remove_on_failure: Option<bool>,
460}
461
462/// Sampling EIP step definition.
463///
464/// Passes 1 of every N exchanges (counter-based, deterministic).
465#[derive(Debug, Clone, PartialEq, Eq)]
466pub struct SamplingStepDef {
467    /// Sampling period: 1 of every `period` exchanges passes.
468    pub period: usize,
469}
470
471/// Sort EIP step definition.
472///
473/// Orders a body collection by extracting a sort key from each element
474/// via a language expression.
475#[derive(Debug, Clone, PartialEq, Eq)]
476pub struct SortStepDef {
477    /// Expression that produces the sort key for each element.
478    pub expression: LanguageExpressionDef,
479    /// Reverse (descending) sort when true. Default false (ascending).
480    pub reverse: bool,
481}
482
483/// Resequence EIP step definition (Phase 3).
484///
485/// Supports batch and stream modes.
486#[derive(Debug, Clone, PartialEq, Eq)]
487pub struct ResequenceStepDef {
488    pub mode: ResequenceModeDef,
489}
490
491/// Resequence mode selection โ€” batch or stream.
492#[derive(Debug, Clone, PartialEq, Eq)]
493pub enum ResequenceModeDef {
494    Batch {
495        correlation: String,
496        sort: String,
497        completion: camel_api::resequencer::BatchCompletion,
498    },
499    Stream {
500        sequence: String,
501        capacity: usize,
502        gap_timeout: u64,
503        on_gap: camel_api::resequencer::GapPolicy,
504        on_capacity_exceeded: camel_api::resequencer::CapacityPolicy,
505        dedup: bool,
506    },
507}
508
509#[derive(Debug, Clone, PartialEq, Eq)]
510pub struct EnrichStepDef {
511    pub uri: String,
512    pub strategy: Option<String>,
513    pub timeout_ms: Option<u64>,
514}
515
516#[derive(Debug, Clone, PartialEq)]
517pub struct DoTryCatchClauseDef {
518    pub exception: Option<Vec<String>>,
519    pub when: Option<LanguageExpressionDef>,
520    pub on_when: Option<LanguageExpressionDef>,
521    pub disposition: camel_api::error_handler::ExceptionDisposition,
522    pub steps: Vec<DeclarativeStep>,
523}
524
525#[derive(Debug, Clone, PartialEq)]
526pub struct DoTryFinallyDef {
527    pub on_when: Option<LanguageExpressionDef>,
528    pub steps: Vec<DeclarativeStep>,
529}
530
531#[derive(Debug, Clone, PartialEq)]
532pub enum DeclarativeStep {
533    To(ToStepDef),
534    SetHeader(SetHeaderStepDef),
535    SetHeaderIfAbsent(SetHeaderStepDef),
536    SetProperty(SetPropertyStepDef),
537    SetBody(SetBodyStepDef),
538    ConvertBodyTo(BodyTypeDef),
539    DynamicRouter(DynamicRouterStepDef),
540    Filter(FilterStepDef),
541    Function(FunctionStepDef),
542    LoadBalance(LoadBalanceStepDef),
543    Log(LogStepDef),
544    Choice(ChoiceStepDef),
545    Split(SplitStepDef),
546    Aggregate(AggregateStepDef),
547    WireTap(WireTapStepDef),
548    Multicast(MulticastStepDef),
549    RoutingSlip(RoutingSlipStepDef),
550    RecipientList(RecipientListStepDef),
551    Stop,
552    Throttle(ThrottleStepDef),
553    Script(ScriptStepDef),
554    StreamCache(StreamCacheStepDef),
555    Marshal(DataFormatDef),
556    Unmarshal(DataFormatDef),
557    Validate(ValidateStepDef),
558    Bean(BeanStepDef),
559    Delay(DelayStepDef),
560    Loop(LoopStepDef),
561    Enrich(EnrichStepDef),
562    PollEnrich(EnrichStepDef),
563    IdempotentConsumer(IdempotentConsumerStepDef),
564    ClaimCheck(ClaimCheckStepDef),
565    Sampling(SamplingStepDef),
566    Sort(SortStepDef),
567    Resequence(ResequenceStepDef),
568    DoTry {
569        steps: Vec<DeclarativeStep>,
570        catch: Vec<DoTryCatchClauseDef>,
571        finally: Option<DoTryFinallyDef>,
572    },
573}
574
575impl DeclarativeStep {
576    pub fn kind(&self) -> crate::contract::DeclarativeStepKind {
577        match self {
578            DeclarativeStep::To(_) => crate::contract::DeclarativeStepKind::To,
579            DeclarativeStep::Log(_) => crate::contract::DeclarativeStepKind::Log,
580            DeclarativeStep::SetHeader(_) => crate::contract::DeclarativeStepKind::SetHeader,
581            DeclarativeStep::SetHeaderIfAbsent(_) => {
582                crate::contract::DeclarativeStepKind::SetHeaderIfAbsent
583            }
584            DeclarativeStep::SetProperty(_) => crate::contract::DeclarativeStepKind::SetProperty,
585            DeclarativeStep::SetBody(_) => crate::contract::DeclarativeStepKind::SetBody,
586            DeclarativeStep::ConvertBodyTo(_) => {
587                crate::contract::DeclarativeStepKind::ConvertBodyTo
588            }
589            DeclarativeStep::DynamicRouter(_) => {
590                crate::contract::DeclarativeStepKind::DynamicRouter
591            }
592            DeclarativeStep::Filter(_) => crate::contract::DeclarativeStepKind::Filter,
593            DeclarativeStep::Function(_) => crate::contract::DeclarativeStepKind::Function,
594            DeclarativeStep::LoadBalance(_) => crate::contract::DeclarativeStepKind::LoadBalance,
595            DeclarativeStep::Choice(_) => crate::contract::DeclarativeStepKind::Choice,
596            DeclarativeStep::Split(_) => crate::contract::DeclarativeStepKind::Split,
597            DeclarativeStep::Aggregate(_) => crate::contract::DeclarativeStepKind::Aggregate,
598            DeclarativeStep::WireTap(_) => crate::contract::DeclarativeStepKind::WireTap,
599            DeclarativeStep::Multicast(_) => crate::contract::DeclarativeStepKind::Multicast,
600            DeclarativeStep::RoutingSlip(_) => crate::contract::DeclarativeStepKind::RoutingSlip,
601            DeclarativeStep::RecipientList(_) => {
602                crate::contract::DeclarativeStepKind::RecipientList
603            }
604            DeclarativeStep::Stop => crate::contract::DeclarativeStepKind::Stop,
605            DeclarativeStep::Throttle(_) => crate::contract::DeclarativeStepKind::Throttle,
606            DeclarativeStep::Script(_) => crate::contract::DeclarativeStepKind::Script,
607            DeclarativeStep::StreamCache(_) => crate::contract::DeclarativeStepKind::StreamCache,
608            DeclarativeStep::Marshal(_) => crate::contract::DeclarativeStepKind::Marshal,
609            DeclarativeStep::Unmarshal(_) => crate::contract::DeclarativeStepKind::Unmarshal,
610            DeclarativeStep::Validate(_) => crate::contract::DeclarativeStepKind::Validate,
611            DeclarativeStep::Bean(_) => crate::contract::DeclarativeStepKind::Bean,
612            DeclarativeStep::Delay(_) => crate::contract::DeclarativeStepKind::Delay,
613            DeclarativeStep::Loop(_) => crate::contract::DeclarativeStepKind::Loop,
614            DeclarativeStep::Enrich(_) => crate::contract::DeclarativeStepKind::Enrich,
615            DeclarativeStep::PollEnrich(_) => crate::contract::DeclarativeStepKind::PollEnrich,
616            DeclarativeStep::IdempotentConsumer(_) => {
617                crate::contract::DeclarativeStepKind::IdempotentConsumer
618            }
619            DeclarativeStep::ClaimCheck(_) => crate::contract::DeclarativeStepKind::ClaimCheck,
620            DeclarativeStep::Sampling(_) => crate::contract::DeclarativeStepKind::Sampling,
621            DeclarativeStep::Sort(_) => crate::contract::DeclarativeStepKind::Sort,
622            DeclarativeStep::Resequence(_) => crate::contract::DeclarativeStepKind::Resequence,
623            DeclarativeStep::DoTry { .. } => crate::contract::DeclarativeStepKind::DoTry,
624        }
625    }
626}
627
628#[cfg(test)]
629mod tests {
630    use super::*;
631
632    #[test]
633    fn to_step_def_new() {
634        let def = ToStepDef::new("direct:a");
635        assert_eq!(def.uri, "direct:a");
636    }
637
638    #[test]
639    fn log_step_def_info() {
640        let def = LogStepDef::info("hello");
641        assert_eq!(def.level, LogLevelDef::Info);
642        match def.message {
643            ValueSourceDef::Literal(v) => assert_eq!(v, serde_json::Value::String("hello".into())),
644            _ => panic!("expected literal"),
645        }
646    }
647
648    #[test]
649    fn set_header_literal() {
650        let def = SetHeaderStepDef::literal("key", "value");
651        assert_eq!(def.key, "key");
652        match def.value {
653            ValueSourceDef::Literal(v) => assert_eq!(v, serde_json::Value::String("value".into())),
654            _ => panic!("expected literal"),
655        }
656    }
657
658    #[test]
659    fn bean_step_def_new() {
660        let def = BeanStepDef::new("myBean", "process");
661        assert_eq!(def.name, "myBean");
662        assert_eq!(def.method, "process");
663    }
664
665    #[test]
666    fn throttle_strategy_default() {
667        assert_eq!(ThrottleStrategyDef::default(), ThrottleStrategyDef::Delay);
668    }
669
670    #[test]
671    fn load_balance_strategy_default() {
672        assert_eq!(
673            LoadBalanceStrategyDef::default(),
674            LoadBalanceStrategyDef::RoundRobin
675        );
676    }
677
678    #[test]
679    fn concurrency_variants_equality() {
680        assert_eq!(
681            DeclarativeConcurrency::Sequential,
682            DeclarativeConcurrency::Sequential
683        );
684        assert_ne!(
685            DeclarativeConcurrency::Sequential,
686            DeclarativeConcurrency::Concurrent { max: None }
687        );
688    }
689
690    #[test]
691    fn body_type_variants() {
692        assert_eq!(BodyTypeDef::Text, BodyTypeDef::Text);
693        assert_ne!(BodyTypeDef::Text, BodyTypeDef::Json);
694    }
695
696    #[test]
697    fn data_format_def() {
698        let def = DataFormatDef {
699            format: "protobuf".into(),
700            schema: None,
701            config: None,
702        };
703        assert_eq!(def.format, "protobuf");
704        assert!(def.schema.is_none());
705    }
706
707    #[test]
708    fn stream_cache_step_def() {
709        let def = StreamCacheStepDef {
710            threshold: Some(1024),
711        };
712        assert_eq!(def.threshold, Some(1024));
713    }
714
715    #[test]
716    fn delay_step_def() {
717        let def = DelayStepDef {
718            delay_ms: 500,
719            dynamic_header: Some("X-Delay".into()),
720        };
721        assert_eq!(def.delay_ms, 500);
722        assert_eq!(def.dynamic_header.as_deref(), Some("X-Delay"));
723    }
724
725    #[test]
726    fn circuit_breaker_def() {
727        let cb = DeclarativeCircuitBreaker {
728            failure_threshold: 3,
729            open_duration_ms: 5000,
730        };
731        assert_eq!(cb.failure_threshold, 3);
732        assert_eq!(cb.open_duration_ms, 5000);
733    }
734}