use faultbox::{Adhoc, Capture, Config, EventKind};
mod storage {
use super::*;
pub fn commit(id: u64) {
faultbox::breadcrumb!(Info, "storage.commit", "committed", { "commit_id": id });
}
pub fn detect_corruption() -> Option<std::path::PathBuf> {
let ctx = Adhoc {
kind: "storage.checksum",
key: "class=checksum".to_owned(),
value: serde_json::json!({ "page": 828 }),
};
Capture::new(EventKind::Corruption, "checksum mismatch on read")
.domain(&ctx)
.emit()
}
}
mod database {
pub fn query() {
faultbox::breadcrumb!(Info, "database.query", "scanning", { "table": "users" });
}
pub fn propagate_without_reporting() {
faultbox::breadcrumb!(Warn, "database.read", "read failed, propagating");
}
}
#[test]
fn layers_share_one_recorder_one_trail_and_one_report_per_detection_site() {
let tmp = tempfile::tempdir().unwrap();
let reports = tmp.path().join("reports");
assert!(
faultbox::init(
Config::new("theapp", "0.1.0", &reports)
.install_panic_hook(false)
.features(["storage", "database"]),
),
"the first init wins"
);
assert!(
!faultbox::init(Config::new(
"storage",
"9.9.9",
tmp.path().join("elsewhere")
)),
"a second init is refused, not merged"
);
storage::commit(9557);
database::query();
storage::commit(9558);
database::propagate_without_reporting();
let dir = storage::detect_corruption().expect("the detection site reports");
let group = faultbox::reader::load(&dir).unwrap();
let all = faultbox::reader::list(&reports).unwrap();
assert_eq!(all.len(), 1, "one detection site, one report group");
assert_eq!(group.occurrences(), 1);
assert_eq!(group.first.meta.project, "theapp");
assert_eq!(
group.first.meta.version, "0.1.0",
"the losing init's version must not leak in"
);
assert_eq!(
group.first.domain_kind.as_deref(),
Some("storage.checksum"),
"the detecting layer is identified by its domain kind"
);
let trail: Vec<&str> = group
.first
.breadcrumbs
.iter()
.map(|c| c.category.as_str())
.collect();
assert_eq!(
trail,
[
"storage.commit",
"database.query",
"storage.commit",
"database.read",
],
"one interleaved trail across layers, each crumb exactly once"
);
assert!(
group
.first
.breadcrumbs
.iter()
.any(|c| c.category.starts_with("database.")),
"a lower layer's report must still see upper-layer breadcrumbs"
);
}