aivcs_core/domain/
release.rs1use chrono::{DateTime, Utc};
4use serde::{Deserialize, Serialize};
5use uuid::Uuid;
6
7#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
9#[serde(rename_all = "UPPERCASE")]
10pub enum ReleaseEnvironment {
11 Dev,
12 Staging,
13 Production,
14}
15
16#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
18pub struct Release {
19 pub release_id: Uuid,
21
22 pub agent_name: String,
24
25 pub spec_digest: String,
27
28 pub tools_digest: String,
30
31 pub graph_digest: String,
33
34 pub version: String,
36
37 pub environment: ReleaseEnvironment,
39
40 pub promoted_at: DateTime<Utc>,
42
43 pub promoted_by: String,
45
46 pub metadata: serde_json::Value,
48}
49
50impl Release {
51 pub fn new(
53 agent_name: String,
54 spec_digest: String,
55 tools_digest: String,
56 graph_digest: String,
57 version: String,
58 environment: ReleaseEnvironment,
59 promoted_by: String,
60 ) -> Self {
61 Self {
62 release_id: Uuid::new_v4(),
63 agent_name,
64 spec_digest,
65 tools_digest,
66 graph_digest,
67 version,
68 environment,
69 promoted_at: Utc::now(),
70 promoted_by,
71 metadata: serde_json::json!({}),
72 }
73 }
74}
75
76#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
78pub struct ReleasePointer {
79 pub agent_name: String,
81
82 pub current_spec_digest: String,
84
85 pub previous_spec_digest: Option<String>,
87
88 pub last_updated: DateTime<Utc>,
90}
91
92impl ReleasePointer {
93 pub fn new(agent_name: String, current_spec_digest: String) -> Self {
95 Self {
96 agent_name,
97 current_spec_digest,
98 previous_spec_digest: None,
99 last_updated: Utc::now(),
100 }
101 }
102
103 pub fn promote(&mut self, new_spec_digest: String) {
105 self.previous_spec_digest = Some(self.current_spec_digest.clone());
106 self.current_spec_digest = new_spec_digest;
107 self.last_updated = Utc::now();
108 }
109}
110
111#[cfg(test)]
112mod tests {
113 use super::*;
114
115 #[test]
116 fn test_release_serde_roundtrip() {
117 let release = Release::new(
118 "my_agent".to_string(),
119 "spec_digest_123".to_string(),
120 "tools_digest_abc".to_string(),
121 "graph_digest_xyz".to_string(),
122 "1.2.3".to_string(),
123 ReleaseEnvironment::Production,
124 "github-ci".to_string(),
125 );
126
127 let json = serde_json::to_string(&release).expect("serialize");
128 let deserialized: Release = serde_json::from_str(&json).expect("deserialize");
129
130 assert_eq!(release, deserialized);
131 }
132
133 #[test]
134 fn test_release_environment_serde() {
135 let envs = [
136 ReleaseEnvironment::Dev,
137 ReleaseEnvironment::Staging,
138 ReleaseEnvironment::Production,
139 ];
140
141 for env in &envs {
142 let json = serde_json::to_string(env).expect("serialize");
143 let deserialized: ReleaseEnvironment =
144 serde_json::from_str(&json).expect("deserialize");
145 assert_eq!(*env, deserialized);
146 }
147 }
148
149 #[test]
150 fn test_release_pointer_previous_is_none() {
151 let pointer = ReleasePointer::new("my_agent".to_string(), "spec_digest_abc".to_string());
152
153 assert_eq!(pointer.agent_name, "my_agent");
154 assert_eq!(pointer.current_spec_digest, "spec_digest_abc");
155 assert!(pointer.previous_spec_digest.is_none());
156 }
157
158 #[test]
159 fn test_release_pointer_promote() {
160 let mut pointer = ReleasePointer::new("my_agent".to_string(), "spec_digest_v1".to_string());
161
162 pointer.promote("spec_digest_v2".to_string());
163
164 assert_eq!(pointer.current_spec_digest, "spec_digest_v2");
165 assert_eq!(
166 pointer.previous_spec_digest,
167 Some("spec_digest_v1".to_string())
168 );
169 }
170
171 #[test]
172 fn test_release_pointer_serde_roundtrip() {
173 let pointer =
174 ReleasePointer::new("my_agent".to_string(), "spec_digest_current".to_string());
175
176 let json = serde_json::to_string(&pointer).expect("serialize");
177 let deserialized: ReleasePointer = serde_json::from_str(&json).expect("deserialize");
178
179 assert_eq!(pointer, deserialized);
180 }
181
182 #[test]
183 fn test_release_environment_all_variants() {
184 let dev_json = serde_json::to_string(&ReleaseEnvironment::Dev).expect("serialize");
185 let staging_json = serde_json::to_string(&ReleaseEnvironment::Staging).expect("serialize");
186 let prod_json = serde_json::to_string(&ReleaseEnvironment::Production).expect("serialize");
187
188 assert!(dev_json.contains("DEV"));
189 assert!(staging_json.contains("STAGING"));
190 assert!(prod_json.contains("PRODUCTION"));
191 }
192}