canic-host 0.110.35

Host-side App build and desired-state Fleet reconciliation library for Canic workspaces
Documentation
use super::*;
use canic_core::dto::{page::Page, public_status::PublicMetric};

fn snapshot(family: PublicMetricFamily, names: &[&str]) -> PublicMetricsSnapshot {
    PublicMetricsSnapshot {
        family,
        state: PublicSnapshotState::Fresh,
        sampled_at_ns: Some(100),
        stale_after_ns: 500,
        truncated: false,
        metrics: Page {
            total: names.len() as u64,
            entries: names
                .iter()
                .map(|name| PublicMetric {
                    name: (*name).into(),
                    canister_id: None,
                    value: u128::MAX,
                    unit: "cycles".into(),
                    observed_at_ns: 90,
                    kind: PublicMetricKind::Counter {
                        window_id: 7,
                        saturated: true,
                    },
                })
                .collect(),
        },
    }
}

#[test]
fn grants_keep_precision_source_time_and_saturation_without_inventing_consumption() {
    let family = PublicMetricFamily::Operations;
    let mut reply = snapshot(
        family,
        &[
            "cycles_funding.cycles_granted_total",
            "cycles_funding.cycles_granted_to_child",
            "cycles_funding.cycles_requested_total",
            "timer.events.scheduler_started",
        ],
    );
    reply.state = PublicSnapshotState::Stale;
    reply.metrics.entries[1].canister_id = Some(candid::Principal::anonymous());
    let view = samples(reply, family).unwrap();
    assert_eq!(view.state, CostSampleState::Stale);
    assert_eq!(view.rows.len(), 3);
    assert_eq!(view.rows[0].value, u128::MAX.to_string());
    assert_eq!(view.rows[0].observed_at_ns, 90);
    assert_eq!(
        view.rows[0].measurement,
        CostMetricKind::Counter {
            window_id: 7,
            saturated: true
        }
    );
    assert_eq!(view.rows[1].canister_id.as_deref(), Some("2vxsx-fae"));
    assert_eq!(view.sampled_at_ns, Some(100));
}

#[test]
fn timer_measurements_keep_source_registration_and_both_phases() {
    let family = PublicMetricFamily::Performance;
    let mut reply = snapshot(
        family,
        &[
            "perf.timer.app.checkpoint.tick.scheduler",
            "perf.timer.app.checkpoint.tick.scheduler.calls",
            "perf.timer.app.checkpoint.tick.work",
            "perf.timer.app.checkpoint.tick.work.calls",
            "perf.endpoint.query.status",
        ],
    );
    for row in &mut reply.metrics.entries {
        row.kind = PublicMetricKind::TimerCounter {
            registration: canic_core::dto::public_status::TimerMetricRegistration {
                canister_version: 3,
                started_at_ns: 10,
                sequence: 7,
            },
            saturated: true,
        };
    }
    let view = samples(reply, family).unwrap();
    assert_eq!(view.rows.len(), 4);
    assert!(view.rows.iter().all(|row| row.measurement
        == CostMetricKind::TimerCounter {
            registration: TimerRegistrationView {
                canister_version: 3,
                started_at_ns: 10,
                sequence: 7
            },
            saturated: true,
        }));
    let json = serde_json::to_vec(&view).unwrap();
    assert_eq!(
        serde_json::from_slice::<CostSamplesView>(&json).unwrap(),
        view
    );
}

#[test]
fn pagination_and_source_truncation_survive_filtering() {
    let family = PublicMetricFamily::Operations;
    let mut reply = snapshot(family, &["unselected"]);
    reply.metrics.total = 2;
    let view = samples(reply, family).unwrap();
    assert!(view.truncated);
    assert!(view.rows.is_empty());
    let mut reply = snapshot(family, &[]);
    reply.truncated = true;
    assert!(samples(reply, family).unwrap().truncated);
}

#[test]
fn malformed_pages_reject_before_projection() {
    let family = PublicMetricFamily::Cycles;
    assert_eq!(
        samples(
            snapshot(family, &["balance"]),
            PublicMetricFamily::Performance
        ),
        Err(ObservationFailure::InvalidResponse)
    );
    assert_eq!(
        samples(snapshot(family, &["balance", "balance"]), family),
        Err(ObservationFailure::InvalidResponse)
    );
    let mut reply = snapshot(family, &["balance"]);
    reply.metrics.entries[0].observed_at_ns = 101;
    assert_eq!(
        samples(reply, family),
        Err(ObservationFailure::InvalidResponse)
    );
    let names = vec!["balance"; 257];
    assert_eq!(
        samples(snapshot(family, &names), family),
        Err(ObservationFailure::InvalidResponse)
    );
    let mut reply = snapshot(family, &["balance"]);
    reply.metrics.total = 0;
    assert_eq!(
        samples(reply, family),
        Err(ObservationFailure::InvalidResponse)
    );
}

#[test]
fn disabled_empty_family_remains_disabled() {
    let family = PublicMetricFamily::Cycles;
    let mut reply = snapshot(family, &[]);
    reply.state = PublicSnapshotState::Disabled;
    reply.sampled_at_ns = None;
    let view = samples(reply, family).unwrap();
    assert_eq!(view.state, CostSampleState::Disabled);
    assert_eq!(view.sampled_at_ns, None);
    assert!(view.rows.is_empty());
}