Skip to main content

koi_embedded/
events.rs

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}