Skip to main content

rullama_a2a/
push_notification.rs

1//! Push notification configuration types.
2
3use serde::{Deserialize, Serialize};
4
5/// Authentication details for push notifications.
6#[derive(Debug, Clone, Serialize, Deserialize)]
7pub struct AuthenticationInfo {
8    /// HTTP authentication scheme (e.g. `Bearer`, `Basic`).
9    pub scheme: String,
10    /// Credentials (format depends on scheme).
11    #[serde(skip_serializing_if = "Option::is_none")]
12    pub credentials: Option<String>,
13}
14
15/// Push notification configuration for a task.
16#[derive(Debug, Clone, Serialize, Deserialize)]
17pub struct TaskPushNotificationConfig {
18    /// Optional tenant identifier.
19    #[serde(skip_serializing_if = "Option::is_none")]
20    pub tenant: Option<String>,
21    /// Configuration identifier.
22    #[serde(rename = "configId", skip_serializing_if = "Option::is_none")]
23    pub config_id: Option<String>,
24    /// Associated task identifier.
25    #[serde(rename = "taskId")]
26    pub task_id: String,
27    /// URL where the notification should be sent.
28    pub url: String,
29    /// Session/task-specific token.
30    #[serde(skip_serializing_if = "Option::is_none")]
31    pub token: Option<String>,
32    /// Authentication information for sending the notification.
33    #[serde(skip_serializing_if = "Option::is_none")]
34    pub authentication: Option<AuthenticationInfo>,
35    /// ISO 8601 timestamp when this configuration was created.
36    #[serde(rename = "createdAt", skip_serializing_if = "Option::is_none")]
37    pub created_at: Option<String>,
38}
39
40#[cfg(test)]
41mod tests {
42    use super::*;
43
44    fn minimal_config() -> TaskPushNotificationConfig {
45        TaskPushNotificationConfig {
46            tenant: None,
47            config_id: None,
48            task_id: "task-1".to_string(),
49            url: "https://example.com/notify".to_string(),
50            token: None,
51            authentication: None,
52            created_at: None,
53        }
54    }
55
56    #[test]
57    fn minimal_config_roundtrip() {
58        let cfg = minimal_config();
59        let json = serde_json::to_string(&cfg).unwrap();
60        let back: TaskPushNotificationConfig = serde_json::from_str(&json).unwrap();
61        assert_eq!(back.task_id, cfg.task_id);
62        assert_eq!(back.url, cfg.url);
63    }
64
65    #[test]
66    fn optional_fields_omitted_when_none() {
67        let cfg = minimal_config();
68        let json = serde_json::to_string(&cfg).unwrap();
69        assert!(!json.contains("tenant"));
70        assert!(!json.contains("configId"));
71        assert!(!json.contains("token"));
72        assert!(!json.contains("authentication"));
73        assert!(!json.contains("createdAt"));
74    }
75
76    #[test]
77    fn json_uses_camel_case_task_id() {
78        let cfg = minimal_config();
79        let json = serde_json::to_string(&cfg).unwrap();
80        assert!(json.contains("taskId"));
81        assert!(!json.contains("task_id"));
82    }
83
84    #[test]
85    fn with_authentication_roundtrip() {
86        let mut cfg = minimal_config();
87        cfg.authentication = Some(AuthenticationInfo {
88            scheme: "Bearer".to_string(),
89            credentials: Some("token-abc".to_string()),
90        });
91        let json = serde_json::to_string(&cfg).unwrap();
92        let back: TaskPushNotificationConfig = serde_json::from_str(&json).unwrap();
93        let auth = back.authentication.unwrap();
94        assert_eq!(auth.scheme, "Bearer");
95        assert_eq!(auth.credentials.as_deref(), Some("token-abc"));
96    }
97
98    #[test]
99    fn authentication_info_credentials_omitted_when_none() {
100        let auth = AuthenticationInfo {
101            scheme: "Basic".to_string(),
102            credentials: None,
103        };
104        let json = serde_json::to_string(&auth).unwrap();
105        assert!(!json.contains("credentials"));
106    }
107}