1use koi_common::types::ServiceRecord;
2use koi_health::HealthStatus;
3use koi_proxy::ProxyEntry;
4
5#[derive(Debug, Clone)]
6pub enum KoiEvent {
7 MdnsFound(ServiceRecord),
8 MdnsResolved(ServiceRecord),
9 MdnsRemoved {
10 name: String,
11 service_type: String,
12 },
13 DnsEntryUpdated {
14 name: String,
15 ip: String,
16 },
17 DnsEntryRemoved {
18 name: String,
19 },
20 HealthChanged {
21 name: String,
22 status: HealthStatus,
23 },
24 CertmeshMemberJoined {
25 hostname: String,
26 fingerprint: String,
27 },
28 CertmeshMemberRevoked {
29 hostname: String,
30 },
31 CertmeshDestroyed,
32 ProxyEntryUpdated {
33 entry: ProxyEntry,
34 },
35 ProxyEntryRemoved {
36 name: String,
37 },
38 RuntimeInstanceStarted {
39 name: String,
40 backend: String,
41 },
42 RuntimeInstanceStopped {
43 name: String,
44 },
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50 use std::collections::HashMap;
51
52 fn sample_record() -> ServiceRecord {
53 ServiceRecord {
54 name: "My App".to_string(),
55 service_type: "_http._tcp".to_string(),
56 host: Some("server.local".to_string()),
57 ip: Some("192.168.1.42".to_string()),
58 port: Some(8080),
59 txt: HashMap::new(),
60 }
61 }
62
63 #[test]
64 fn mdns_found_variant_construction() {
65 let event = KoiEvent::MdnsFound(sample_record());
66 assert!(matches!(event, KoiEvent::MdnsFound(ref r) if r.name == "My App"));
67 }
68
69 #[test]
70 fn mdns_resolved_variant_construction() {
71 let event = KoiEvent::MdnsResolved(sample_record());
72 assert!(matches!(event, KoiEvent::MdnsResolved(ref r) if r.port == Some(8080)));
73 }
74
75 #[test]
76 fn mdns_removed_variant_construction() {
77 let event = KoiEvent::MdnsRemoved {
78 name: "Old Service".to_string(),
79 service_type: "_http._tcp".to_string(),
80 };
81 assert!(matches!(event, KoiEvent::MdnsRemoved { ref name, .. } if name == "Old Service"));
82 }
83
84 #[test]
85 fn dns_entry_updated_variant() {
86 let event = KoiEvent::DnsEntryUpdated {
87 name: "grafana".to_string(),
88 ip: "10.0.0.5".to_string(),
89 };
90 assert!(
91 matches!(event, KoiEvent::DnsEntryUpdated { ref name, ref ip } if name == "grafana" && ip == "10.0.0.5")
92 );
93 }
94
95 #[test]
96 fn dns_entry_removed_variant() {
97 let event = KoiEvent::DnsEntryRemoved {
98 name: "grafana".to_string(),
99 };
100 assert!(matches!(event, KoiEvent::DnsEntryRemoved { ref name } if name == "grafana"));
101 }
102
103 #[test]
104 fn health_changed_variant() {
105 let event = KoiEvent::HealthChanged {
106 name: "web-api".to_string(),
107 status: HealthStatus::Up,
108 };
109 assert!(
110 matches!(event, KoiEvent::HealthChanged { ref name, status: HealthStatus::Up } if name == "web-api")
111 );
112 }
113
114 #[test]
115 fn certmesh_member_joined_variant() {
116 let event = KoiEvent::CertmeshMemberJoined {
117 hostname: "node1".to_string(),
118 fingerprint: "abc123".to_string(),
119 };
120 assert!(
121 matches!(event, KoiEvent::CertmeshMemberJoined { ref hostname, .. } if hostname == "node1")
122 );
123 }
124
125 #[test]
126 fn certmesh_member_revoked_variant() {
127 let event = KoiEvent::CertmeshMemberRevoked {
128 hostname: "node2".to_string(),
129 };
130 assert!(
131 matches!(event, KoiEvent::CertmeshMemberRevoked { ref hostname } if hostname == "node2")
132 );
133 }
134
135 #[test]
136 fn certmesh_destroyed_variant() {
137 let event = KoiEvent::CertmeshDestroyed;
138 assert!(matches!(event, KoiEvent::CertmeshDestroyed));
139 }
140
141 #[test]
142 fn proxy_entry_updated_variant() {
143 let entry = ProxyEntry {
144 name: "grafana".to_string(),
145 listen_port: 443,
146 backend: "http://localhost:3000".to_string(),
147 allow_remote: false,
148 };
149 let event = KoiEvent::ProxyEntryUpdated {
150 entry: entry.clone(),
151 };
152 assert!(
153 matches!(event, KoiEvent::ProxyEntryUpdated { ref entry } if entry.name == "grafana")
154 );
155 }
156
157 #[test]
158 fn proxy_entry_removed_variant() {
159 let event = KoiEvent::ProxyEntryRemoved {
160 name: "grafana".to_string(),
161 };
162 assert!(matches!(event, KoiEvent::ProxyEntryRemoved { ref name } if name == "grafana"));
163 }
164
165 #[test]
166 fn runtime_instance_started_variant() {
167 let event = KoiEvent::RuntimeInstanceStarted {
168 name: "nginx".to_string(),
169 backend: "docker".to_string(),
170 };
171 assert!(
172 matches!(event, KoiEvent::RuntimeInstanceStarted { ref name, ref backend } if name == "nginx" && backend == "docker")
173 );
174 }
175
176 #[test]
177 fn runtime_instance_stopped_variant() {
178 let event = KoiEvent::RuntimeInstanceStopped {
179 name: "nginx".to_string(),
180 };
181 assert!(matches!(event, KoiEvent::RuntimeInstanceStopped { ref name } if name == "nginx"));
182 }
183
184 #[test]
185 fn clone_preserves_data() {
186 let event = KoiEvent::MdnsFound(sample_record());
187 let cloned = event.clone();
188 match (&event, &cloned) {
189 (KoiEvent::MdnsFound(a), KoiEvent::MdnsFound(b)) => {
190 assert_eq!(a.name, b.name);
191 assert_eq!(a.port, b.port);
192 assert_eq!(a.service_type, b.service_type);
193 }
194 _ => panic!("clone should preserve variant"),
195 }
196 }
197
198 #[test]
199 fn debug_does_not_panic() {
200 let events = vec![
201 KoiEvent::MdnsFound(sample_record()),
202 KoiEvent::MdnsRemoved {
203 name: "x".to_string(),
204 service_type: "y".to_string(),
205 },
206 KoiEvent::DnsEntryUpdated {
207 name: "a".to_string(),
208 ip: "1.2.3.4".to_string(),
209 },
210 KoiEvent::DnsEntryRemoved {
211 name: "a".to_string(),
212 },
213 KoiEvent::HealthChanged {
214 name: "svc".to_string(),
215 status: HealthStatus::Down,
216 },
217 KoiEvent::CertmeshMemberJoined {
218 hostname: "h".to_string(),
219 fingerprint: "f".to_string(),
220 },
221 KoiEvent::CertmeshMemberRevoked {
222 hostname: "h".to_string(),
223 },
224 KoiEvent::CertmeshDestroyed,
225 KoiEvent::ProxyEntryUpdated {
226 entry: ProxyEntry {
227 name: "p".to_string(),
228 listen_port: 443,
229 backend: "http://localhost".to_string(),
230 allow_remote: false,
231 },
232 },
233 KoiEvent::ProxyEntryRemoved {
234 name: "p".to_string(),
235 },
236 KoiEvent::RuntimeInstanceStarted {
237 name: "web".to_string(),
238 backend: "docker".to_string(),
239 },
240 KoiEvent::RuntimeInstanceStopped {
241 name: "web".to_string(),
242 },
243 ];
244 for event in &events {
245 let _ = format!("{event:?}");
246 }
247 }
248}