ic-query 0.10.0

Internet Computer query library for NNS, SNS, ICRC, and related public network metadata
Documentation
#[cfg(feature = "host")]
use crate::{
    duration::{display_duration_seconds, parse_duration_seconds},
    token_amount::e8s_decimal_text,
};
use crate::{
    table::{ColumnAlign, render_table},
    text_value::{optional_u64_text, sanitize_text, truncate_text, yes_no},
    token_amount::base_units_decimal_text,
    token_metadata_text::optional_text,
};

#[test]
fn base_units_render_as_two_decimal_token_amounts() {
    assert_eq!(base_units_decimal_text("0", 8), "0.00");
    assert_eq!(base_units_decimal_text("000000000", 8), "0.00");
    assert_eq!(base_units_decimal_text("10_000", 8), "0.00");
    assert_eq!(
        base_units_decimal_text("100_923_109_141_460", 8),
        "1009231.09"
    );
    assert_eq!(base_units_decimal_text("500000", 8), "0.01");
    assert_eq!(base_units_decimal_text("123456789", 8), "1.23");
    assert_eq!(base_units_decimal_text("123500000", 8), "1.24");
    assert_eq!(base_units_decimal_text("3000000000000", 8), "30000.00");
    assert_eq!(base_units_decimal_text("123", 0), "123.00");
    assert_eq!(base_units_decimal_text("123", 1), "12.30");
    assert_eq!(base_units_decimal_text("123", 2), "1.23");
    assert_eq!(base_units_decimal_text("999", 3), "1.00");
    assert_eq!(base_units_decimal_text("not-a-number", 8), "not-a-number");
}

#[test]
#[cfg(feature = "host")]
fn e8s_render_as_two_decimal_token_amounts() {
    assert_eq!(e8s_decimal_text(0), "0.00");
    assert_eq!(e8s_decimal_text(123), "0.00");
    assert_eq!(e8s_decimal_text(499_999), "0.00");
    assert_eq!(e8s_decimal_text(500_000), "0.01");
    assert_eq!(e8s_decimal_text(100_000_000), "1.00");
    assert_eq!(e8s_decimal_text(123_456_789), "1.23");
    assert_eq!(e8s_decimal_text(123_500_000), "1.24");
    assert_eq!(e8s_decimal_text(3_000_000_000_000), "30000.00");
}

#[test]
fn render_table_handles_long_left_aligned_cells() {
    let rows = [[
        "ICRC-1".to_string(),
        "https://github.com/dfinity/ICRC-1?with=a-long-token-metadata-url".to_string(),
    ]];

    let table = render_table(
        &["STANDARD", "URL"],
        &rows,
        &[ColumnAlign::Left, ColumnAlign::Left],
    );

    assert!(table.contains("ICRC-1"));
    assert!(table.contains("a-long-token-metadata-url"));
}

#[test]
fn render_table_right_aligns_cells() {
    let rows = [["1".to_string(), "Dragginz".to_string()]];

    let table = render_table(
        &["ID", "NAME"],
        &rows,
        &[ColumnAlign::Right, ColumnAlign::Left],
    );

    assert!(table.contains("ID   NAME"));
    assert!(table.contains(" 1   Dragginz"));
}

#[test]
fn render_table_aligns_wide_unicode_cells_by_terminal_width() {
    let rows = [
        ["東京".to_string(), "1".to_string()],
        ["Paris".to_string(), "2".to_string()],
    ];

    let table = render_table(
        &["CITY", "ID"],
        &rows,
        &[ColumnAlign::Left, ColumnAlign::Right],
    );

    assert_eq!(table, "CITY    ID\n-----   --\n東京     1\nParis    2");
}

#[test]
fn render_table_escapes_control_characters_in_values() {
    let rows = [["line one\nline two\u{1b}[31m".to_string()]];

    let table = render_table(&["VALUE"], &rows, &[ColumnAlign::Left]);

    assert!(table.contains("line one\\nline two\\u{1b}[31m"));
    assert_eq!(table.lines().count(), 3);
    assert!(!table.contains('\u{1b}'));
}

#[test]
fn scalar_text_helpers_escape_controls_without_changing_raw_models() {
    let optional = "line one\nline two\u{1b}[31m".to_string();

    assert_eq!(sanitize_text(&optional), "line one\\nline two\\u{1b}[31m");
    assert_eq!(optional_text(Some(&optional)), sanitize_text(&optional));
    assert_eq!(truncate_text("abcdef", 3), "abc...");
    assert_eq!(optional_u64_text(None), "-");
    assert_eq!(optional_u64_text(Some(42)), "42");
    assert_eq!(yes_no(true), "yes");
    assert_eq!(yes_no(false), "no");
    assert!(optional.contains('\n'));
    assert!(optional.contains('\u{1b}'));
}

#[test]
fn scalar_text_sanitizing_is_control_free_and_idempotent() {
    let ascii = (0..=127).filter_map(char::from_u32).collect::<String>();
    let sanitized = sanitize_text(&ascii);

    assert!(!sanitized.chars().any(char::is_control));
    assert_eq!(sanitize_text(&sanitized), sanitized);
    for limit in 0..=sanitized.chars().count() {
        assert!(!truncate_text(&ascii, limit).chars().any(char::is_control));
    }
}

#[test]
#[cfg(feature = "host")]
fn duration_display_uses_largest_readable_unit() {
    assert_eq!(display_duration_seconds(0), "0s");
    assert_eq!(display_duration_seconds(86_400), "1d");
    assert_eq!(display_duration_seconds(2_629_800), "30.44d");
    assert_eq!(display_duration_seconds(5_400), "1.50h");
    assert_eq!(display_duration_seconds(90), "1.50m");
    assert_eq!(display_duration_seconds(45), "45s");
}

#[test]
#[cfg(feature = "host")]
fn duration_parser_accepts_integer_units() {
    assert_eq!(parse_duration_seconds("45").expect("seconds"), 45);
    assert_eq!(parse_duration_seconds("30m").expect("minutes"), 1_800);
    assert_eq!(parse_duration_seconds("2h").expect("hours"), 7_200);
    assert_eq!(parse_duration_seconds("1d").expect("days"), 86_400);
    assert!(parse_duration_seconds("0").is_err());
    assert!(parse_duration_seconds("1.5h").is_err());
}