durable_execution_sdk_testing/checkpoint_server/
checkpoint_token.rs1use base64::{engine::general_purpose::URL_SAFE_NO_PAD, Engine};
7use serde::{Deserialize, Serialize};
8
9use crate::error::TestError;
10
11use super::types::{CheckpointToken, ExecutionId, InvocationId};
12
13#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
15pub struct CheckpointTokenData {
16 pub execution_id: ExecutionId,
18 pub token: String,
20 pub invocation_id: InvocationId,
22}
23
24pub fn encode_checkpoint_token(data: &CheckpointTokenData) -> CheckpointToken {
34 let json = serde_json::to_string(data).expect("CheckpointTokenData should serialize");
35 URL_SAFE_NO_PAD.encode(json.as_bytes())
36}
37
38pub fn decode_checkpoint_token(token: &str) -> Result<CheckpointTokenData, TestError> {
48 let bytes = URL_SAFE_NO_PAD
49 .decode(token)
50 .map_err(|e| TestError::InvalidCheckpointToken(format!("base64 decode error: {}", e)))?;
51
52 let json = String::from_utf8(bytes)
53 .map_err(|e| TestError::InvalidCheckpointToken(format!("utf8 decode error: {}", e)))?;
54
55 serde_json::from_str(&json)
56 .map_err(|e| TestError::InvalidCheckpointToken(format!("json parse error: {}", e)))
57}
58
59#[cfg(test)]
60mod tests {
61 use super::*;
62
63 #[test]
64 fn test_encode_decode_roundtrip() {
65 let data = CheckpointTokenData {
66 execution_id: "exec-123".to_string(),
67 token: "token-abc".to_string(),
68 invocation_id: "inv-456".to_string(),
69 };
70
71 let encoded = encode_checkpoint_token(&data);
72 let decoded = decode_checkpoint_token(&encoded).unwrap();
73
74 assert_eq!(data, decoded);
75 }
76
77 #[test]
78 fn test_decode_invalid_base64() {
79 let result = decode_checkpoint_token("not-valid-base64!!!");
80 assert!(result.is_err());
81 match result {
82 Err(TestError::InvalidCheckpointToken(msg)) => {
83 assert!(msg.contains("base64"));
84 }
85 _ => panic!("Expected InvalidCheckpointToken error"),
86 }
87 }
88
89 #[test]
90 fn test_decode_invalid_json() {
91 let invalid_json = URL_SAFE_NO_PAD.encode(b"not json");
93 let result = decode_checkpoint_token(&invalid_json);
94 assert!(result.is_err());
95 match result {
96 Err(TestError::InvalidCheckpointToken(msg)) => {
97 assert!(msg.contains("json"));
98 }
99 _ => panic!("Expected InvalidCheckpointToken error"),
100 }
101 }
102
103 #[test]
104 fn test_encode_produces_url_safe_string() {
105 let data = CheckpointTokenData {
106 execution_id: "exec/with+special=chars".to_string(),
107 token: "token".to_string(),
108 invocation_id: "inv".to_string(),
109 };
110
111 let encoded = encode_checkpoint_token(&data);
112
113 assert!(!encoded.contains('+'));
115 assert!(!encoded.contains('/'));
116 }
118}