Skip to main content

max / makenotwork

28.7 KB · 980 lines History Blame Raw
1 //! Tests for [`super`].
2
3 use super::*;
4
5 fn now() -> DateTime<Utc> {
6 "2026-07-21T18:24:39Z".parse().unwrap()
7 }
8
9 fn checked_at() -> String {
10 "2026-07-21T18:24:00Z".into()
11 }
12
13 fn healthy(name: &str) -> TargetView {
14 TargetView {
15 name: name.into(),
16 label: name.to_uppercase(),
17 health_configured: true,
18 health: Some(HealthView {
19 status: HealthStatus::Operational,
20 checked_at: checked_at(),
21 version: Some("1.4.0".into()),
22 error: None,
23 }),
24 uptime_24h: Some(100.0),
25 latency_avg_ms: Some(42.0),
26 tls: None,
27 incident: None,
28 whois: None,
29 backups: Vec::new(),
30 scan_pipeline: None,
31 systemd: None,
32 ca_bundle: None,
33 synckit_fleet: None,
34 tests: None,
35 dns: None,
36 cors: None,
37 }
38 }
39
40 fn node<'a>(p: &'a Payload, id: &str) -> &'a Node {
41 p.node(id).unwrap_or_else(|| panic!("no node {id}"))
42 }
43
44 #[test]
45 fn a_healthy_target_is_ok_and_structurally_sound() {
46 let p = payload(&[healthy("mnw")], now());
47
48 assert_eq!(p.source, SOURCE);
49 assert_eq!(p.schema, ops_status::SCHEMA_VERSION);
50 assert_eq!(p.validate(), Ok(()));
51 assert_eq!(node(&p, "target:mnw").status, Status::Ok);
52 assert_eq!(node(&p, "target:mnw").label, "MNW");
53 assert_eq!(p.worst_status(), Status::Ok);
54 }
55
56 #[test]
57 fn uptime_and_latency_are_typed_values_not_strings() {
58 let p = payload(&[healthy("mnw")], now());
59 let n = node(&p, "target:mnw");
60
61 let uptime = n.fields.iter().find(|f| f.label == "uptime 24h").unwrap();
62 assert_eq!(
63 uptime.value,
64 Value::Progress {
65 value: 100.0,
66 max: 100.0,
67 unit: Some("%".into())
68 }
69 );
70 let latency = n.fields.iter().find(|f| f.label == "latency 24h").unwrap();
71 assert_eq!(
72 latency.value,
73 Value::Quantity {
74 value: 42.0,
75 unit: Some("ms".into())
76 }
77 );
78 }
79
80 #[test]
81 fn an_unreachable_target_is_failed_and_says_why() {
82 let mut t = healthy("mnw");
83 t.health = Some(HealthView {
84 status: HealthStatus::Unreachable,
85 checked_at: checked_at(),
86 version: None,
87 error: Some("connection timed out".into()),
88 });
89 let p = payload(&[t], now());
90
91 let n = node(&p, "target:mnw");
92 assert_eq!(n.status, Status::Failed);
93 assert_eq!(n.conditions[0].condition_type, "health");
94 assert_eq!(
95 n.conditions[0].detail.as_deref(),
96 Some("connection timed out")
97 );
98 assert_eq!(p.worst_status(), Status::Failed);
99 }
100
101 #[test]
102 fn a_target_never_checked_is_pending_not_healthy() {
103 let mut t = healthy("new");
104 t.health = None;
105 let p = payload(&[t], now());
106
107 let n = node(&p, "target:new");
108 assert_eq!(n.status, Status::Pending);
109 assert_eq!(
110 n.conditions[0].detail.as_deref(),
111 Some("no health check recorded yet")
112 );
113 // No health snapshot means no version and no checked-at field.
114 assert!(n.fields.iter().all(|f| f.label != "version"));
115 assert!(n.fields.iter().all(|f| f.label != "checked"));
116 assert_eq!(p.validate(), Ok(()));
117 }
118
119 #[test]
120 fn an_expiring_certificate_degrades_an_otherwise_healthy_target() {
121 // The Sando contrast: unlike a promotion gate, an expiring cert is a real
122 // problem with the target and must color it even when health is green.
123 let mut t = healthy("mnw");
124 t.tls = Some(TlsView {
125 valid: true,
126 days_remaining: 9,
127 checked_at: checked_at(),
128 error: None,
129 webpki_trusted: Some(true),
130 platform_trusted: Some(true),
131 platform_error: None,
132 });
133 let p = payload(&[t], now());
134
135 let n = node(&p, "target:mnw");
136 assert_eq!(n.status, Status::Degraded);
137 let tls = n
138 .conditions
139 .iter()
140 .find(|c| c.condition_type == "tls")
141 .unwrap();
142 assert_eq!(tls.status, Status::Degraded);
143 assert!(tls.detail.as_deref().unwrap().contains("9 days"));
144 }
145
146 #[test]
147 fn an_expired_certificate_fails_the_target() {
148 let mut t = healthy("mnw");
149 t.tls = Some(TlsView {
150 valid: true,
151 days_remaining: -3,
152 checked_at: checked_at(),
153 error: None,
154 webpki_trusted: Some(true),
155 platform_trusted: Some(true),
156 platform_error: None,
157 });
158 let p = payload(&[t], now());
159
160 let n = node(&p, "target:mnw");
161 assert_eq!(n.status, Status::Failed);
162 let tls = n
163 .conditions
164 .iter()
165 .find(|c| c.condition_type == "tls")
166 .unwrap();
167 assert!(
168 tls.detail
169 .as_deref()
170 .unwrap()
171 .contains("expired 3 days ago")
172 );
173 }
174
175 #[test]
176 fn a_healthy_certificate_stays_ok_but_still_reports_its_runway() {
177 let mut t = healthy("mnw");
178 t.tls = Some(TlsView {
179 valid: true,
180 days_remaining: 60,
181 checked_at: checked_at(),
182 error: None,
183 webpki_trusted: Some(true),
184 platform_trusted: Some(true),
185 platform_error: None,
186 });
187 let p = payload(&[t], now());
188
189 let n = node(&p, "target:mnw");
190 assert_eq!(n.status, Status::Ok);
191 let tls = n
192 .conditions
193 .iter()
194 .find(|c| c.condition_type == "tls")
195 .unwrap();
196 assert_eq!(tls.status, Status::Ok);
197 assert!(tls.detail.as_deref().unwrap().contains("60 days remaining"));
198 }
199
200 #[test]
201 fn a_host_trust_store_that_rejects_a_publicly_valid_chain_degrades_the_target() {
202 // multithreaded takes its outbound trust anchors from the host CA
203 // bundle on every path it has, with no in-binary fallback. A bundle
204 // that goes stale or thin therefore breaks OAuth, link previews and S3
205 // at once while the certificates themselves are perfectly good, which
206 // is why the two stores are reported separately rather than folded.
207 let mut t = healthy("mnw");
208 t.tls = Some(TlsView {
209 valid: true,
210 days_remaining: 60,
211 checked_at: checked_at(),
212 error: None,
213 webpki_trusted: Some(true),
214 platform_trusted: Some(false),
215 platform_error: Some("invalid peer certificate: UnknownIssuer".into()),
216 });
217 let p = payload(&[t], now());
218
219 let n = node(&p, "target:mnw");
220 assert_eq!(n.status, Status::Degraded);
221 let tls = n
222 .conditions
223 .iter()
224 .find(|c| c.condition_type == "tls")
225 .unwrap();
226 assert_eq!(tls.status, Status::Degraded);
227 let detail = tls.detail.as_deref().unwrap();
228 assert!(detail.contains("host trust store"));
229 assert!(detail.contains("UnknownIssuer"));
230 }
231
232 #[test]
233 fn a_thin_ca_bundle_fails_the_target_and_a_stale_one_only_degrades_it() {
234 // The severity split is the point: a bundle below the certificate floor
235 // means outbound TLS is broken now, while a package one release behind
236 // is drift. Collapsing them would either page on drift or bury an outage.
237 let mut thin = healthy("mnw");
238 thin.ca_bundle = Some(CaBundleView {
239 status: "thin".into(),
240 issues: vec!["bundle holds 3 certificates, below the floor of 80".into()],
241 checked_at: checked_at(),
242 error: None,
243 });
244 assert_eq!(
245 node(&payload(&[thin], now()), "target:mnw").status,
246 Status::Failed
247 );
248
249 let mut stale = healthy("mnw");
250 stale.ca_bundle = Some(CaBundleView {
251 status: "stale".into(),
252 issues: vec!["ca-certificates 20260601 installed, 20261101 available".into()],
253 checked_at: checked_at(),
254 error: None,
255 });
256 let p = payload(&[stale], now());
257 let n = node(&p, "target:mnw");
258 assert_eq!(n.status, Status::Degraded);
259 let ca = n
260 .conditions
261 .iter()
262 .find(|c| c.condition_type == "ca_bundle")
263 .unwrap();
264 assert!(ca.detail.as_deref().unwrap().contains("20261101 available"));
265 }
266
267 #[test]
268 fn a_ca_bundle_probe_that_could_not_run_says_so_rather_than_reading_green() {
269 let mut t = healthy("mnw");
270 t.ca_bundle = Some(CaBundleView {
271 status: "error".into(),
272 issues: Vec::new(),
273 checked_at: checked_at(),
274 error: Some("apt-cache policy failed to run: No such file or directory".into()),
275 });
276 let p = payload(&[t], now());
277 let n = node(&p, "target:mnw");
278 assert_eq!(n.status, Status::Degraded);
279 let ca = n
280 .conditions
281 .iter()
282 .find(|c| c.condition_type == "ca_bundle")
283 .unwrap();
284 assert!(ca.detail.as_deref().unwrap().contains("probe error"));
285 }
286
287 #[test]
288 fn a_pre_migration_tls_row_reports_expiry_and_claims_nothing_about_trust() {
289 // Rows written before the trust columns existed carry NULL, not false.
290 // Reading those as "untrusted" would light up every target on the first
291 // run after an upgrade, which trains the eye to ignore the condition.
292 let mut t = healthy("mnw");
293 t.tls = Some(TlsView {
294 valid: true,
295 days_remaining: 60,
296 checked_at: checked_at(),
297 error: None,
298 webpki_trusted: None,
299 platform_trusted: None,
300 platform_error: None,
301 });
302 let p = payload(&[t], now());
303
304 let n = node(&p, "target:mnw");
305 assert_eq!(n.status, Status::Ok);
306 let tls = n
307 .conditions
308 .iter()
309 .find(|c| c.condition_type == "tls")
310 .unwrap();
311 assert_eq!(tls.status, Status::Ok);
312 assert!(tls.detail.as_deref().unwrap().contains("60 days remaining"));
313 }
314
315 #[test]
316 fn an_open_incident_surfaces_with_its_transition_and_start() {
317 let mut t = healthy("mnw");
318 t.incident = Some(IncidentView {
319 from_status: "operational".into(),
320 to_status: "unreachable".into(),
321 started_at: "2026-07-21T17:00:00Z".into(),
322 });
323 // Health has recovered on paper but the incident is still open: the node
324 // must not read green while an incident stands.
325 let p = payload(&[t], now());
326
327 let n = node(&p, "target:mnw");
328 assert_eq!(n.status, Status::Failed);
329 let incident = n
330 .conditions
331 .iter()
332 .find(|c| c.condition_type == "incident")
333 .unwrap();
334 assert_eq!(incident.status, Status::Failed);
335 assert_eq!(
336 incident.detail.as_deref(),
337 Some("operational to unreachable")
338 );
339 assert_eq!(
340 incident.since,
341 Some("2026-07-21T17:00:00Z".parse::<DateTime<Utc>>().unwrap())
342 );
343 }
344
345 #[test]
346 fn a_failed_whois_lookup_is_degraded_not_failed() {
347 // Registrar WHOIS is flaky; a lookup error is not proof the domain lapsed.
348 let mut t = healthy("mnw");
349 t.whois = Some(WhoisView {
350 days_remaining: None,
351 checked_at: checked_at(),
352 error: Some("connection reset".into()),
353 });
354 let p = payload(&[t], now());
355
356 let n = node(&p, "target:mnw");
357 assert_eq!(n.status, Status::Degraded);
358 }
359
360 #[test]
361 fn an_expiring_domain_degrades_the_target() {
362 let mut t = healthy("mnw");
363 t.whois = Some(WhoisView {
364 days_remaining: Some(12),
365 checked_at: checked_at(),
366 error: None,
367 });
368 let p = payload(&[t], now());
369
370 let n = node(&p, "target:mnw");
371 assert_eq!(n.status, Status::Degraded);
372 let whois = n
373 .conditions
374 .iter()
375 .find(|c| c.condition_type == "whois")
376 .unwrap();
377 assert!(whois.detail.as_deref().unwrap().contains("12 days"));
378 }
379
380 #[test]
381 fn a_whois_check_with_no_signal_emits_no_condition() {
382 let mut t = healthy("mnw");
383 t.whois = Some(WhoisView {
384 days_remaining: None,
385 checked_at: checked_at(),
386 error: None,
387 });
388 let p = payload(&[t], now());
389
390 let n = node(&p, "target:mnw");
391 assert!(n.conditions.iter().all(|c| c.condition_type != "whois"));
392 assert_eq!(n.status, Status::Ok);
393 }
394
395 #[test]
396 fn the_loudest_of_several_problems_wins_the_target() {
397 let mut t = healthy("mnw");
398 t.health = Some(HealthView {
399 status: HealthStatus::Degraded,
400 checked_at: checked_at(),
401 version: Some("1.4.0".into()),
402 error: Some("unexpected status 503".into()),
403 });
404 t.tls = Some(TlsView {
405 valid: true,
406 days_remaining: -1,
407 checked_at: checked_at(),
408 error: None,
409 webpki_trusted: Some(true),
410 platform_trusted: Some(true),
411 platform_error: None,
412 });
413 let p = payload(&[t], now());
414
415 // health is degraded, tls is failed: the target is failed.
416 assert_eq!(node(&p, "target:mnw").status, Status::Failed);
417 }
418
419 #[test]
420 fn one_targets_failure_does_not_touch_another() {
421 let mut down = healthy("mt");
422 down.health = Some(HealthView {
423 status: HealthStatus::Error,
424 checked_at: checked_at(),
425 version: None,
426 error: Some("500 Internal Server Error".into()),
427 });
428 let p = payload(&[healthy("mnw"), down], now());
429
430 assert_eq!(node(&p, "target:mnw").status, Status::Ok);
431 assert_eq!(node(&p, "target:mt").status, Status::Failed);
432 assert_eq!(p.worst_status(), Status::Failed);
433 assert_eq!(p.validate(), Ok(()));
434 }
435
436 #[test]
437 fn an_unknown_incident_status_stays_legible() {
438 let mut t = healthy("mnw");
439 t.incident = Some(IncidentView {
440 from_status: "operational".into(),
441 to_status: "sideways".into(),
442 started_at: checked_at(),
443 });
444 let p = payload(&[t], now());
445
446 let incident = node(&p, "target:mnw")
447 .conditions
448 .iter()
449 .find(|c| c.condition_type == "incident")
450 .unwrap();
451 assert_eq!(incident.status, Status::Unknown);
452 }
453
454 #[test]
455 fn a_malformed_timestamp_costs_only_that_timestamp() {
456 let mut t = healthy("mnw");
457 t.health = Some(HealthView {
458 status: HealthStatus::Operational,
459 checked_at: "not a timestamp".into(),
460 version: Some("1.4.0".into()),
461 error: None,
462 });
463 let p = payload(&[t], now());
464
465 let n = node(&p, "target:mnw");
466 assert_eq!(n.status, Status::Ok);
467 assert_eq!(n.conditions[0].since, None);
468 assert!(n.fields.iter().all(|f| f.label != "checked"));
469 }
470
471 #[test]
472 fn a_stale_backup_degrades_the_target_and_names_the_database() {
473 // The 40-day-stale backup that stayed green by every check that existed.
474 let mut t = healthy("mnw");
475 t.backups = vec![BackupView {
476 database: "makenotwork".into(),
477 status: "stale".into(),
478 age_hours: Some(960),
479 checked_at: checked_at(),
480 error: None,
481 }];
482 let p = payload(&[t], now());
483
484 let n = node(&p, "target:mnw");
485 assert_eq!(n.status, Status::Degraded);
486 let backup = n
487 .conditions
488 .iter()
489 .find(|c| c.condition_type == "backup:makenotwork")
490 .unwrap();
491 assert_eq!(backup.status, Status::Degraded);
492 assert!(backup.detail.as_deref().unwrap().contains("960h"));
493 }
494
495 #[test]
496 fn a_missing_backup_fails_the_target() {
497 let mut t = healthy("mnw");
498 t.backups = vec![BackupView {
499 database: "makenotwork".into(),
500 status: "missing".into(),
501 age_hours: None,
502 checked_at: checked_at(),
503 error: None,
504 }];
505 let p = payload(&[t], now());
506 assert_eq!(node(&p, "target:mnw").status, Status::Failed);
507 }
508
509 #[test]
510 fn several_databases_read_as_distinct_conditions() {
511 let mut t = healthy("mnw");
512 t.backups = vec![
513 BackupView {
514 database: "makenotwork".into(),
515 status: "ok".into(),
516 age_hours: Some(6),
517 checked_at: checked_at(),
518 error: None,
519 },
520 BackupView {
521 database: "multithreaded".into(),
522 status: "ok".into(),
523 age_hours: Some(7),
524 checked_at: checked_at(),
525 error: None,
526 },
527 ];
528 let p = payload(&[t], now());
529
530 let n = node(&p, "target:mnw");
531 assert_eq!(n.status, Status::Ok);
532 assert!(
533 n.conditions
534 .iter()
535 .any(|c| c.condition_type == "backup:makenotwork")
536 );
537 assert!(
538 n.conditions
539 .iter()
540 .any(|c| c.condition_type == "backup:multithreaded")
541 );
542 }
543
544 #[test]
545 fn a_degraded_scan_pipeline_carries_its_issues() {
546 let mut t = healthy("mnw");
547 t.scan_pipeline = Some(ScanView {
548 status: "degraded".into(),
549 issues: vec!["thumbnail error rate 22%".into(), "queue stuck: 4".into()],
550 checked_at: checked_at(),
551 error: None,
552 });
553 let p = payload(&[t], now());
554
555 let n = node(&p, "target:mnw");
556 assert_eq!(n.status, Status::Degraded);
557 let scan = n
558 .conditions
559 .iter()
560 .find(|c| c.condition_type == "scan_pipeline")
561 .unwrap();
562 assert!(
563 scan.detail
564 .as_deref()
565 .unwrap()
566 .contains("thumbnail error rate 22%")
567 );
568 assert!(scan.detail.as_deref().unwrap().contains("queue stuck: 4"));
569 }
570
571 #[test]
572 fn an_unreachable_scan_pipeline_fails_the_target() {
573 let mut t = healthy("mnw");
574 t.scan_pipeline = Some(ScanView {
575 status: "unreachable".into(),
576 issues: Vec::new(),
577 checked_at: checked_at(),
578 error: Some("502 Bad Gateway".into()),
579 });
580 let p = payload(&[t], now());
581
582 let n = node(&p, "target:mnw");
583 assert_eq!(n.status, Status::Failed);
584 let scan = n
585 .conditions
586 .iter()
587 .find(|c| c.condition_type == "scan_pipeline")
588 .unwrap();
589 assert!(scan.detail.as_deref().unwrap().contains("502 Bad Gateway"));
590 }
591
592 fn fleet_view(versions: Vec<(Option<&str>, i64)>) -> SyncKitFleetView {
593 SyncKitFleetView {
594 devices: versions.iter().map(|(_, d)| d).sum(),
595 window_days: 30,
596 versions: versions
597 .into_iter()
598 .map(|(v, d)| (v.map(str::to_string), d))
599 .collect(),
600 checked_at: checked_at(),
601 error: None,
602 }
603 }
604
605 #[test]
606 fn a_fleet_readout_reports_the_distribution_without_degrading() {
607 let mut t = healthy("mnw");
608 t.synckit_fleet = Some(fleet_view(vec![(Some("0.6.0"), 12), (None, 3)]));
609 let p = payload(&[t], now());
610
611 let n = node(&p, "target:mnw");
612 assert_eq!(n.status, Status::Ok);
613 let fleet = n
614 .conditions
615 .iter()
616 .find(|c| c.condition_type == "synckit_fleet")
617 .unwrap();
618 assert_eq!(fleet.status, Status::Ok);
619 let detail = fleet.detail.as_deref().unwrap();
620 assert!(detail.contains("15 devices in 30d"), "got {detail}");
621 assert!(detail.contains("0.6.0 x12"), "got {detail}");
622 assert!(detail.contains("unknown x3"), "got {detail}");
623 }
624
625 #[test]
626 fn an_ancient_version_in_the_field_does_not_degrade_the_target() {
627 // The whole design call: version age is a fact, not an incident. A fleet
628 // entirely on a year-old SDK must still read green.
629 let mut t = healthy("mnw");
630 t.synckit_fleet = Some(fleet_view(vec![(Some("0.1.0"), 200)]));
631 let p = payload(&[t], now());
632
633 let n = node(&p, "target:mnw");
634 assert_eq!(n.status, Status::Ok);
635 }
636
637 #[test]
638 fn an_empty_fleet_is_ok_and_says_so() {
639 let mut t = healthy("mnw");
640 t.synckit_fleet = Some(fleet_view(vec![]));
641 let p = payload(&[t], now());
642
643 let n = node(&p, "target:mnw");
644 assert_eq!(n.status, Status::Ok);
645 let fleet = n
646 .conditions
647 .iter()
648 .find(|c| c.condition_type == "synckit_fleet")
649 .unwrap();
650 assert!(
651 fleet
652 .detail
653 .as_deref()
654 .unwrap()
655 .contains("no devices synced in 30d")
656 );
657 }
658
659 #[test]
660 fn an_unavailable_fleet_readout_degrades_but_never_fails() {
661 let mut t = healthy("mnw");
662 let mut fleet = fleet_view(vec![]);
663 fleet.error = Some("HTTP 401 (alerts ingest token rejected)".into());
664 t.synckit_fleet = Some(fleet);
665 let p = payload(&[t], now());
666
667 let n = node(&p, "target:mnw");
668 assert_eq!(
669 n.status,
670 Status::Degraded,
671 "a readout PoM cannot take is yellow, not red: health owns whether MNW is up"
672 );
673 let fleet = n
674 .conditions
675 .iter()
676 .find(|c| c.condition_type == "synckit_fleet")
677 .unwrap();
678 assert!(fleet.detail.as_deref().unwrap().contains("401"));
679 }
680
681 #[test]
682 fn a_successful_readout_becomes_target_fields() {
683 let mut t = healthy("mnw");
684 t.synckit_fleet = Some(fleet_view(vec![(Some("0.6.0"), 12)]));
685 let p = payload(&[t], now());
686
687 let n = node(&p, "target:mnw");
688 assert!(n.fields.iter().any(|f| f.label == "synckit fleet"));
689 assert!(n.fields.iter().any(|f| f.label == "synckit devices"));
690 }
691
692 #[test]
693 fn an_unavailable_readout_contributes_no_fields() {
694 // A blank column beats a field reading "0 devices" that is really "PoM
695 // could not ask".
696 let mut t = healthy("mnw");
697 let mut fleet = fleet_view(vec![]);
698 fleet.error = Some("request: timed out".into());
699 t.synckit_fleet = Some(fleet);
700 let p = payload(&[t], now());
701
702 let n = node(&p, "target:mnw");
703 assert!(!n.fields.iter().any(|f| f.label.starts_with("synckit")));
704 }
705
706 fn tests_view(passed: bool, stale: bool) -> TestsView {
707 TestsView {
708 ran: true,
709 passed,
710 total_passed: Some(226),
711 total_failed: if passed { Some(0) } else { Some(3) },
712 started_at: Some(checked_at()),
713 stale,
714 stale_reason: if stale {
715 Some("tests are 12 days old (threshold: 7d)".into())
716 } else {
717 None
718 },
719 }
720 }
721
722 fn condition<'a>(n: &'a Node, ty: &str) -> &'a Condition {
723 n.conditions
724 .iter()
725 .find(|c| c.condition_type == ty)
726 .unwrap_or_else(|| panic!("no {ty} condition"))
727 }
728
729 #[test]
730 fn a_never_run_test_target_is_pending_not_healthy() {
731 let mut t = healthy("mnw");
732 t.tests = Some(TestsView {
733 ran: false,
734 passed: false,
735 total_passed: None,
736 total_failed: None,
737 started_at: None,
738 stale: true,
739 stale_reason: Some("no tests have been run".into()),
740 });
741 let p = payload(&[t], now());
742 let n = node(&p, "target:mnw");
743 // Pending is quieter than degraded: no evidence is not a failure.
744 assert_eq!(n.status, Status::Pending);
745 assert_eq!(
746 condition(n, "tests").detail.as_deref(),
747 Some("no tests have been run yet")
748 );
749 }
750
751 #[test]
752 fn a_failing_test_run_degrades_but_does_not_fail_the_target() {
753 // A red suite is a regression signal; the running service is health's job.
754 let mut t = healthy("mnw");
755 t.tests = Some(tests_view(false, false));
756 let p = payload(&[t], now());
757 let n = node(&p, "target:mnw");
758 assert_eq!(n.status, Status::Degraded);
759 let c = condition(n, "tests");
760 assert_eq!(c.status, Status::Degraded);
761 assert!(c.detail.as_deref().unwrap().contains("3 failed"));
762 }
763
764 #[test]
765 fn a_stale_but_passing_test_run_degrades_the_target() {
766 let mut t = healthy("mnw");
767 t.tests = Some(tests_view(true, true));
768 let p = payload(&[t], now());
769 let n = node(&p, "target:mnw");
770 assert_eq!(n.status, Status::Degraded);
771 assert!(
772 condition(n, "tests")
773 .detail
774 .as_deref()
775 .unwrap()
776 .contains("12 days old")
777 );
778 }
779
780 #[test]
781 fn a_fresh_passing_test_run_leaves_the_target_ok() {
782 let mut t = healthy("mnw");
783 t.tests = Some(tests_view(true, false));
784 let p = payload(&[t], now());
785 let n = node(&p, "target:mnw");
786 assert_eq!(n.status, Status::Ok);
787 assert_eq!(condition(n, "tests").status, Status::Ok);
788 }
789
790 #[test]
791 fn a_dns_mismatch_degrades_the_target_and_names_the_record() {
792 let mut t = healthy("mnw");
793 t.dns = Some(DnsView {
794 records: vec![
795 DnsRecordView {
796 name: "makenot.work".into(),
797 record_type: "A".into(),
798 matches: true,
799 error: None,
800 },
801 DnsRecordView {
802 name: "makenot.work".into(),
803 record_type: "MX".into(),
804 matches: false,
805 error: None,
806 },
807 ],
808 checked_at: Some(checked_at()),
809 });
810 let p = payload(&[t], now());
811 let n = node(&p, "target:mnw");
812 assert_eq!(n.status, Status::Degraded);
813 let c = condition(n, "dns");
814 assert_eq!(c.status, Status::Degraded);
815 assert!(c.detail.as_deref().unwrap().contains("MX does not match"));
816 }
817
818 #[test]
819 fn a_dns_lookup_error_reads_as_a_flaky_degrade_not_a_mismatch() {
820 let mut t = healthy("mnw");
821 t.dns = Some(DnsView {
822 records: vec![DnsRecordView {
823 name: "makenot.work".into(),
824 record_type: "TXT".into(),
825 matches: false,
826 error: Some("SERVFAIL".into()),
827 }],
828 checked_at: Some(checked_at()),
829 });
830 let p = payload(&[t], now());
831 let c = condition(node(&p, "target:mnw"), "dns");
832 assert_eq!(c.status, Status::Degraded);
833 assert!(
834 c.detail
835 .as_deref()
836 .unwrap()
837 .contains("lookup failed: SERVFAIL")
838 );
839 }
840
841 #[test]
842 fn all_dns_records_matching_stays_ok_and_counts_them() {
843 let mut t = healthy("mnw");
844 t.dns = Some(DnsView {
845 records: vec![DnsRecordView {
846 name: "makenot.work".into(),
847 record_type: "A".into(),
848 matches: true,
849 error: None,
850 }],
851 checked_at: Some(checked_at()),
852 });
853 let p = payload(&[t], now());
854 let n = node(&p, "target:mnw");
855 assert_eq!(n.status, Status::Ok);
856 // Singular, not "1 records match".
857 assert_eq!(
858 condition(n, "dns").detail.as_deref(),
859 Some("1 record match")
860 );
861 }
862
863 #[test]
864 fn a_cors_misconfiguration_degrades_the_target() {
865 let mut t = healthy("mnw");
866 t.cors = Some(CorsView {
867 checks: vec![CorsCheckView {
868 url: "https://makenot.work/api".into(),
869 origin: "https://app.makenot.work".into(),
870 passes: false,
871 error: None,
872 }],
873 checked_at: Some(checked_at()),
874 });
875 let p = payload(&[t], now());
876 let n = node(&p, "target:mnw");
877 assert_eq!(n.status, Status::Degraded);
878 assert!(
879 condition(n, "cors")
880 .detail
881 .as_deref()
882 .unwrap()
883 .contains("does not allow https://app.makenot.work")
884 );
885 }
886
887 #[test]
888 fn passing_cors_preflights_stay_ok() {
889 let mut t = healthy("mnw");
890 t.cors = Some(CorsView {
891 checks: vec![CorsCheckView {
892 url: "https://makenot.work/api".into(),
893 origin: "https://app.makenot.work".into(),
894 passes: true,
895 error: None,
896 }],
897 checked_at: Some(checked_at()),
898 });
899 let p = payload(&[t], now());
900 assert_eq!(node(&p, "target:mnw").status, Status::Ok);
901 }
902
903 #[test]
904 fn the_new_conditions_do_not_disturb_a_target_that_has_none_of_them() {
905 // A target without test/dns/cors config emits none of the three, exactly
906 // as before, the additive property the shared contract exists to give.
907 let p = payload(&[healthy("mnw")], now());
908 let n = node(&p, "target:mnw");
909 assert!(n.conditions.iter().all(|c| c.condition_type != "tests"));
910 assert!(n.conditions.iter().all(|c| c.condition_type != "dns"));
911 assert!(n.conditions.iter().all(|c| c.condition_type != "cors"));
912 assert_eq!(p.validate(), Ok(()));
913 }
914
915 #[test]
916 fn a_target_that_does_not_watch_health_says_nothing_about_health() {
917 // The test-only targets (the desktop apps, the SDK) have no HTTP endpoint
918 // and never will. Emitting `health: pending` for them is a row that can
919 // never go green, which held af/bb/go/sk pending forever and the whole
920 // source with them.
921 let mut t = healthy("af");
922 t.health_configured = false;
923 t.health = None;
924 let p = payload(&[t], now());
925 let n = node(&p, "target:af");
926 assert!(
927 n.conditions.iter().all(|c| c.condition_type != "health"),
928 "unconfigured health must emit no condition, got {:?}",
929 n.conditions
930 );
931 assert_eq!(p.validate(), Ok(()));
932 }
933
934 #[test]
935 fn a_stored_snapshot_still_reports_after_the_config_is_removed() {
936 // Config is truth for whether to watch, but evidence already collected
937 // should not vanish: dropping the block should not silently erase the
938 // last thing PoM knew about that target's health.
939 let mut t = healthy("mnw");
940 t.health_configured = false;
941 let p = payload(&[t], now());
942 let n = node(&p, "target:mnw");
943 assert!(n.conditions.iter().any(|c| c.condition_type == "health"));
944 assert_eq!(p.validate(), Ok(()));
945 }
946
947 #[test]
948 fn a_watched_target_with_no_snapshot_yet_is_still_pending() {
949 // The case the flag must not break: health IS configured, no check has
950 // run, so "evidence of nothing" is the honest answer.
951 let mut t = healthy("mnw");
952 t.health = None;
953 let p = payload(&[t], now());
954 let n = node(&p, "target:mnw");
955 let h = n
956 .conditions
957 .iter()
958 .find(|c| c.condition_type == "health")
959 .expect("configured health must still emit a condition");
960 assert_eq!(h.status, Status::Pending);
961 assert_eq!(p.validate(), Ok(()));
962 }
963
964 #[test]
965 fn render_is_a_pure_function_of_state_and_clock() {
966 let a = payload(&[healthy("mnw")], now());
967 let b = payload(&[healthy("mnw")], now());
968 assert_eq!(
969 serde_json::to_value(&a).unwrap(),
970 serde_json::to_value(&b).unwrap()
971 );
972 }
973
974 #[test]
975 fn pom_declares_no_actions() {
976 let p = payload(&[healthy("mnw")], now());
977 assert!(p.actions.is_empty());
978 assert!(node(&p, "target:mnw").actions.is_empty());
979 }
980