1use perfgate_types::{HostInfo, RunMeta, RunReceipt};
8
9#[derive(Debug, Clone)]
11pub struct PromoteRequest {
12 pub receipt: RunReceipt,
14
15 pub normalize: bool,
18}
19
20#[derive(Debug, Clone)]
22pub struct PromoteResult {
23 pub receipt: RunReceipt,
25}
26
27pub struct PromoteUseCase;
29
30impl PromoteUseCase {
31 pub fn execute(req: PromoteRequest) -> PromoteResult {
37 let receipt = if req.normalize {
38 Self::normalize_receipt(req.receipt)
39 } else {
40 req.receipt
41 };
42
43 PromoteResult { receipt }
44 }
45
46 fn normalize_receipt(mut receipt: RunReceipt) -> RunReceipt {
48 receipt.run = RunMeta {
49 id: "baseline".to_string(),
50 started_at: "1970-01-01T00:00:00Z".to_string(),
51 ended_at: "1970-01-01T00:00:00Z".to_string(),
52 host: HostInfo {
53 os: receipt.run.host.os,
54 arch: receipt.run.host.arch,
55 cpu_count: receipt.run.host.cpu_count,
56 memory_bytes: receipt.run.host.memory_bytes,
57 hostname_hash: receipt.run.host.hostname_hash,
58 },
59 };
60 receipt
61 }
62}
63
64#[cfg(test)]
65mod tests {
66 use super::*;
67 use perfgate_types::{BenchMeta, HostInfo, RunMeta, Sample, Stats, ToolInfo, U64Summary};
68
69 fn create_test_receipt() -> RunReceipt {
70 RunReceipt {
71 schema: "perfgate.run.v1".to_string(),
72 tool: ToolInfo {
73 name: "perfgate".to_string(),
74 version: "0.1.0".to_string(),
75 },
76 run: RunMeta {
77 id: "unique-run-id-12345".to_string(),
78 started_at: "2024-01-15T10:00:00Z".to_string(),
79 ended_at: "2024-01-15T10:00:05Z".to_string(),
80 host: HostInfo {
81 os: "linux".to_string(),
82 arch: "x86_64".to_string(),
83 cpu_count: Some(4),
84 memory_bytes: Some(8_000_000_000),
85 hostname_hash: Some("testhash123".to_string()),
86 },
87 },
88 bench: BenchMeta {
89 name: "test-benchmark".to_string(),
90 cwd: None,
91 command: vec!["echo".to_string(), "hello".to_string()],
92 repeat: 5,
93 warmup: 0,
94 work_units: None,
95 timeout_ms: None,
96 },
97 samples: vec![Sample {
98 wall_ms: 100,
99 exit_code: 0,
100 warmup: false,
101 timed_out: false,
102 cpu_ms: None,
103 page_faults: None,
104 ctx_switches: None,
105 max_rss_kb: None,
106 io_read_bytes: None,
107 io_write_bytes: None,
108 network_packets: None,
109 energy_uj: None,
110 binary_bytes: None,
111 stdout: None,
112 stderr: None,
113 }],
114 stats: Stats {
115 wall_ms: U64Summary::new(100, 100, 100),
116 cpu_ms: None,
117 page_faults: None,
118 ctx_switches: None,
119 max_rss_kb: None,
120 io_read_bytes: None,
121 io_write_bytes: None,
122 network_packets: None,
123 energy_uj: None,
124 binary_bytes: None,
125 throughput_per_s: None,
126 },
127 }
128 }
129
130 #[test]
131 fn test_promote_without_normalize() {
132 let receipt = create_test_receipt();
133 let original_run_id = receipt.run.id.clone();
134 let original_started_at = receipt.run.started_at.clone();
135
136 let result = PromoteUseCase::execute(PromoteRequest {
137 receipt,
138 normalize: false,
139 });
140
141 assert_eq!(result.receipt.run.id, original_run_id);
143 assert_eq!(result.receipt.run.started_at, original_started_at);
144 }
145
146 #[test]
147 fn test_promote_with_normalize() {
148 let receipt = create_test_receipt();
149
150 let result = PromoteUseCase::execute(PromoteRequest {
151 receipt,
152 normalize: true,
153 });
154
155 assert_eq!(result.receipt.run.id, "baseline");
157 assert_eq!(result.receipt.run.started_at, "1970-01-01T00:00:00Z");
158 assert_eq!(result.receipt.run.ended_at, "1970-01-01T00:00:00Z");
159
160 assert_eq!(result.receipt.run.host.os, "linux");
162 assert_eq!(result.receipt.run.host.arch, "x86_64");
163
164 assert_eq!(result.receipt.bench.name, "test-benchmark");
166 assert_eq!(result.receipt.stats.wall_ms.median, 100);
167 }
168
169 #[test]
170 fn test_normalize_preserves_bench_data() {
171 let receipt = create_test_receipt();
172
173 let result = PromoteUseCase::execute(PromoteRequest {
174 receipt: receipt.clone(),
175 normalize: true,
176 });
177
178 assert_eq!(result.receipt.bench.name, receipt.bench.name);
180 assert_eq!(result.receipt.bench.command, receipt.bench.command);
181 assert_eq!(result.receipt.bench.repeat, receipt.bench.repeat);
182 assert_eq!(result.receipt.bench.warmup, receipt.bench.warmup);
183
184 assert_eq!(result.receipt.samples.len(), receipt.samples.len());
186 assert_eq!(
187 result.receipt.samples[0].wall_ms,
188 receipt.samples[0].wall_ms
189 );
190
191 assert_eq!(
193 result.receipt.stats.wall_ms.median,
194 receipt.stats.wall_ms.median
195 );
196 }
197
198 #[test]
199 fn test_normalize_preserves_schema_and_tool() {
200 let receipt = create_test_receipt();
201
202 let result = PromoteUseCase::execute(PromoteRequest {
203 receipt: receipt.clone(),
204 normalize: true,
205 });
206
207 assert_eq!(result.receipt.schema, receipt.schema);
208 assert_eq!(result.receipt.tool.name, receipt.tool.name);
209 assert_eq!(result.receipt.tool.version, receipt.tool.version);
210 }
211
212 #[test]
213 fn test_promote_preserves_optional_none_fields() {
214 let mut receipt = create_test_receipt();
215 receipt.run.host.cpu_count = None;
216 receipt.run.host.memory_bytes = None;
217 receipt.run.host.hostname_hash = None;
218 receipt.bench.cwd = None;
219 receipt.bench.work_units = None;
220 receipt.bench.timeout_ms = None;
221
222 let result = PromoteUseCase::execute(PromoteRequest {
223 receipt,
224 normalize: true,
225 });
226
227 assert!(result.receipt.run.host.cpu_count.is_none());
228 assert!(result.receipt.run.host.memory_bytes.is_none());
229 assert!(result.receipt.run.host.hostname_hash.is_none());
230 assert!(result.receipt.bench.cwd.is_none());
231 assert!(result.receipt.bench.work_units.is_none());
232 }
233
234 #[test]
235 fn test_promote_normalize_idempotent() {
236 let receipt = create_test_receipt();
237
238 let first = PromoteUseCase::execute(PromoteRequest {
239 receipt,
240 normalize: true,
241 });
242
243 let second = PromoteUseCase::execute(PromoteRequest {
244 receipt: first.receipt.clone(),
245 normalize: true,
246 });
247
248 assert_eq!(first.receipt.run.id, second.receipt.run.id);
249 assert_eq!(first.receipt.run.started_at, second.receipt.run.started_at);
250 assert_eq!(first.receipt.run.ended_at, second.receipt.run.ended_at);
251 }
252}