icydb-cli 0.296.10

Developer CLI tools for IcyDB
use super::{average, entity_row, render_metrics_report};
use icydb::metrics::{EntityMetrics, MetricsReport};

#[test]
fn metrics_report_displays_producer_overflow_quality() {
    let wire = icydb_schema::wire::encode_one(MetricsReport::default()).unwrap();
    let report: MetricsReport = icydb_schema::wire::decode_one(&wire).unwrap();
    let rendered = render_metrics_report(&report);
    assert_eq!(
        rendered
            .matches("sample overflow=false, total overflow=false")
            .count(),
        2
    );
    assert!(!rendered.contains("+ marks a clipped count or instruction total"));
    assert!(rendered.contains("None"));
}

fn decoded_entity(
    hits: u64,
    total: u128,
    mean: Option<u128>,
    samples_saturated: bool,
    total_saturated: bool,
) -> EntityMetrics {
    #[derive(serde::Serialize)]
    struct ReportEntity {
        path: &'static str,
        instructions: ReportInstructions,
    }
    #[derive(serde::Serialize)]
    struct ReportInstructions {
        samples: u64,
        total: u128,
        maximum: Option<u64>,
        samples_overflowed: bool,
        total_overflowed: bool,
    }
    let maximum = (hits != 0).then_some(u64::try_from(total).unwrap_or(u64::MAX));
    let instructions = ReportInstructions {
        samples: hits,
        total,
        maximum,
        samples_overflowed: samples_saturated,
        total_overflowed: total_saturated,
    };
    let encoded = icydb_schema::wire::encode_one(ReportEntity {
        path: "app.Entity",
        instructions,
    })
    .expect("encode report entity");
    let decoded: EntityMetrics =
        icydb_schema::wire::decode_one(&encoded).expect("decode report entity");
    assert_eq!(average(decoded.instructions()), mean);
    decoded
}

#[test]
fn metrics_entity_row_keeps_fields_and_rounds_average_down() {
    assert_eq!(
        entity_row(&decoded_entity(3, 10, Some(3), false, false)),
        ["app.Entity", "3", "10", "3", "10"].map(str::to_string),
    );
}

#[test]
fn metrics_average_distinguishes_empty_from_measured_zero() {
    assert_eq!(
        entity_row(&decoded_entity(0, 0, None, false, false))[3],
        "unavailable"
    );
    assert_eq!(
        entity_row(&decoded_entity(3, 0, Some(0), false, false))[3],
        "0"
    );
}

#[test]
fn metrics_average_preserves_exact_caps_and_producer_quality() {
    assert_eq!(
        entity_row(&decoded_entity(u64::MAX, 0, Some(0), false, false))[3],
        "0"
    );
    for (total, total_saturated) in [(0, false), (u128::MAX, true)] {
        let row = entity_row(&decoded_entity(
            u64::MAX,
            total,
            None,
            true,
            total_saturated,
        ));
        assert_eq!(row[1], format!("{}+", u64::MAX));
        assert_eq!(
            row[2],
            if total_saturated {
                format!("{total}+")
            } else {
                total.to_string()
            }
        );
        assert_eq!(row[3], "unavailable");
    }
    assert_eq!(
        entity_row(&decoded_entity(
            1,
            u128::from(u64::MAX),
            Some(u128::from(u64::MAX)),
            false,
            false
        ))[3],
        u64::MAX.to_string()
    );
}

#[test]
fn metrics_average_preserves_totals_above_the_individual_sample_limit() {
    let total = u128::from(u64::MAX) * 2;
    assert_eq!(
        entity_row(&decoded_entity(
            2,
            total,
            Some(u128::from(u64::MAX)),
            false,
            false
        )),
        [
            "app.Entity".to_string(),
            "2".to_string(),
            total.to_string(),
            u64::MAX.to_string(),
            u64::MAX.to_string(),
        ]
    );
}