Skip to main content

swf_core/models/
call.rs

1use super::task::TaskDefinitionFields;
2use crate::models::authentication::*;
3use crate::models::resource::*;
4use serde::{Deserialize, Serialize};
5use serde_json::Value;
6use std::collections::HashMap;
7
8string_constants! {
9    /// Enumerates all supported call types
10    CallType {
11        ASYNCAPI => "asyncapi",
12        GRPC => "grpc",
13        HTTP => "http",
14        OPENAPI => "openapi",
15        A2A => "a2a",
16    }
17}
18
19string_constants! {
20    /// Enumerates all supported AsyncAPI protocols
21    AsyncApiProtocol {
22        AMQP => "amqp",
23        AMQP1 => "amqp1",
24        ANYPOINTMQ => "anypointmq",
25        GOOGLE_PUBSUB => "googlepubsub",
26        HTTP => "http",
27        IBMMQ => "ibmmq",
28        JMS => "jms",
29        KAFKA => "kafka",
30        MERCURE => "mercure",
31        MQTT => "mqtt",
32        MQTT5 => "mqtt5",
33        NATS => "nats",
34        PULSAR => "pulsar",
35        REDIS => "redis",
36        SNS => "sns",
37        SOLACE => "solace",
38        SQS => "sqs",
39        STOMP => "stomp",
40        WS => "ws",
41    }
42}
43
44string_constants! {
45    /// Enumerates all supported A2A methods
46    A2AMethod {
47        MESSAGE_SEND => "message/send",
48        MESSAGE_STREAM => "message/stream",
49        TASKS_GET => "tasks/get",
50        TASKS_LIST => "tasks/list",
51        TASKS_CANCEL => "tasks/cancel",
52        TASKS_RESUBSCRIBE => "tasks/resubscribe",
53        TASKS_PUSH_NOTIFICATION_CONFIG_SET => "tasks/pushNotificationConfig/set",
54        TASKS_PUSH_NOTIFICATION_CONFIG_GET => "tasks/pushNotificationConfig/get",
55        TASKS_PUSH_NOTIFICATION_CONFIG_LIST => "tasks/pushNotificationConfig/list",
56        TASKS_PUSH_NOTIFICATION_CONFIG_DELETE => "tasks/pushNotificationConfig/delete",
57        AGENT_GET_AUTHENTICATED_EXTENDED_CARD => "agent/getAuthenticatedExtendedCard",
58    }
59}
60
61/// Represents a value that can be any of the supported call task definitions
62#[derive(Debug, Clone, PartialEq, Serialize)]
63#[serde(untagged)]
64#[allow(clippy::large_enum_variant)]
65pub enum CallTaskDefinition {
66    /// Variant holding the definition of an AsyncAPI call
67    AsyncAPI(CallAsyncAPIDefinition),
68    /// Variant holding the definition of a GRPC call
69    GRPC(Box<CallGRPCDefinition>),
70    /// Variant holding the definition of an HTTP call
71    HTTP(CallHTTPDefinition),
72    /// Variant holding the definition of an OpenAPI call
73    OpenAPI(CallOpenAPIDefinition),
74    /// Variant holding the definition of an A2A call
75    A2A(CallA2ADefinition),
76    /// Variant holding the definition of a function call
77    Function(CallFunctionDefinition),
78}
79
80impl CallTaskDefinition {
81    /// Returns the common fields shared by all call task definition variants.
82    pub fn common_fields(&self) -> &super::task::TaskDefinitionFields {
83        match self {
84            CallTaskDefinition::HTTP(t) => &t.common,
85            CallTaskDefinition::GRPC(t) => &t.common,
86            CallTaskDefinition::OpenAPI(t) => &t.common,
87            CallTaskDefinition::AsyncAPI(t) => &t.common,
88            CallTaskDefinition::A2A(t) => &t.common,
89            CallTaskDefinition::Function(t) => &t.common,
90        }
91    }
92
93    /// Returns the call type name string for this variant (e.g., "http", "grpc", "openapi").
94    /// Used for handler lookup and error messages.
95    pub fn call_type_name(&self) -> &'static str {
96        match self {
97            CallTaskDefinition::HTTP(_) => "http",
98            CallTaskDefinition::GRPC(_) => "grpc",
99            CallTaskDefinition::OpenAPI(_) => "openapi",
100            CallTaskDefinition::AsyncAPI(_) => "asyncapi",
101            CallTaskDefinition::A2A(_) => "a2a",
102            CallTaskDefinition::Function(_) => "function",
103        }
104    }
105}
106
107impl<'de> serde::Deserialize<'de> for CallTaskDefinition {
108    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
109    where
110        D: serde::Deserializer<'de>,
111    {
112        let value = Value::deserialize(deserializer)?;
113
114        let call_value = value.get("call").and_then(|v| v.as_str()).unwrap_or("");
115
116        macro_rules! try_call {
117            ($variant:ident, $ty:ty) => {
118                <$ty>::deserialize(value)
119                    .map(CallTaskDefinition::$variant)
120                    .map_err(serde::de::Error::custom)
121            };
122        }
123        macro_rules! try_call_boxed {
124            ($variant:ident, $ty:ty) => {
125                <$ty>::deserialize(value)
126                    .map(|v| CallTaskDefinition::$variant(Box::new(v)))
127                    .map_err(serde::de::Error::custom)
128            };
129        }
130
131        match call_value {
132            "asyncapi" => try_call!(AsyncAPI, CallAsyncAPIDefinition),
133            "grpc" => try_call_boxed!(GRPC, CallGRPCDefinition),
134            "http" => try_call!(HTTP, CallHTTPDefinition),
135            "openapi" => try_call!(OpenAPI, CallOpenAPIDefinition),
136            "a2a" => try_call!(A2A, CallA2ADefinition),
137            _ => try_call!(Function, CallFunctionDefinition),
138        }
139    }
140}
141
142/// Represents the HTTP call arguments
143#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
144pub struct HTTPArguments {
145    /// Gets/sets the HTTP method of the HTTP request to perform
146    pub method: String,
147
148    /// Gets/sets the HTTP endpoint to send the request to
149    pub endpoint: super::resource::OneOfEndpointDefinitionOrUri,
150
151    /// Gets/sets a name/value mapping of the headers, if any, of the HTTP request to perform
152    #[serde(skip_serializing_if = "Option::is_none")]
153    pub headers: Option<OneOfHeadersOrExpression>,
154
155    /// Gets/sets the body, if any, of the HTTP request to perform
156    #[serde(skip_serializing_if = "Option::is_none")]
157    pub body: Option<Value>,
158
159    /// Gets/sets a name/value mapping of the query parameters, if any, of the HTTP request to perform
160    #[serde(skip_serializing_if = "Option::is_none")]
161    pub query: Option<OneOfQueryOrExpression>,
162
163    /// Gets/sets the http call output format. Defaults to 'content'
164    #[serde(skip_serializing_if = "Option::is_none")]
165    pub output: Option<String>,
166
167    /// Gets/sets whether redirection status codes (300-399) should be treated as errors
168    #[serde(skip_serializing_if = "Option::is_none")]
169    pub redirect: Option<bool>,
170}
171
172/// Represents headers that can be either a map or a runtime expression
173#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(untagged)]
175pub enum StringMapOrExpression {
176    /// A name/value mapping
177    Map(HashMap<String, String>),
178    /// A runtime expression
179    Expression(String),
180}
181
182impl Default for StringMapOrExpression {
183    fn default() -> Self {
184        StringMapOrExpression::Map(HashMap::new())
185    }
186}
187
188/// Represents headers that can be either a map or a runtime expression
189pub type OneOfHeadersOrExpression = StringMapOrExpression;
190
191/// Represents query parameters that can be either a map or a runtime expression
192pub type OneOfQueryOrExpression = StringMapOrExpression;
193
194/// Macro to define a call task definition struct with the common `call`, `with`, and `common` fields.
195macro_rules! define_call_definition {
196    ($( #[$meta:meta] )* $name:ident, $with_ty:ty) => {
197        $( #[$meta] )*
198        #[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
199        pub struct $name {
200            /// Gets/sets the call type identifier
201            pub call: String,
202
203            /// Gets/sets the call arguments
204            pub with: $with_ty,
205
206            /// Gets/sets the task's common fields
207            #[serde(flatten)]
208            pub common: TaskDefinitionFields,
209        }
210    };
211}
212
213define_call_definition!(
214    /// Represents the definition of an HTTP call task
215    CallHTTPDefinition, HTTPArguments
216);
217
218/// Represents the GRPC service definition
219#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
220pub struct GRPCServiceDefinition {
221    /// Gets/sets the name of the GRPC service to call
222    pub name: String,
223
224    /// Gets/sets the hostname of the GRPC service to call
225    pub host: String,
226
227    /// Gets/sets the port number of the GRPC service to call
228    #[serde(skip_serializing_if = "Option::is_none")]
229    pub port: Option<u16>,
230
231    /// Gets/sets the endpoint's authentication policy, if any
232    #[serde(skip_serializing_if = "Option::is_none")]
233    pub authentication: Option<ReferenceableAuthenticationPolicy>,
234}
235
236/// Represents the GRPC call arguments
237#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
238pub struct GRPCArguments {
239    /// Gets/sets the proto resource that describes the GRPC service to call
240    pub proto: ExternalResourceDefinition,
241
242    /// Gets/sets the GRPC service definition
243    pub service: GRPCServiceDefinition,
244
245    /// Gets/sets the name of the method to call on the defined GRPC service
246    pub method: String,
247
248    /// Gets/sets the arguments, if any, to call the method with
249    #[serde(skip_serializing_if = "Option::is_none")]
250    pub arguments: Option<HashMap<String, Value>>,
251
252    /// Gets/sets the authentication policy, if any, to use when calling the GRPC service
253    #[serde(skip_serializing_if = "Option::is_none")]
254    pub authentication: Option<ReferenceableAuthenticationPolicy>,
255}
256
257define_call_definition!(
258    /// Represents the definition of a GRPC call task
259    CallGRPCDefinition, GRPCArguments
260);
261
262/// Represents the OpenAPI call arguments
263#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
264pub struct OpenAPIArguments {
265    /// Gets/sets the document that defines the OpenAPI operation to call
266    pub document: ExternalResourceDefinition,
267
268    /// Gets/sets the id of the OpenAPI operation to call
269    #[serde(rename = "operationId")]
270    pub operation_id: String,
271
272    /// Gets/sets a name/value mapping of the parameters of the OpenAPI operation to call
273    #[serde(skip_serializing_if = "Option::is_none")]
274    pub parameters: Option<HashMap<String, Value>>,
275
276    /// Gets/sets the authentication policy, if any, to use when calling the OpenAPI operation
277    #[serde(skip_serializing_if = "Option::is_none")]
278    pub authentication: Option<ReferenceableAuthenticationPolicy>,
279
280    /// Gets/sets the http call output format. Defaults to 'content'
281    #[serde(skip_serializing_if = "Option::is_none")]
282    pub output: Option<String>,
283
284    /// Gets/sets whether redirection status codes (300-399) should be treated as errors
285    #[serde(skip_serializing_if = "Option::is_none")]
286    pub redirect: Option<bool>,
287}
288
289define_call_definition!(
290    /// Represents the definition of an OpenAPI call task
291    CallOpenAPIDefinition, OpenAPIArguments
292);
293
294/// Represents the AsyncAPI server configuration
295#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
296pub struct AsyncApiServerDefinition {
297    /// Gets/sets the target server's name
298    pub name: String,
299
300    /// Gets/sets the target server's variables, if any
301    #[serde(skip_serializing_if = "Option::is_none")]
302    pub variables: Option<HashMap<String, Value>>,
303}
304
305/// Represents an AsyncAPI outbound message
306#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
307pub struct AsyncApiOutboundMessageDefinition {
308    /// Gets/sets the message's payload, if any
309    #[serde(skip_serializing_if = "Option::is_none")]
310    pub payload: Option<Value>,
311
312    /// Gets/sets the message's headers, if any
313    #[serde(skip_serializing_if = "Option::is_none")]
314    pub headers: Option<Value>,
315}
316
317/// Represents an AsyncAPI inbound message (extends outbound with correlationId)
318#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
319pub struct AsyncApiInboundMessageDefinition {
320    /// Gets/sets the message's payload, if any
321    #[serde(skip_serializing_if = "Option::is_none")]
322    pub payload: Option<Value>,
323
324    /// Gets/sets the message's headers, if any
325    #[serde(skip_serializing_if = "Option::is_none")]
326    pub headers: Option<Value>,
327
328    /// Gets/sets the message's correlation id, if any
329    #[serde(rename = "correlationId", skip_serializing_if = "Option::is_none")]
330    pub correlation_id: Option<String>,
331}
332
333/// Represents an AsyncAPI message consumption policy
334#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
335#[serde(untagged)]
336pub enum AsyncApiMessageConsumptionPolicy {
337    /// Consume a specific amount of messages
338    Amount {
339        /// The amount of (filtered) messages to consume
340        amount: u32,
341    },
342    /// Consume for a specified duration
343    For {
344        /// The duration to consume messages for
345        #[serde(rename = "for")]
346        for_: super::duration::OneOfDurationOrIso8601Expression,
347    },
348    /// Consume while a condition is true
349    While {
350        /// A runtime expression evaluated after each consumed message
351        #[serde(rename = "while")]
352        while_: String,
353    },
354    /// Consume until a condition is true
355    Until {
356        /// A runtime expression evaluated before each consumed message
357        until: String,
358    },
359}
360
361impl Default for AsyncApiMessageConsumptionPolicy {
362    fn default() -> Self {
363        AsyncApiMessageConsumptionPolicy::Amount { amount: 1 }
364    }
365}
366
367/// Represents an AsyncAPI subscription
368#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
369pub struct AsyncApiSubscriptionDefinition {
370    /// Gets/sets a runtime expression, if any, used to filter consumed messages
371    #[serde(skip_serializing_if = "Option::is_none")]
372    pub filter: Option<String>,
373
374    /// Gets/sets the subscription's message consumption policy
375    pub consume: AsyncApiMessageConsumptionPolicy,
376
377    /// Gets/sets the configuration of the iterator, if any, for processing each consumed item
378    #[serde(skip_serializing_if = "Option::is_none")]
379    pub foreach: Option<super::task::SubscriptionIteratorDefinition>,
380}
381
382/// Represents the AsyncAPI call arguments
383#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
384pub struct AsyncApiArguments {
385    /// Gets/sets the document that defines the AsyncAPI operation to call
386    pub document: ExternalResourceDefinition,
387
388    /// Gets/sets the name of the channel (AsyncAPI v2.6.0)
389    #[serde(skip_serializing_if = "Option::is_none")]
390    pub channel: Option<String>,
391
392    /// Gets/sets a reference to the AsyncAPI operation to call
393    #[serde(skip_serializing_if = "Option::is_none")]
394    pub operation: Option<String>,
395
396    /// Gets/sets the server to call the specified AsyncAPI operation on
397    #[serde(skip_serializing_if = "Option::is_none")]
398    pub server: Option<AsyncApiServerDefinition>,
399
400    /// Gets/sets the protocol to use to select the target server
401    #[serde(skip_serializing_if = "Option::is_none")]
402    pub protocol: Option<String>,
403
404    /// Gets/sets the message to publish using the target operation
405    #[serde(skip_serializing_if = "Option::is_none")]
406    pub message: Option<AsyncApiOutboundMessageDefinition>,
407
408    /// Gets/sets the subscription to messages consumed using the target operation
409    #[serde(skip_serializing_if = "Option::is_none")]
410    pub subscription: Option<AsyncApiSubscriptionDefinition>,
411
412    /// Gets/sets the authentication policy, if any, to use when calling the AsyncAPI operation
413    #[serde(skip_serializing_if = "Option::is_none")]
414    pub authentication: Option<ReferenceableAuthenticationPolicy>,
415}
416
417define_call_definition!(
418    /// Represents the definition of an AsyncAPI call task
419    CallAsyncAPIDefinition, AsyncApiArguments
420);
421
422/// Represents A2A call parameters that can be either an object or a runtime expression
423#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
424#[serde(untagged)]
425pub enum OneOfA2AParametersOrExpression {
426    /// A parameters object
427    Map(HashMap<String, Value>),
428    /// A runtime expression
429    Expression(String),
430}
431
432impl Default for OneOfA2AParametersOrExpression {
433    fn default() -> Self {
434        OneOfA2AParametersOrExpression::Map(HashMap::new())
435    }
436}
437
438/// Represents the A2A call arguments
439#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
440pub struct A2AArguments {
441    /// Gets/sets the Agent Card that defines the agent to call
442    #[serde(rename = "agentCard", skip_serializing_if = "Option::is_none")]
443    pub agent_card: Option<ExternalResourceDefinition>,
444
445    /// Gets/sets the server endpoint to send the request to
446    #[serde(skip_serializing_if = "Option::is_none")]
447    pub server: Option<super::resource::OneOfEndpointDefinitionOrUri>,
448
449    /// Gets/sets the A2A method to send
450    pub method: String,
451
452    /// Gets/sets the parameters object to send with the A2A method
453    #[serde(skip_serializing_if = "Option::is_none")]
454    pub parameters: Option<OneOfA2AParametersOrExpression>,
455}
456
457define_call_definition!(
458    /// Represents the definition of an A2A call task
459    CallA2ADefinition, A2AArguments
460);
461
462/// Represents the definition of a function call task
463#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
464pub struct CallFunctionDefinition {
465    /// Gets/sets the name of the function to call
466    pub call: String,
467
468    /// Gets/sets a name/value mapping of the parameters, if any, to call the function with
469    #[serde(skip_serializing_if = "Option::is_none")]
470    pub with: Option<HashMap<String, Value>>,
471
472    /// Gets/sets the task's common fields
473    #[serde(flatten)]
474    pub common: TaskDefinitionFields,
475}
476
477#[cfg(test)]
478mod tests {
479    use super::*;
480
481    #[test]
482    fn test_call_http_deserialize() {
483        let json = r#"{
484            "call": "http",
485            "with": {
486                "method": "GET",
487                "endpoint": "http://example.com/api"
488            }
489        }"#;
490        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
491        assert_eq!(http.call, "http");
492        assert_eq!(http.with.method, "GET");
493        match &http.with.endpoint {
494            OneOfEndpointDefinitionOrUri::Uri(uri) => assert_eq!(uri, "http://example.com/api"),
495            _ => panic!("Expected Uri variant"),
496        }
497    }
498
499    #[test]
500    fn test_call_http_with_headers_and_query() {
501        let json = r#"{
502            "call": "http",
503            "with": {
504                "method": "POST",
505                "endpoint": "http://example.com/api",
506                "headers": {"Authorization": "Bearer token"},
507                "query": {"page": "1"},
508                "output": "response",
509                "redirect": true
510            }
511        }"#;
512        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
513        assert_eq!(http.with.method, "POST");
514        assert!(http.with.headers.is_some());
515        assert!(http.with.query.is_some());
516        assert_eq!(http.with.output, Some("response".to_string()));
517        assert_eq!(http.with.redirect, Some(true));
518    }
519
520    #[test]
521    fn test_call_http_roundtrip() {
522        let json = r#"{
523            "call": "http",
524            "with": {
525                "method": "GET",
526                "endpoint": "http://example.com/api"
527            }
528        }"#;
529        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
530        let serialized = serde_json::to_string(&http).unwrap();
531        let deserialized: CallHTTPDefinition = serde_json::from_str(&serialized).unwrap();
532        assert_eq!(http, deserialized);
533    }
534
535    #[test]
536    fn test_call_http_with_endpoint_config() {
537        let json = r#"{
538            "call": "http",
539            "with": {
540                "method": "GET",
541                "endpoint": {
542                    "uri": "http://example.com/{id}",
543                    "authentication": {
544                        "basic": {"username": "admin", "password": "secret"}
545                    }
546                }
547            }
548        }"#;
549        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
550        match &http.with.endpoint {
551            OneOfEndpointDefinitionOrUri::Endpoint(ep) => {
552                assert_eq!(ep.uri, "http://example.com/{id}");
553                assert!(ep.authentication.is_some());
554            }
555            _ => panic!("Expected Endpoint variant"),
556        }
557    }
558
559    #[test]
560    fn test_call_grpc_deserialize() {
561        let json = r#"{
562            "call": "grpc",
563            "with": {
564                "proto": {
565                    "name": "MyProto",
566                    "endpoint": "http://example.com/proto"
567                },
568                "service": {
569                    "name": "UserService",
570                    "host": "example.com",
571                    "port": 50051
572                },
573                "method": "GetUser",
574                "arguments": {"userId": "12345"}
575            }
576        }"#;
577        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
578        assert_eq!(grpc.call, "grpc");
579        assert_eq!(grpc.with.proto.name, Some("MyProto".to_string()));
580        assert_eq!(grpc.with.service.name, "UserService");
581        assert_eq!(grpc.with.service.host, "example.com");
582        assert_eq!(grpc.with.service.port, Some(50051));
583        assert_eq!(grpc.with.method, "GetUser");
584    }
585
586    #[test]
587    fn test_call_grpc_roundtrip() {
588        let json = r#"{
589            "call": "grpc",
590            "with": {
591                "proto": {
592                    "name": "MyProto",
593                    "endpoint": "http://example.com/proto"
594                },
595                "service": {
596                    "name": "UserService",
597                    "host": "example.com",
598                    "port": 50051
599                },
600                "method": "GetUser"
601            }
602        }"#;
603        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
604        let serialized = serde_json::to_string(&grpc).unwrap();
605        let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
606        assert_eq!(grpc, deserialized);
607    }
608
609    #[test]
610    fn test_call_openapi_deserialize() {
611        let json = r#"{
612            "call": "openapi",
613            "with": {
614                "document": {
615                    "name": "MyOpenAPIDoc",
616                    "endpoint": "http://example.com/openapi.json"
617                },
618                "operationId": "getUsers",
619                "parameters": {"param1": "value1"},
620                "authentication": {"use": "my-auth"},
621                "output": "content",
622                "redirect": true
623            }
624        }"#;
625        let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
626        assert_eq!(openapi.call, "openapi");
627        assert_eq!(openapi.with.operation_id, "getUsers");
628        assert!(openapi.with.parameters.is_some());
629        assert!(openapi.with.authentication.is_some());
630        assert_eq!(openapi.with.output, Some("content".to_string()));
631        assert_eq!(openapi.with.redirect, Some(true));
632    }
633
634    #[test]
635    fn test_call_openapi_roundtrip() {
636        let json = r#"{
637            "call": "openapi",
638            "with": {
639                "document": {
640                    "name": "MyOpenAPIDoc",
641                    "endpoint": "http://example.com/openapi.json"
642                },
643                "operationId": "getUsers"
644            }
645        }"#;
646        let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
647        let serialized = serde_json::to_string(&openapi).unwrap();
648        let deserialized: CallOpenAPIDefinition = serde_json::from_str(&serialized).unwrap();
649        assert_eq!(openapi, deserialized);
650    }
651
652    #[test]
653    fn test_call_asyncapi_deserialize() {
654        let json = r#"{
655            "call": "asyncapi",
656            "with": {
657                "document": {
658                    "name": "MyAsyncAPIDoc",
659                    "endpoint": "http://example.com/asyncapi.json"
660                },
661                "operation": "user.signup",
662                "server": {"name": "default-server"},
663                "protocol": "http",
664                "message": {
665                    "payload": {"userId": "12345"}
666                },
667                "authentication": {"use": "asyncapi-auth"}
668            }
669        }"#;
670        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
671        assert_eq!(asyncapi.call, "asyncapi");
672        assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
673        assert!(asyncapi.with.server.is_some());
674        assert_eq!(asyncapi.with.protocol, Some("http".to_string()));
675        assert!(asyncapi.with.message.is_some());
676        assert!(asyncapi.with.authentication.is_some());
677    }
678
679    #[test]
680    fn test_call_asyncapi_roundtrip() {
681        let json = r#"{
682            "call": "asyncapi",
683            "with": {
684                "document": {
685                    "name": "MyAsyncAPIDoc",
686                    "endpoint": "http://example.com/asyncapi.json"
687                },
688                "operation": "user.signup",
689                "protocol": "http"
690            }
691        }"#;
692        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
693        let serialized = serde_json::to_string(&asyncapi).unwrap();
694        let deserialized: CallAsyncAPIDefinition = serde_json::from_str(&serialized).unwrap();
695        assert_eq!(asyncapi, deserialized);
696    }
697
698    #[test]
699    fn test_call_function_deserialize() {
700        let json = r#"{
701            "call": "myFunction",
702            "with": {
703                "param1": "value1",
704                "param2": 42
705            }
706        }"#;
707        let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
708        assert_eq!(func.call, "myFunction");
709        assert!(func.with.is_some());
710        let with = func.with.unwrap();
711        assert_eq!(with.get("param1").unwrap(), "value1");
712    }
713
714    #[test]
715    fn test_call_function_roundtrip() {
716        let json = r#"{
717            "call": "myFunction",
718            "with": {"param1": "value1"}
719        }"#;
720        let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
721        let serialized = serde_json::to_string(&func).unwrap();
722        let deserialized: CallFunctionDefinition = serde_json::from_str(&serialized).unwrap();
723        assert_eq!(func, deserialized);
724    }
725
726    #[test]
727    fn test_call_a2a_deserialize() {
728        let json = r#"{
729            "call": "a2a",
730            "with": {
731                "method": "message/send",
732                "parameters": {"message": "hello"}
733            }
734        }"#;
735        let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
736        assert_eq!(a2a.call, "a2a");
737        assert_eq!(a2a.with.method, "message/send");
738        assert!(a2a.with.parameters.is_some());
739    }
740
741    #[test]
742    fn test_call_task_definition_http() {
743        let json =
744            r#"{"call": "http", "with": {"method": "GET", "endpoint": "http://example.com"}}"#;
745        let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
746        match def {
747            CallTaskDefinition::HTTP(http) => assert_eq!(http.call, "http"),
748            _ => panic!("Expected HTTP variant"),
749        }
750    }
751
752    #[test]
753    fn test_call_task_definition_grpc() {
754        let json = r#"{"call": "grpc", "with": {"proto": {"endpoint": "http://example.com/proto"}, "service": {"name": "Svc", "host": "example.com"}, "method": "Get"}}"#;
755        let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
756        match def {
757            CallTaskDefinition::GRPC(grpc) => assert_eq!(grpc.call, "grpc"),
758            _ => panic!("Expected GRPC variant"),
759        }
760    }
761
762    #[test]
763    fn test_call_task_definition_openapi() {
764        let json = r#"{"call": "openapi", "with": {"document": {"endpoint": "http://example.com/openapi.json"}, "operationId": "op1"}}"#;
765        let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
766        match def {
767            CallTaskDefinition::OpenAPI(openapi) => assert_eq!(openapi.call, "openapi"),
768            _ => panic!("Expected OpenAPI variant"),
769        }
770    }
771
772    #[test]
773    fn test_call_task_definition_function() {
774        let json = r#"{"call": "myFunc", "with": {"key": "val"}}"#;
775        let def: CallTaskDefinition = serde_json::from_str(json).unwrap();
776        match def {
777            CallTaskDefinition::Function(func) => assert_eq!(func.call, "myFunc"),
778            _ => panic!("Expected Function variant"),
779        }
780    }
781
782    #[test]
783    fn test_headers_map_vs_expression() {
784        let map_json = r#"{"Authorization": "Bearer token"}"#;
785        let map: OneOfHeadersOrExpression = serde_json::from_str(map_json).unwrap();
786        assert!(matches!(map, OneOfHeadersOrExpression::Map(_)));
787
788        let expr_json = r#""${ .headers }""#;
789        let expr: OneOfHeadersOrExpression = serde_json::from_str(expr_json).unwrap();
790        assert!(matches!(expr, OneOfHeadersOrExpression::Expression(_)));
791    }
792
793    #[test]
794    fn test_query_map_vs_expression() {
795        let map_json = r#"{"page": "1"}"#;
796        let map: OneOfQueryOrExpression = serde_json::from_str(map_json).unwrap();
797        assert!(matches!(map, OneOfQueryOrExpression::Map(_)));
798
799        let expr_json = r#""${ .queryParams }""#;
800        let expr: OneOfQueryOrExpression = serde_json::from_str(expr_json).unwrap();
801        assert!(matches!(expr, OneOfQueryOrExpression::Expression(_)));
802    }
803
804    #[test]
805    fn test_asyncapi_consumption_policy_amount() {
806        let json = r#"{"amount": 5}"#;
807        let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
808        match policy {
809            AsyncApiMessageConsumptionPolicy::Amount { amount } => assert_eq!(amount, 5),
810            _ => panic!("Expected Amount variant"),
811        }
812    }
813
814    #[test]
815    fn test_asyncapi_consumption_policy_for() {
816        let json = r#"{"for": "PT30S"}"#;
817        let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
818        match policy {
819            AsyncApiMessageConsumptionPolicy::For { for_ } => {
820                // Should parse as ISO8601 expression
821                assert!(matches!(
822                    for_,
823                    crate::models::duration::OneOfDurationOrIso8601Expression::Iso8601Expression(_)
824                ));
825            }
826            _ => panic!("Expected For variant"),
827        }
828    }
829
830    #[test]
831    fn test_asyncapi_consumption_policy_for_duration() {
832        let json = r#"{"for": {"seconds": 30}}"#;
833        let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
834        match policy {
835            AsyncApiMessageConsumptionPolicy::For { for_ } => {
836                assert!(matches!(
837                    for_,
838                    crate::models::duration::OneOfDurationOrIso8601Expression::Duration(_)
839                ));
840            }
841            _ => panic!("Expected For variant"),
842        }
843    }
844
845    #[test]
846    fn test_asyncapi_consumption_policy_while() {
847        let json = r#"{"while": "${ .counter < 10 }"}"#;
848        let policy: AsyncApiMessageConsumptionPolicy = serde_json::from_str(json).unwrap();
849        match policy {
850            AsyncApiMessageConsumptionPolicy::While { while_ } => {
851                assert_eq!(while_, "${ .counter < 10 }");
852            }
853            _ => panic!("Expected While variant"),
854        }
855    }
856
857    #[test]
858    fn test_grpc_service_with_authentication() {
859        let json = r#"{
860            "name": "UserService",
861            "host": "example.com",
862            "port": 50051,
863            "authentication": {"use": "grpc-auth"}
864        }"#;
865        let service: GRPCServiceDefinition = serde_json::from_str(json).unwrap();
866        assert_eq!(service.name, "UserService");
867        assert!(service.authentication.is_some());
868    }
869
870    // Additional tests matching Go SDK's task_call_test.go
871
872    #[test]
873    fn test_call_http_with_common_fields() {
874        // Matches Go SDK's TestCallHTTP_UnmarshalJSON with full TaskBase fields
875        let json = r#"{
876            "if": "${condition}",
877            "input": {"from": {"key": "value"}},
878            "output": {"as": {"result": "output"}},
879            "timeout": {"after": "PT10S"},
880            "then": "continue",
881            "metadata": {"meta": "data"},
882            "call": "http",
883            "with": {
884                "method": "GET",
885                "endpoint": "http://example.com",
886                "headers": {"Authorization": "Bearer token"},
887                "query": {"q": "search"},
888                "output": "content",
889                "redirect": true
890            }
891        }"#;
892        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
893        assert_eq!(http.call, "http");
894        assert_eq!(http.common.if_, Some("${condition}".to_string()));
895        assert!(http.common.input.is_some());
896        assert!(http.common.output.is_some());
897        assert!(http.common.timeout.is_some());
898        assert_eq!(http.common.then, Some("continue".to_string()));
899        assert!(http.common.metadata.is_some());
900        assert_eq!(http.with.method, "GET");
901        assert_eq!(http.with.output, Some("content".to_string()));
902        assert_eq!(http.with.redirect, Some(true));
903    }
904
905    #[test]
906    fn test_call_http_with_common_fields_roundtrip() {
907        let json = r#"{
908            "if": "${condition}",
909            "input": {"from": {"key": "value"}},
910            "output": {"as": {"result": "output"}},
911            "timeout": {"after": "PT10S"},
912            "then": "continue",
913            "call": "http",
914            "with": {
915                "method": "GET",
916                "endpoint": "http://example.com"
917            }
918        }"#;
919        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
920        let serialized = serde_json::to_string(&http).unwrap();
921        let deserialized: CallHTTPDefinition = serde_json::from_str(&serialized).unwrap();
922        assert_eq!(http, deserialized);
923    }
924
925    #[test]
926    fn test_call_openapi_with_authentication() {
927        // Matches Go SDK's TestCallOpenAPI with authentication.use
928        let json = r#"{
929            "call": "openapi",
930            "with": {
931                "document": {
932                    "name": "MyOpenAPIDoc",
933                    "endpoint": "http://example.com/openapi.json"
934                },
935                "operationId": "getUsers",
936                "parameters": {"param1": "value1", "param2": "value2"},
937                "authentication": {"use": "my-auth"},
938                "output": "content",
939                "redirect": true
940            }
941        }"#;
942        let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
943        assert_eq!(openapi.with.operation_id, "getUsers");
944        assert!(openapi.with.authentication.is_some());
945        assert_eq!(openapi.with.output, Some("content".to_string()));
946        assert_eq!(openapi.with.redirect, Some(true));
947    }
948
949    #[test]
950    fn test_call_grpc_with_full_arguments() {
951        // Matches Go SDK's TestCallGRPC_UnmarshalJSON
952        let json = r#"{
953            "call": "grpc",
954            "with": {
955                "proto": {
956                    "name": "MyProtoFile",
957                    "endpoint": "http://example.com/protofile"
958                },
959                "service": {
960                    "name": "UserService",
961                    "host": "example.com",
962                    "port": 50051
963                },
964                "method": "GetUser",
965                "arguments": {"userId": "12345"}
966            }
967        }"#;
968        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
969        assert_eq!(grpc.call, "grpc");
970        assert_eq!(grpc.with.proto.name, Some("MyProtoFile".to_string()));
971        assert_eq!(grpc.with.service.name, "UserService");
972        assert_eq!(grpc.with.service.host, "example.com");
973        assert_eq!(grpc.with.service.port, Some(50051));
974        assert_eq!(grpc.with.method, "GetUser");
975        assert!(grpc.with.arguments.is_some());
976    }
977
978    #[test]
979    fn test_call_asyncapi_with_subscription() {
980        let json = r#"{
981            "call": "asyncapi",
982            "with": {
983                "document": {
984                    "name": "MyAsyncAPIDoc",
985                    "endpoint": "http://example.com/asyncapi.json"
986                },
987                "operation": "user.signup",
988                "server": {"name": "default-server"},
989                "protocol": "http",
990                "subscription": {
991                    "filter": "${ .type == \"order\" }",
992                    "consume": {"amount": 5}
993                },
994                "authentication": {"use": "asyncapi-auth-policy"}
995            }
996        }"#;
997        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
998        assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
999        assert!(asyncapi.with.subscription.is_some());
1000        let sub = asyncapi.with.subscription.as_ref().unwrap();
1001        assert!(sub.filter.is_some());
1002        match &sub.consume {
1003            AsyncApiMessageConsumptionPolicy::Amount { amount } => assert_eq!(*amount, 5),
1004            _ => panic!("Expected Amount variant"),
1005        }
1006    }
1007
1008    #[test]
1009    fn test_call_asyncapi_with_message_and_subscription() {
1010        // Both message (outbound) and subscription (inbound) in same call
1011        let json = r#"{
1012            "call": "asyncapi",
1013            "with": {
1014                "document": {"endpoint": "http://example.com/asyncapi.json"},
1015                "operation": "order.process",
1016                "protocol": "kafka",
1017                "message": {"payload": {"orderId": "123"}},
1018                "subscription": {
1019                    "consume": {"while": "${ .status != \"completed\" }"}
1020                }
1021            }
1022        }"#;
1023        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1024        assert!(asyncapi.with.message.is_some());
1025        assert!(asyncapi.with.subscription.is_some());
1026        let sub = asyncapi.with.subscription.as_ref().unwrap();
1027        match &sub.consume {
1028            AsyncApiMessageConsumptionPolicy::While { while_ } => {
1029                assert_eq!(while_, "${ .status != \"completed\" }");
1030            }
1031            _ => panic!("Expected While variant"),
1032        }
1033    }
1034
1035    #[test]
1036    fn test_call_function_with_common_fields_roundtrip() {
1037        let json = r#"{
1038            "call": "myFunction",
1039            "with": {"param1": "value1", "param2": 42}
1040        }"#;
1041        let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
1042        let serialized = serde_json::to_string(&func).unwrap();
1043        let deserialized: CallFunctionDefinition = serde_json::from_str(&serialized).unwrap();
1044        assert_eq!(func, deserialized);
1045    }
1046
1047    #[test]
1048    fn test_call_a2a_with_agent_card() {
1049        let json = r#"{
1050            "call": "a2a",
1051            "with": {
1052                "agentCard": {
1053                    "name": "my-agent",
1054                    "endpoint": "http://example.com/agent-card"
1055                },
1056                "server": "http://example.com/a2a-server",
1057                "method": "tasks/get",
1058                "parameters": {"taskId": "123"}
1059            }
1060        }"#;
1061        let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1062        assert_eq!(a2a.call, "a2a");
1063        assert!(a2a.with.agent_card.is_some());
1064        assert!(a2a.with.server.is_some());
1065        assert_eq!(a2a.with.method, "tasks/get");
1066        assert!(a2a.with.parameters.is_some());
1067    }
1068
1069    #[test]
1070    fn test_call_a2a_roundtrip() {
1071        let json = r#"{
1072            "call": "a2a",
1073            "with": {
1074                "method": "message/send",
1075                "parameters": {"message": "hello"}
1076            }
1077        }"#;
1078        let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1079        let serialized = serde_json::to_string(&a2a).unwrap();
1080        let deserialized: CallA2ADefinition = serde_json::from_str(&serialized).unwrap();
1081        assert_eq!(a2a, deserialized);
1082    }
1083
1084    // Tests matching Go SDK's task_call_test.go - Call tasks with full TaskBase fields
1085
1086    #[test]
1087    fn test_call_grpc_with_common_fields() {
1088        // Matches Go SDK's TestCallGRPC with full TaskBase fields
1089        let json = r#"{
1090            "if": "${condition}",
1091            "input": {"from": {"key": "value"}},
1092            "output": {"as": {"result": "output"}},
1093            "timeout": {"after": "PT10S"},
1094            "then": "continue",
1095            "metadata": {"meta": "data"},
1096            "call": "grpc",
1097            "with": {
1098                "proto": {
1099                    "name": "MyProtoFile",
1100                    "endpoint": "http://example.com/protofile"
1101                },
1102                "service": {
1103                    "name": "UserService",
1104                    "host": "example.com",
1105                    "port": 50051
1106                },
1107                "method": "GetUser",
1108                "arguments": {"userId": "12345"}
1109            }
1110        }"#;
1111        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1112        assert_eq!(grpc.call, "grpc");
1113        assert_eq!(grpc.common.if_, Some("${condition}".to_string()));
1114        assert!(grpc.common.input.is_some());
1115        assert!(grpc.common.output.is_some());
1116        assert!(grpc.common.timeout.is_some());
1117        assert_eq!(grpc.common.then, Some("continue".to_string()));
1118        assert!(grpc.common.metadata.is_some());
1119        assert_eq!(grpc.with.service.name, "UserService");
1120        assert_eq!(grpc.with.method, "GetUser");
1121    }
1122
1123    #[test]
1124    fn test_call_grpc_with_common_fields_roundtrip() {
1125        let json = r#"{
1126            "if": "${condition}",
1127            "output": {"as": {"result": "output"}},
1128            "then": "continue",
1129            "call": "grpc",
1130            "with": {
1131                "proto": {"endpoint": "http://example.com/proto"},
1132                "service": {"name": "Svc", "host": "example.com"},
1133                "method": "Get"
1134            }
1135        }"#;
1136        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1137        let serialized = serde_json::to_string(&grpc).unwrap();
1138        let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
1139        assert_eq!(grpc, deserialized);
1140    }
1141
1142    #[test]
1143    fn test_call_openapi_with_common_fields() {
1144        // Matches Go SDK's TestCallOpenAPI with full TaskBase fields
1145        let json = r#"{
1146            "if": "${condition}",
1147            "input": {"from": {"key": "value"}},
1148            "output": {"as": {"result": "output"}},
1149            "timeout": {"after": "PT10S"},
1150            "then": "continue",
1151            "metadata": {"meta": "data"},
1152            "call": "openapi",
1153            "with": {
1154                "document": {
1155                    "name": "MyOpenAPIDoc",
1156                    "endpoint": "http://example.com/openapi.json"
1157                },
1158                "operationId": "getUsers",
1159                "parameters": {"param1": "value1"},
1160                "authentication": {"use": "my-auth"},
1161                "output": "content",
1162                "redirect": true
1163            }
1164        }"#;
1165        let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
1166        assert_eq!(openapi.call, "openapi");
1167        assert_eq!(openapi.common.if_, Some("${condition}".to_string()));
1168        assert!(openapi.common.input.is_some());
1169        assert!(openapi.common.output.is_some());
1170        assert!(openapi.common.timeout.is_some());
1171        assert_eq!(openapi.common.then, Some("continue".to_string()));
1172        assert!(openapi.common.metadata.is_some());
1173        assert_eq!(openapi.with.operation_id, "getUsers");
1174        assert_eq!(openapi.with.output, Some("content".to_string()));
1175        assert_eq!(openapi.with.redirect, Some(true));
1176    }
1177
1178    #[test]
1179    fn test_call_openapi_with_common_fields_roundtrip() {
1180        let json = r#"{
1181            "if": "${condition}",
1182            "output": {"as": {"result": "output"}},
1183            "then": "continue",
1184            "call": "openapi",
1185            "with": {
1186                "document": {"endpoint": "http://example.com/openapi.json"},
1187                "operationId": "op1"
1188            }
1189        }"#;
1190        let openapi: CallOpenAPIDefinition = serde_json::from_str(json).unwrap();
1191        let serialized = serde_json::to_string(&openapi).unwrap();
1192        let deserialized: CallOpenAPIDefinition = serde_json::from_str(&serialized).unwrap();
1193        assert_eq!(openapi, deserialized);
1194    }
1195
1196    #[test]
1197    fn test_call_asyncapi_with_common_fields() {
1198        // Matches Go SDK's TestCallAsyncAPI with full TaskBase fields
1199        let json = r#"{
1200            "if": "${condition}",
1201            "input": {"from": {"key": "value"}},
1202            "output": {"as": {"result": "output"}},
1203            "timeout": {"after": "PT10S"},
1204            "then": "continue",
1205            "metadata": {"meta": "data"},
1206            "call": "asyncapi",
1207            "with": {
1208                "document": {
1209                    "name": "MyAsyncAPIDoc",
1210                    "endpoint": "http://example.com/asyncapi.json"
1211                },
1212                "operation": "user.signup",
1213                "server": {"name": "default-server"},
1214                "protocol": "http",
1215                "message": {
1216                    "payload": {"userId": "12345"}
1217                },
1218                "authentication": {"use": "asyncapi-auth-policy"}
1219            }
1220        }"#;
1221        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1222        assert_eq!(asyncapi.call, "asyncapi");
1223        assert_eq!(asyncapi.common.if_, Some("${condition}".to_string()));
1224        assert!(asyncapi.common.input.is_some());
1225        assert!(asyncapi.common.output.is_some());
1226        assert!(asyncapi.common.timeout.is_some());
1227        assert_eq!(asyncapi.common.then, Some("continue".to_string()));
1228        assert!(asyncapi.common.metadata.is_some());
1229        assert_eq!(asyncapi.with.operation, Some("user.signup".to_string()));
1230        assert_eq!(asyncapi.with.protocol, Some("http".to_string()));
1231    }
1232
1233    #[test]
1234    fn test_call_asyncapi_with_common_fields_roundtrip() {
1235        let json = r#"{
1236            "if": "${condition}",
1237            "output": {"as": {"result": "output"}},
1238            "then": "continue",
1239            "call": "asyncapi",
1240            "with": {
1241                "document": {"endpoint": "http://example.com/asyncapi.json"},
1242                "operation": "user.signup",
1243                "protocol": "http"
1244            }
1245        }"#;
1246        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1247        let serialized = serde_json::to_string(&asyncapi).unwrap();
1248        let deserialized: CallAsyncAPIDefinition = serde_json::from_str(&serialized).unwrap();
1249        assert_eq!(asyncapi, deserialized);
1250    }
1251
1252    #[test]
1253    fn test_call_function_with_common_fields() {
1254        // Matches Go SDK's TestCallFunction with full TaskBase fields
1255        let json = r#"{
1256            "if": "${condition}",
1257            "input": {"from": {"key": "value"}},
1258            "output": {"as": {"result": "output"}},
1259            "timeout": {"after": "PT10S"},
1260            "then": "continue",
1261            "metadata": {"meta": "data"},
1262            "call": "myFunction",
1263            "with": {"param1": "value1", "param2": 42}
1264        }"#;
1265        let func: CallFunctionDefinition = serde_json::from_str(json).unwrap();
1266        assert_eq!(func.call, "myFunction");
1267        assert_eq!(func.common.if_, Some("${condition}".to_string()));
1268        assert!(func.common.input.is_some());
1269        assert!(func.common.output.is_some());
1270        assert!(func.common.timeout.is_some());
1271        assert_eq!(func.common.then, Some("continue".to_string()));
1272        assert!(func.common.metadata.is_some());
1273    }
1274
1275    #[test]
1276    fn test_call_a2a_with_common_fields() {
1277        let json = r#"{
1278            "if": "${condition}",
1279            "input": {"from": {"key": "value"}},
1280            "output": {"as": {"result": "output"}},
1281            "timeout": {"after": "PT10S"},
1282            "then": "continue",
1283            "call": "a2a",
1284            "with": {
1285                "method": "message/send",
1286                "parameters": {"message": "hello"}
1287            }
1288        }"#;
1289        let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1290        assert_eq!(a2a.call, "a2a");
1291        assert_eq!(a2a.common.if_, Some("${condition}".to_string()));
1292        assert!(a2a.common.input.is_some());
1293        assert!(a2a.common.output.is_some());
1294        assert!(a2a.common.timeout.is_some());
1295        assert_eq!(a2a.common.then, Some("continue".to_string()));
1296    }
1297
1298    #[test]
1299    fn test_call_http_with_body() {
1300        // Test HTTP call with body payload
1301        let json = r#"{
1302            "call": "http",
1303            "with": {
1304                "method": "POST",
1305                "endpoint": "http://example.com/api",
1306                "headers": {"Content-Type": "application/json"},
1307                "body": {"name": "test", "value": 42},
1308                "output": "content"
1309            }
1310        }"#;
1311        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1312        assert_eq!(http.with.method, "POST");
1313        assert!(http.with.body.is_some());
1314        let body = http.with.body.unwrap();
1315        assert_eq!(body["name"], "test");
1316        assert_eq!(body["value"], 42);
1317    }
1318
1319    #[test]
1320    fn test_call_http_headers_expression() {
1321        // Test HTTP headers as runtime expression
1322        let json = r#"{
1323            "call": "http",
1324            "with": {
1325                "method": "GET",
1326                "endpoint": "http://example.com",
1327                "headers": "${ .requestHeaders }"
1328            }
1329        }"#;
1330        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1331        match &http.with.headers {
1332            Some(OneOfHeadersOrExpression::Expression(expr)) => {
1333                assert_eq!(expr, "${ .requestHeaders }");
1334            }
1335            _ => panic!("Expected Expression variant for headers"),
1336        }
1337    }
1338
1339    #[test]
1340    fn test_call_http_query_expression() {
1341        // Test HTTP query as runtime expression
1342        let json = r#"{
1343            "call": "http",
1344            "with": {
1345                "method": "GET",
1346                "endpoint": "http://example.com",
1347                "query": "${ .queryParams }"
1348            }
1349        }"#;
1350        let http: CallHTTPDefinition = serde_json::from_str(json).unwrap();
1351        match &http.with.query {
1352            Some(OneOfQueryOrExpression::Expression(expr)) => {
1353                assert_eq!(expr, "${ .queryParams }");
1354            }
1355            _ => panic!("Expected Expression variant for query"),
1356        }
1357    }
1358
1359    #[test]
1360    fn test_call_grpc_service_with_authentication_roundtrip() {
1361        let json = r#"{
1362            "call": "grpc",
1363            "with": {
1364                "proto": {"endpoint": "http://example.com/proto"},
1365                "service": {
1366                    "name": "Svc",
1367                    "host": "example.com",
1368                    "authentication": {"use": "grpc-auth"}
1369                },
1370                "method": "Get"
1371            }
1372        }"#;
1373        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1374        let serialized = serde_json::to_string(&grpc).unwrap();
1375        let deserialized: CallGRPCDefinition = serde_json::from_str(&serialized).unwrap();
1376        assert_eq!(grpc, deserialized);
1377    }
1378
1379    #[test]
1380    fn test_call_a2a_with_parameters_expression() {
1381        // A2A parameters as runtime expression
1382        let json = r#"{
1383            "call": "a2a",
1384            "with": {
1385                "method": "tasks/get",
1386                "parameters": "${ .taskParams }"
1387            }
1388        }"#;
1389        let a2a: CallA2ADefinition = serde_json::from_str(json).unwrap();
1390        match &a2a.with.parameters {
1391            Some(OneOfA2AParametersOrExpression::Expression(expr)) => {
1392                assert_eq!(expr, "${ .taskParams }");
1393            }
1394            _ => panic!("Expected Expression variant for parameters"),
1395        }
1396    }
1397
1398    #[test]
1399    fn test_call_grpc_with_authentication() {
1400        // Matches Go SDK's TestCallGRPC_UnmarshalJSON - GRPCArguments-level authentication
1401        let json = r#"{
1402            "call": "grpc",
1403            "with": {
1404                "proto": {"endpoint": "http://example.com/proto"},
1405                "service": {
1406                    "name": "UserService",
1407                    "host": "example.com"
1408                },
1409                "method": "GetUser",
1410                "arguments": {"userId": "12345"},
1411                "authentication": {"use": "my-auth"}
1412            }
1413        }"#;
1414        let grpc: CallGRPCDefinition = serde_json::from_str(json).unwrap();
1415        assert_eq!(grpc.call, "grpc");
1416        assert!(grpc.with.authentication.is_some());
1417        match grpc.with.authentication.unwrap() {
1418            ReferenceableAuthenticationPolicy::Reference(r) => {
1419                assert_eq!(r.use_, "my-auth");
1420            }
1421            _ => panic!("Expected Reference variant"),
1422        }
1423    }
1424
1425    #[test]
1426    fn test_call_asyncapi_channel() {
1427        // AsyncAPI with channel (v2.6.0)
1428        let json = r#"{
1429            "call": "asyncapi",
1430            "with": {
1431                "document": {"endpoint": "http://example.com/asyncapi.json"},
1432                "channel": "user-events",
1433                "protocol": "kafka",
1434                "message": {"payload": {"event": "created"}}
1435            }
1436        }"#;
1437        let asyncapi: CallAsyncAPIDefinition = serde_json::from_str(json).unwrap();
1438        assert_eq!(asyncapi.with.channel, Some("user-events".to_string()));
1439        assert!(asyncapi.with.message.is_some());
1440    }
1441}