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