Skip to main content

agm_core/model/
mem_file.rs

1//! Memory sidecar model types for `.agm.mem` files.
2
3use std::collections::BTreeMap;
4
5use serde::{Deserialize, Serialize};
6
7use super::memory::{MemoryScope, MemoryTtl};
8
9/// Represents a parsed `.agm.mem` sidecar file.
10#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
11pub struct MemFile {
12    pub format_version: String,
13    pub package: String,
14    pub updated_at: String,
15    pub entries: BTreeMap<String, MemFileEntry>,
16}
17
18/// A single memory entry.
19#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
20pub struct MemFileEntry {
21    pub topic: String,
22    pub scope: MemoryScope,
23    pub ttl: MemoryTtl,
24    pub value: String,
25    pub created_at: String,
26    pub updated_at: String,
27}
28
29#[cfg(test)]
30mod tests {
31    use super::*;
32
33    fn make_entry(ttl: MemoryTtl) -> MemFileEntry {
34        MemFileEntry {
35            topic: "infrastructure".to_owned(),
36            scope: MemoryScope::Project,
37            ttl,
38            value: "some value".to_owned(),
39            created_at: "2026-04-08T10:00:00Z".to_owned(),
40            updated_at: "2026-04-08T10:00:00Z".to_owned(),
41        }
42    }
43
44    // -----------------------------------------------------------------------
45    // A: Serde roundtrip — minimal (no entries)
46    // -----------------------------------------------------------------------
47
48    #[test]
49    fn test_mem_file_serde_roundtrip_minimal() {
50        let mem = MemFile {
51            format_version: "1.0".to_owned(),
52            package: "test.pkg".to_owned(),
53            updated_at: "2026-04-08T10:00:00Z".to_owned(),
54            entries: BTreeMap::new(),
55        };
56        let json = serde_json::to_string(&mem).unwrap();
57        let back: MemFile = serde_json::from_str(&json).unwrap();
58        assert_eq!(mem, back);
59    }
60
61    // -----------------------------------------------------------------------
62    // B: Serde roundtrip — permanent TTL
63    // -----------------------------------------------------------------------
64
65    #[test]
66    fn test_mem_file_serde_roundtrip_permanent_ttl() {
67        let mut entries = BTreeMap::new();
68        entries.insert("key.one".to_owned(), make_entry(MemoryTtl::Permanent));
69        let mem = MemFile {
70            format_version: "1.0".to_owned(),
71            package: "test.pkg".to_owned(),
72            updated_at: "2026-04-08T10:00:00Z".to_owned(),
73            entries,
74        };
75        let json = serde_json::to_string(&mem).unwrap();
76        let back: MemFile = serde_json::from_str(&json).unwrap();
77        assert_eq!(mem, back);
78    }
79
80    // -----------------------------------------------------------------------
81    // C: Serde roundtrip — session TTL
82    // -----------------------------------------------------------------------
83
84    #[test]
85    fn test_mem_file_serde_roundtrip_session_ttl() {
86        let mut entries = BTreeMap::new();
87        entries.insert("key.session".to_owned(), make_entry(MemoryTtl::Session));
88        let mem = MemFile {
89            format_version: "1.0".to_owned(),
90            package: "test.pkg".to_owned(),
91            updated_at: "2026-04-08T10:00:00Z".to_owned(),
92            entries,
93        };
94        let json = serde_json::to_string(&mem).unwrap();
95        let back: MemFile = serde_json::from_str(&json).unwrap();
96        assert_eq!(mem, back);
97    }
98
99    // -----------------------------------------------------------------------
100    // D: Serde roundtrip — duration TTL
101    // -----------------------------------------------------------------------
102
103    #[test]
104    fn test_mem_file_serde_roundtrip_duration_ttl() {
105        let mut entries = BTreeMap::new();
106        entries.insert(
107            "key.duration".to_owned(),
108            make_entry(MemoryTtl::Duration("P30D".to_owned())),
109        );
110        let mem = MemFile {
111            format_version: "1.0".to_owned(),
112            package: "test.pkg".to_owned(),
113            updated_at: "2026-04-08T10:00:00Z".to_owned(),
114            entries,
115        };
116        let json = serde_json::to_string(&mem).unwrap();
117        let back: MemFile = serde_json::from_str(&json).unwrap();
118        assert_eq!(mem, back);
119    }
120
121    // -----------------------------------------------------------------------
122    // E: All scopes in a single file
123    // -----------------------------------------------------------------------
124
125    #[test]
126    fn test_mem_file_serde_roundtrip_all_scopes() {
127        let mut entries = BTreeMap::new();
128        for (key, scope) in [
129            ("k.node", MemoryScope::Node),
130            ("k.session", MemoryScope::Session),
131            ("k.project", MemoryScope::Project),
132            ("k.global", MemoryScope::Global),
133        ] {
134            entries.insert(
135                key.to_owned(),
136                MemFileEntry {
137                    topic: "test".to_owned(),
138                    scope,
139                    ttl: MemoryTtl::Permanent,
140                    value: "val".to_owned(),
141                    created_at: "2026-04-08T10:00:00Z".to_owned(),
142                    updated_at: "2026-04-08T10:00:00Z".to_owned(),
143                },
144            );
145        }
146        let mem = MemFile {
147            format_version: "1.0".to_owned(),
148            package: "test.pkg".to_owned(),
149            updated_at: "2026-04-08T10:00:00Z".to_owned(),
150            entries,
151        };
152        let json = serde_json::to_string(&mem).unwrap();
153        let back: MemFile = serde_json::from_str(&json).unwrap();
154        assert_eq!(mem, back);
155    }
156}