http_tunnel_common/models/
pending.rs

1use serde::{Deserialize, Serialize};
2
3/// Pending request state tracked in DynamoDB while waiting for response
4#[derive(Debug, Clone, Serialize, Deserialize)]
5pub struct PendingRequest {
6    /// Unique request identifier
7    pub request_id: String,
8
9    /// Connection ID that should handle this request
10    pub connection_id: String,
11
12    /// Original API Gateway request context (for response)
13    pub api_gateway_request_id: String,
14
15    /// Timestamp when request was forwarded (Unix epoch seconds)
16    pub created_at: i64,
17
18    /// TTL for auto-cleanup (Unix epoch seconds)
19    /// Should be short-lived (e.g., 30 seconds)
20    pub ttl: i64,
21}
22
23impl PendingRequest {
24    /// Create a new pending request entry
25    pub fn new(
26        request_id: String,
27        connection_id: String,
28        api_gateway_request_id: String,
29        created_at: i64,
30        ttl: i64,
31    ) -> Self {
32        Self {
33            request_id,
34            connection_id,
35            api_gateway_request_id,
36            created_at,
37            ttl,
38        }
39    }
40
41    /// Check if the request has expired based on current timestamp
42    pub fn is_expired(&self, current_timestamp: i64) -> bool {
43        current_timestamp > self.ttl
44    }
45
46    /// Get the age of the request in seconds
47    pub fn age_secs(&self, current_timestamp: i64) -> i64 {
48        current_timestamp - self.created_at
49    }
50}
51
52#[cfg(test)]
53mod tests {
54    use super::*;
55
56    #[test]
57    fn test_pending_request_creation() {
58        let pending = PendingRequest::new(
59            "req_123".to_string(),
60            "conn_abc".to_string(),
61            "gw_req_xyz".to_string(),
62            1234567890,
63            1234567920,
64        );
65
66        assert_eq!(pending.request_id, "req_123");
67        assert_eq!(pending.connection_id, "conn_abc");
68        assert_eq!(pending.api_gateway_request_id, "gw_req_xyz");
69        assert_eq!(pending.created_at, 1234567890);
70        assert_eq!(pending.ttl, 1234567920);
71    }
72
73    #[test]
74    fn test_pending_request_expiration() {
75        let pending = PendingRequest::new(
76            "req_123".to_string(),
77            "conn_abc".to_string(),
78            "gw_req_xyz".to_string(),
79            1234567890,
80            1234567920,
81        );
82
83        // Not expired before TTL
84        assert!(!pending.is_expired(1234567900));
85        assert!(!pending.is_expired(1234567920));
86
87        // Expired after TTL
88        assert!(pending.is_expired(1234567921));
89        assert!(pending.is_expired(1234568000));
90    }
91
92    #[test]
93    fn test_pending_request_age() {
94        let pending = PendingRequest::new(
95            "req_123".to_string(),
96            "conn_abc".to_string(),
97            "gw_req_xyz".to_string(),
98            1234567890,
99            1234567920,
100        );
101
102        assert_eq!(pending.age_secs(1234567890), 0);
103        assert_eq!(pending.age_secs(1234567900), 10);
104        assert_eq!(pending.age_secs(1234567920), 30);
105    }
106
107    #[test]
108    fn test_pending_request_serialization() {
109        let pending = PendingRequest::new(
110            "req_abc123".to_string(),
111            "conn_xyz789".to_string(),
112            "gw_req_456".to_string(),
113            1234567890,
114            1234567920,
115        );
116
117        let json = serde_json::to_string(&pending).unwrap();
118        assert!(json.contains(r#""request_id":"req_abc123"#));
119        assert!(json.contains(r#""connection_id":"conn_xyz789"#));
120        assert!(json.contains(r#""api_gateway_request_id":"gw_req_456"#));
121
122        let parsed: PendingRequest = serde_json::from_str(&json).unwrap();
123        assert_eq!(parsed.request_id, pending.request_id);
124        assert_eq!(parsed.connection_id, pending.connection_id);
125        assert_eq!(
126            parsed.api_gateway_request_id,
127            pending.api_gateway_request_id
128        );
129        assert_eq!(parsed.created_at, pending.created_at);
130        assert_eq!(parsed.ttl, pending.ttl);
131    }
132
133    #[test]
134    fn test_pending_request_deserialization() {
135        let json = r#"{
136            "request_id": "req_test",
137            "connection_id": "conn_test",
138            "api_gateway_request_id": "gw_test",
139            "created_at": 1000000000,
140            "ttl": 1000000030
141        }"#;
142
143        let parsed: PendingRequest = serde_json::from_str(json).unwrap();
144        assert_eq!(parsed.request_id, "req_test");
145        assert_eq!(parsed.connection_id, "conn_test");
146        assert_eq!(parsed.api_gateway_request_id, "gw_test");
147        assert_eq!(parsed.created_at, 1000000000);
148        assert_eq!(parsed.ttl, 1000000030);
149    }
150}