use std::fs;
use std::path::{Path, PathBuf};
use std::time::Duration;
use super::*;
fn root() -> PathBuf {
Path::new(env!("CARGO_MANIFEST_DIR")).join("../..")
}
fn read(relative: &str) -> String {
let path = root().join(relative);
fs::read_to_string(&path)
.unwrap_or_else(|error| panic!("{} is readable: {error}", path.display()))
}
const DASHBOARDS: &[&str] = &[
"ops/observability/dashboards/axond-fleet.json",
"ops/observability/dashboards/axond-tenancy.json",
];
const RULES: &str = "ops/observability/alerts/axond-alerts.yml";
const RUNBOOK: &str = "docs/operations/observability-runbook.md";
const COLLECTOR: &str = "ops/observability/otel-collector.yaml";
fn anchors() -> BTreeSet<String> {
let anchors = runbook_anchors(&read(RUNBOOK));
assert!(
anchors.len() >= 12,
"the runbook documents {} failure modes, which is fewer than the operational surface it covers",
anchors.len()
);
anchors
}
#[test]
fn the_catalogue_is_self_consistent() {
assert_eq!(catalog::validate_catalog(), Vec::new());
}
#[test]
fn every_shipped_dashboard_references_only_catalogued_metrics() {
let anchors = anchors();
for dashboard in DASHBOARDS {
let failures = validate_dashboard(dashboard, &read(dashboard), &anchors);
assert_eq!(
failures,
Vec::new(),
"{dashboard} drifted from the catalogue"
);
}
}
#[test]
fn every_shipped_alert_rule_references_only_catalogued_metrics() {
let anchors = anchors();
let (failures, _) = validate_rules(RULES, &read(RULES), &anchors);
assert_eq!(failures, Vec::new(), "{RULES} drifted from the catalogue");
}
#[test]
fn every_documented_failure_mode_has_an_alert_and_every_alert_has_a_runbook() {
let anchors = anchors();
let (failures, covered) = validate_rules(RULES, &read(RULES), &anchors);
assert_eq!(failures, Vec::new());
assert_eq!(uncovered_failure_modes(&anchors, &covered), Vec::new());
}
#[test]
fn the_shipped_pipeline_lets_the_stall_rule_see_a_gap() {
fn minutes(duration: &str, source: &str) -> u64 {
duration
.strip_suffix('m')
.and_then(|value| value.parse().ok())
.unwrap_or_else(|| panic!("{source} states a duration in whole minutes: `{duration}`"))
}
let rules = read(RULES);
let lookback = rules
.split_once("absent_over_time(axond_status_refreshes[")
.and_then(|(_, rest)| rest.split_once(']'))
.map(|(window, _)| window)
.expect("AxondStatusRefresherStalled watches for the absence of the refresh counter");
let collector = read(COLLECTOR);
let expiration = collector
.lines()
.find_map(|line| line.trim().strip_prefix("metric_expiration:"))
.map(str::trim)
.expect("the shipped pipeline states the expiration the rule depends on");
assert!(
minutes(expiration, COLLECTOR) < minutes(lookback, RULES),
"the exporter holds a series for {expiration} while the stall rule looks back {lookback}, \
so the series never lapses and the rule can never fire"
);
}
#[test]
fn the_derived_cadence_cannot_outrun_the_pipeline_that_watches_it() {
fn minutes(duration: &str, source: &str) -> u64 {
duration
.strip_suffix('m')
.and_then(|value| value.parse().ok())
.unwrap_or_else(|| panic!("{source} states a duration in whole minutes: `{duration}`"))
}
let collector = read(COLLECTOR);
let expiration = collector
.lines()
.find_map(|line| line.trim().strip_prefix("metric_expiration:"))
.map(str::trim)
.expect("the shipped pipeline states the expiration the rule depends on");
let rules = read(RULES);
let lookback = rules
.split_once("absent_over_time(axond_status_refreshes[")
.and_then(|(_, rest)| rest.split_once(']'))
.map(|(window, _)| window)
.expect("AxondStatusRefresherStalled watches for the absence of the refresh counter");
let cap = crate::status::probes::MAX_REFRESH_INTERVAL;
for (window, source) in [(expiration, COLLECTOR), (lookback, RULES)] {
assert!(
cap < Duration::from_secs(minutes(window, source) * 60),
"a refresher may be paced every {cap:?} while {source} works in {window} windows, so a \
healthy replica's series lapses and the stall rule pages"
);
}
assert!(
crate::status::probes::MAX_PROBE_TIMEOUT
< Duration::from_secs(minutes(expiration, COLLECTOR) * 60),
"a queue-aware probe may wait {:?} while the exporter retains samples for {expiration}, \
so a deep queue would make a live refresher look stalled",
crate::status::probes::MAX_PROBE_TIMEOUT
);
let stale_expression = rules
.lines()
.find(|line| {
line.contains(
"axond_status_observation_age{axond_status_component=\\\"control_plane\\\"} > ",
)
})
.expect("AxondStatusObservationsStale is scoped to the control plane");
let stale_threshold: u64 = stale_expression
.split_once("} > ")
.and_then(|(_, rest)| rest.split_once('"'))
.and_then(|(threshold, _)| threshold.trim().parse().ok())
.expect("AxondStatusObservationsStale compares the control-plane age against a millisecond threshold");
assert!(
cap < Duration::from_millis(stale_threshold),
"a control-plane round may take {cap:?} while AxondStatusObservationsStale calls {stale_threshold}ms \
stale, so a deployment paced at the cap pages while observing normally"
);
assert!(
crate::status::probes::MAX_STALENESS_BUDGET <= Duration::from_millis(stale_threshold),
"the control-plane registry keeps an observation usable for \
{:?} while the rule pages at {stale_threshold}ms",
crate::status::probes::MAX_STALENESS_BUDGET
);
}
#[test]
fn dashboard_drill_downs_stay_within_the_configured_dimensions() {
let tenancy: Value = serde_json::from_str(&read(DASHBOARDS[1])).expect("valid JSON");
let variables = tenancy["templating"]["list"]
.as_array()
.expect("the tenancy dashboard declares variables");
let names: Vec<&str> = variables
.iter()
.map(|variable| variable["name"].as_str().expect("a named variable"))
.collect();
assert_eq!(
names,
vec!["namespace", "alias", "provider", "target_model"]
);
for variable in variables {
let definition = variable["definition"].as_str().expect("a definition");
assert!(
definition.starts_with("label_values("),
"variable `{definition}` is not a label query"
);
assert_eq!(
validate_drill_down("tenancy", variable["name"].as_str().unwrap(), definition),
Vec::new()
);
}
}
#[test]
fn a_variable_whose_executed_query_differs_from_its_definition_is_refused() {
let dashboard = |variable: serde_json::Value| {
serde_json::json!({
"__inputs": [{"name": "DS_PROMETHEUS", "pluginId": "prometheus"}],
"uid": "test",
"title": "test",
"schemaVersion": 39,
"links": [{"url": RUNBOOK_URL}],
"templating": {"list": [variable]},
"panels": [{
"type": "timeseries",
"title": "panel",
"targets": [{"expr": "sum(rate(axond_request_count[5m]))"}],
}],
})
.to_string()
};
let bounded = "label_values(axond_request_count, axond_namespace)";
let smuggled = "label_values(axond_request_count, axond_status)";
let failures = validate_dashboard(
"drift",
&dashboard(serde_json::json!({
"name": "namespace",
"definition": bounded,
"query": {"query": smuggled, "refId": "StandardVariableQuery"},
})),
&BTreeSet::new(),
);
let text: Vec<String> = failures.iter().map(ToString::to_string).collect();
assert!(
text.iter().any(|failure| failure.contains("but displays")),
"{text:?}"
);
assert!(
failures.iter().any(|failure| matches!(
failure,
AssetError::UnboundedDrillDown { label, .. } if label == "axond.status"
)),
"the executed query's drill-down was never checked: {failures:?}"
);
for query in [
serde_json::json!({"query": bounded, "refId": "StandardVariableQuery"}),
serde_json::json!(bounded),
] {
assert_eq!(
validate_dashboard(
"drift",
&dashboard(serde_json::json!({
"name": "namespace",
"definition": bounded,
"query": query,
})),
&BTreeSet::new(),
),
Vec::new()
);
}
let failures = validate_dashboard(
"drift",
&dashboard(serde_json::json!({"name": "namespace", "definition": bounded})),
&BTreeSet::new(),
);
assert!(
failures
.iter()
.any(|failure| failure.to_string().contains("no `query`")),
"{failures:?}"
);
}
#[test]
fn an_asset_cannot_select_on_an_identity_dimension() {
for (key, _) in catalog::FORBIDDEN_LABEL_KEYS {
let prometheus = key.replace('.', "_");
let expr = format!("sum(rate(axond_request_count{{{prometheus}=\"x\"}}[5m]))");
let failures = validate_expression("hostile", &expr);
assert!(
failures.iter().any(|failure| matches!(
failure,
AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { .. },
..
}
)),
"selecting on `{key}` was accepted"
);
}
}
#[test]
fn the_rule_file_has_the_shape_prometheus_expects() {
let document = parse_yaml(&read(RULES)).expect("the rule file parses");
let groups = document
.get("groups")
.and_then(Yaml::as_sequence)
.expect("groups");
assert!(groups.len() >= 5, "rules are grouped by concern");
for group in groups {
assert!(group.get("name").and_then(Yaml::as_str).is_some());
assert!(
group
.get("interval")
.and_then(Yaml::as_str)
.is_some_and(|interval| interval.ends_with('s') || interval.ends_with('m')),
"every group paces itself"
);
for rule in group
.get("rules")
.and_then(Yaml::as_sequence)
.expect("rules")
{
let alert = rule.get("alert").and_then(Yaml::as_str).expect("an alert");
assert!(
alert.starts_with("Axond"),
"`{alert}` does not carry the product prefix a shared Alertmanager needs"
);
}
}
}
fn dashboard_with(expr: &str) -> String {
serde_json::json!({
"__inputs": [{"name": "DS_PROMETHEUS", "pluginId": "prometheus"}],
"uid": "test",
"title": "test",
"schemaVersion": 39,
"links": [{"url": RUNBOOK_URL}],
"panels": [{
"type": "timeseries",
"title": "panel",
"targets": [{"expr": expr}],
}],
})
.to_string()
}
fn dashboard_failures(expr: &str) -> Vec<AssetError> {
validate_dashboard("drift", &dashboard_with(expr), &BTreeSet::new())
}
#[test]
fn a_renamed_metric_is_refused() {
let failures = dashboard_failures("sum(rate(axond_request_counts[5m]))");
assert!(matches!(
failures.as_slice(),
[AssetError::UnknownFamily { name, .. }] if name == "axond_request_counts"
));
}
#[test]
fn the_suffixed_spelling_of_a_counter_is_refused() {
let failures = dashboard_failures("sum(rate(axond_request_count_total[5m]))");
assert!(matches!(
failures.as_slice(),
[AssetError::UnknownFamily { name, .. }] if name == "axond_request_count_total"
));
}
#[test]
fn a_histogram_is_selected_through_its_families() {
assert_eq!(
dashboard_failures(
"histogram_quantile(0.95, sum by (le) (rate(axond_request_duration_bucket[10m])))"
),
Vec::new()
);
assert_eq!(
dashboard_failures("sum(rate(axond_request_duration_count[10m]))"),
Vec::new()
);
assert!(matches!(
dashboard_failures("sum(rate(axond_request_duration[10m]))").as_slice(),
[AssetError::UnknownFamily { .. }]
));
}
#[test]
fn a_label_the_instrument_does_not_declare_is_refused() {
let failures =
dashboard_failures("max(axond_status_component_state{axond_namespace=\"acme\"})");
assert!(matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { metric, key },
..
}] if metric == "axond.status.component_state" && key == "axond_namespace"
));
}
#[test]
fn a_value_outside_a_closed_vocabulary_is_refused() {
let failures =
dashboard_failures("max(axond_status_component_state{axond_status_component=\"kafka\"})");
assert!(matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UnknownLabelValue { value, .. },
..
}] if value == "kafka"
));
assert_eq!(
dashboard_failures(
"max(axond_status_component_state{axond_status_component=\"budget_store\"})"
),
Vec::new()
);
}
#[test]
fn a_matcher_against_a_dashboard_variable_is_accepted() {
assert_eq!(
dashboard_failures("sum(rate(axond_request_count{axond_namespace=~\"$namespace\"}[5m]))"),
Vec::new()
);
}
#[test]
fn grouping_by_a_label_the_instrument_does_not_declare_is_refused() {
let failures =
dashboard_failures("sum by (axond_namespace) (rate(axond_status_refreshes[5m]))");
assert!(matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { key, .. },
..
}] if key == "axond_namespace"
));
assert_eq!(
dashboard_failures("sum by (axond_status_component) (rate(axond_status_refreshes[5m]))"),
Vec::new()
);
}
#[test]
fn a_compound_expression_is_judged_arm_by_arm() {
let failures = dashboard_failures(
"sum by (axond_namespace) (rate(axond_request_count[5m])) / sum by (axond_namespace) (rate(axond_status_refreshes[5m]))",
);
assert!(
matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { metric, key },
..
}] if metric == "axond.status.refreshes" && key == "axond_namespace"
),
"{failures:?}"
);
assert_eq!(
dashboard_failures(
"sum by (axond_namespace) (rate(axond_request_count[5m])) / sum(rate(axond_status_refreshes[5m]))"
),
Vec::new()
);
}
#[test]
fn grouping_over_series_we_do_not_catalogue_is_not_drift() {
assert!(matches!(
dashboard_failures("sum by (namespace) (job:axond_requests:rate5m) or vector(0)")
.as_slice(),
[AssetError::NoMetricReference { .. }]
));
assert_eq!(
dashboard_failures(
"sum by (axond_status_component) (rate(axond_status_refreshes[5m])) or on() vector(0)"
),
Vec::new()
);
}
#[test]
fn a_panel_that_queries_nothing_of_ours_is_refused() {
assert!(matches!(
dashboard_failures("vector(1)").as_slice(),
[AssetError::NoMetricReference { .. }]
));
}
#[test]
fn a_dashboard_that_hard_codes_its_datasource_is_refused() {
let source = serde_json::json!({
"uid": "test",
"title": "test",
"schemaVersion": 39,
"links": [{"url": RUNBOOK_URL}],
"panels": [],
})
.to_string();
let failures = validate_dashboard("pinned", &source, &BTreeSet::new());
assert!(
failures
.iter()
.any(|failure| failure.to_string().contains("not portable")),
"{failures:?}"
);
}
#[test]
fn an_unbounded_drill_down_is_refused() {
let failures = validate_drill_down(
"drift",
"component",
"label_values(axond_status_component_state, axond_status_component)",
);
assert!(matches!(
failures.as_slice(),
[AssetError::UnboundedDrillDown { label, class, .. }]
if label == "axond.status.component" && *class == "closed"
));
}
fn rules_with(body: &str) -> String {
format!(
"groups:\n - name: test\n interval: 30s\n rules:\n - alert: AxondTest\n{body}"
)
}
#[test]
fn an_alert_without_a_runbook_link_is_refused() {
let source = rules_with(
" expr: \"max(axond_revision_lag) > 1000\"\n for: 5m\n labels:\n severity: warning\n annotations:\n summary: \"s\"\n description: \"d\"\n",
);
let (failures, covered) = validate_rules("drift", &source, &BTreeSet::new());
assert!(covered.is_empty());
assert!(
failures
.iter()
.any(|failure| failure.to_string().contains("no `runbook_url`")),
"{failures:?}"
);
}
#[test]
fn an_alert_pointing_at_a_missing_runbook_section_is_refused() {
let source = rules_with(&format!(
" expr: \"max(axond_revision_lag) > 1000\"\n for: 5m\n labels:\n severity: warning\n annotations:\n summary: \"s\"\n description: \"d\"\n runbook_url: \"{RUNBOOK_URL}#a-section-nobody-wrote\"\n"
));
let (failures, _) = validate_rules("drift", &source, &BTreeSet::new());
assert!(matches!(
failures.as_slice(),
[AssetError::UnknownRunbookAnchor { anchor, .. }] if anchor == "a-section-nobody-wrote"
));
}
#[test]
fn an_alert_that_adds_two_counters_without_defaulting_them_is_refused() {
let source = rules_with(&format!(
" expr: \"sum(rate(axond_budget_capacity_denials[5m])) + sum(rate(axond_rate_limit_capacity_denials[5m])) > 0\"\n for: 5m\n labels:\n severity: warning\n annotations:\n summary: \"s\"\n description: \"d\"\n runbook_url: \"{RUNBOOK_URL}#a-dependency-is-impaired\"\n"
));
let anchors = BTreeSet::from(["a-dependency-is-impaired".to_owned()]);
let (failures, _) = validate_rules("drift", &source, &anchors);
assert!(
failures
.iter()
.any(|failure| failure.to_string().contains("or vector(0)")),
"{failures:?}"
);
for expr in [
"(sum(rate(axond_budget_capacity_denials[5m])) or vector(0)) + (sum(rate(axond_rate_limit_capacity_denials[5m])) or vector(0)) > 0",
"max(axond_revision_lag) > 1000",
] {
let source = rules_with(&format!(
" expr: \"{expr}\"\n for: 5m\n labels:\n severity: warning\n annotations:\n summary: \"s\"\n description: \"d\"\n runbook_url: \"{RUNBOOK_URL}#a-dependency-is-impaired\"\n"
));
let (failures, _) = validate_rules("drift", &source, &anchors);
assert_eq!(failures, Vec::new(), "{expr}");
}
}
#[test]
fn an_alert_without_a_hold_window_or_severity_is_refused() {
let source = rules_with(
" expr: \"max(axond_revision_lag) > 1000\"\n annotations:\n summary: \"s\"\n description: \"d\"\n",
);
let (failures, _) = validate_rules("drift", &source, &BTreeSet::new());
let text: Vec<String> = failures.iter().map(ToString::to_string).collect();
assert!(
text.iter().any(|failure| failure.contains("`for` window")),
"{text:?}"
);
assert!(
text.iter().any(|failure| failure.contains("no severity")),
"{text:?}"
);
}
#[test]
fn a_documented_failure_mode_with_no_rule_is_reported() {
let anchors = BTreeSet::from(["a-dependency-is-impaired".to_owned(), "orphan".to_owned()]);
let covered = BTreeSet::from(["a-dependency-is-impaired".to_owned()]);
assert!(matches!(
uncovered_failure_modes(&anchors, &covered).as_slice(),
[AssetError::UncoveredFailureMode { anchor }] if anchor == "orphan"
));
}
#[test]
fn the_lexer_separates_metrics_from_grouping_labels_and_functions() {
let found = selectors(
"histogram_quantile(0.95, sum by (le, axond_namespace) (rate(axond_request_duration_bucket{axond_status=\"ok\"}[10m]))) / 1e6",
)
.expect("lexes");
assert_eq!(
found
.selectors
.iter()
.map(|selector| selector.family.as_str())
.collect::<Vec<_>>(),
vec!["axond_request_duration_bucket"]
);
assert_eq!(
found
.grouping
.iter()
.map(|grouping| (grouping.label.as_str(), grouping.selectors.as_slice()))
.collect::<Vec<_>>(),
vec![("le", &[0usize][..]), ("axond_namespace", &[0usize][..])]
);
assert_eq!(
found.selectors[0].matchers,
vec![Matcher {
key: "axond_status".to_owned(),
literal: Some("ok".to_owned()),
}]
);
}
#[test]
fn a_metric_matched_against_another_vector_is_still_validated() {
assert_eq!(
dashboard_failures("axond_request_count or on() vector(0)"),
Vec::new()
);
assert_eq!(
dashboard_failures(
"axond_request_count / on(axond_namespace) group_left() axond_cost_microdollars"
),
Vec::new()
);
let failures = dashboard_failures("axond_request_counts or on() vector(0)");
assert!(
matches!(
failures.as_slice(),
[AssetError::UnknownFamily { name, .. }] if name == "axond_request_counts"
),
"{failures:?}"
);
for expr in [
"axond_status_refreshes / on(axond_namespace) axond_request_count",
"axond_status_refreshes / on(axond_namespace) group_left() axond_request_count",
] {
let failures = dashboard_failures(expr);
assert!(
matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { metric, key },
..
}] if metric == "axond.status.refreshes" && key == "axond_namespace"
),
"{expr}: {failures:?}"
);
}
}
#[test]
fn an_excluded_label_does_not_have_to_be_one_the_instrument_declares() {
for expr in [
"sum without (axond_namespace) (rate(axond_status_refreshes[5m]))",
"sum without (le) (rate(axond_request_duration_bucket[5m]))",
"axond_status_refreshes / ignoring(axond_namespace) axond_status_refreshes",
] {
assert_eq!(dashboard_failures(expr), Vec::new(), "{expr}");
}
let failures =
dashboard_failures("sum by (axond_namespace) (rate(axond_status_refreshes[5m]))");
assert!(
matches!(
failures.as_slice(),
[AssetError::Catalog {
source: catalog::CatalogError::UndeclaredLabel { metric, .. },
..
}] if metric == "axond.status.refreshes"
),
"{failures:?}"
);
}
#[test]
fn the_lexer_refuses_a_character_a_prometheus_name_cannot_contain() {
let message = selectors("sum(rate(axond_requést_count[5m]))").expect_err("refuses");
assert!(message.contains("unexpected character"), "{message}");
assert!(matches!(
dashboard_failures("sum(rate(axond_requést_count[5m]))").as_slice(),
[AssetError::Malformed { .. }]
));
}
#[test]
fn the_lexer_reads_every_matcher_operator() {
let found = selectors(
"axond_http_server_requests{http_response_status_code=~\"5..\", http_request_method!=\"GET\", http_route=\"/v1/models\"}",
)
.expect("lexes");
let matchers = &found.selectors[0].matchers;
assert_eq!(matchers.len(), 3);
assert_eq!(
matchers
.iter()
.filter(|matcher| matcher.literal.is_some())
.count(),
1
);
}
#[test]
fn runbook_anchors_come_only_from_the_failure_modes_section() {
let runbook = "# Title\n\n## Where to look\n\n### Not a failure mode\n\n## Failure modes\n\n### A dependency is impaired\n\n### The fleet is split across revisions\n\n## Bounded drill-down\n\n### Also not one\n";
assert_eq!(
runbook_anchors(runbook),
BTreeSet::from([
"a-dependency-is-impaired".to_owned(),
"the-fleet-is-split-across-revisions".to_owned(),
])
);
}
#[test]
fn the_yaml_subset_parses_nested_mappings_and_sequences() {
let document = parse_yaml(
"groups:\n - name: one\n rules:\n - alert: A\n labels:\n severity: warning\n - alert: B\n expr: \"a: b\"\n - name: two\n rules:\n - alert: C\n",
)
.expect("parses");
let groups = document.get("groups").and_then(Yaml::as_sequence).unwrap();
assert_eq!(groups.len(), 2);
let rules = groups[0].get("rules").and_then(Yaml::as_sequence).unwrap();
assert_eq!(rules.len(), 2);
assert_eq!(
rules[0]
.get("labels")
.and_then(|labels| labels.get("severity"))
.and_then(Yaml::as_str),
Some("warning")
);
assert_eq!(rules[1].get("expr").and_then(Yaml::as_str), Some("a: b"));
assert_eq!(
groups[1]
.get("rules")
.and_then(Yaml::as_sequence)
.map(<[Yaml]>::len),
Some(1)
);
}
#[test]
fn the_yaml_subset_refuses_what_it_does_not_implement() {
for source in [
"groups:\n\t- name: tabbed\n",
"base: &anchor\n name: one\n",
"expr: |\n multi\n line\n",
"groups:\n",
"not a mapping\n",
] {
assert!(
parse_yaml(source).is_err(),
"`{source}` was accepted by the subset parser"
);
}
}