pidge_client/auth/
tokens.rs1use chrono::{DateTime, Duration, Utc};
4use serde::{Deserialize, Serialize};
5
6#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
8pub struct TokenSet {
9 pub access_token: String,
10 pub refresh_token: String,
11 pub expires_at: DateTime<Utc>,
12}
13
14impl TokenSet {
15 pub fn needs_refresh(&self) -> bool {
18 Utc::now() + Duration::seconds(60) >= self.expires_at
19 }
20}
21
22#[cfg(test)]
23mod tests {
24 use super::*;
25
26 #[test]
27 fn fresh_token_does_not_need_refresh() {
28 let t = TokenSet {
29 access_token: "a".into(),
30 refresh_token: "r".into(),
31 expires_at: Utc::now() + Duration::seconds(3600),
32 };
33 assert!(!t.needs_refresh());
34 }
35
36 #[test]
37 fn token_expiring_within_60s_needs_refresh() {
38 let t = TokenSet {
39 access_token: "a".into(),
40 refresh_token: "r".into(),
41 expires_at: Utc::now() + Duration::seconds(30),
42 };
43 assert!(t.needs_refresh());
44 }
45
46 #[test]
47 fn already_expired_token_needs_refresh() {
48 let t = TokenSet {
49 access_token: "a".into(),
50 refresh_token: "r".into(),
51 expires_at: Utc::now() - Duration::seconds(10),
52 };
53 assert!(t.needs_refresh());
54 }
55
56 #[test]
57 fn tokens_roundtrip_through_json() {
58 let t = TokenSet {
59 access_token: "ey…".into(),
60 refresh_token: "M.C5…".into(),
61 expires_at: DateTime::parse_from_rfc3339("2026-05-13T23:00:00Z")
62 .unwrap()
63 .to_utc(),
64 };
65 let json = serde_json::to_string(&t).unwrap();
66 let t2: TokenSet = serde_json::from_str(&json).unwrap();
67 assert_eq!(t, t2);
68 }
69}