pinoc 0.3.3

A CLI tool for setting up pinocchio program project
mod common;

use common::{pinoc, read_json, stderr, stdout, temp_copy};
use serde_json::json;

#[test]
fn marked_constants_reach_both_idls() {
    let dir = temp_copy("idl_constants_pda");
    let out = pinoc(&dir, &["idl"]);
    assert!(out.status.success(), "{}", stderr(&out));
    assert!(
        stdout(&out).contains("3 constant(s) added to the IDL"),
        "{}",
        stdout(&out)
    );

    let number =
        |format: &str| json!({ "kind": "numberTypeNode", "format": format, "endian": "le" });
    let codama = read_json(&dir.join("target/idl/idl_constants_pda.codama.json"));
    assert_eq!(
        codama["program"]["constants"],
        json!([
            {
                "kind": "constantNode",
                "name": "maxOpenPositions",
                "docs": ["Open positions a trader may hold."],
                "type": number("u8"),
                "value": { "kind": "numberValueNode", "number": 8 },
            },
            {
                "kind": "constantNode",
                "name": "maxAtas",
                "type": number("u8"),
                "value": { "kind": "numberValueNode", "number": 12 },
            },
            {
                "kind": "constantNode",
                "name": "maxReserveFloor",
                "docs": ["Ceiling on the reserve floor."],
                "type": number("u64"),
                "value": { "kind": "numberValueNode", "number": 100_000_000_000_000u64 },
            },
        ])
    );

    let shank = read_json(&dir.join("target/idl/idl_constants_pda.json"));
    assert_eq!(
        shank["constants"],
        json!([
            { "name": "MAX_OPEN_POSITIONS", "type": "u8", "value": "8" },
            { "name": "MAX_ATAS", "type": "u8", "value": "12" },
            { "name": "MAX_RESERVE_FLOOR", "type": "u64", "value": "100000000000000" },
        ])
    );
}

#[test]
fn a_program_without_markers_has_no_constants_key() {
    let dir = temp_copy("client_program");
    let out = pinoc(&dir, &["idl"]);
    assert!(out.status.success());
    assert!(!stdout(&out).contains("constant(s) added"));
    let shank = read_json(&dir.join("target/idl/client_program.json"));
    assert!(shank.get("constants").is_none());
}

#[test]
fn a_marker_pinoc_cannot_honour_is_an_error() {
    for (source, expected) in [
        (
            "// pinoc:constant\npub const SLOTS: usize = 4;",
            "its type `usize` is not a fixed-width integer",
        ),
        (
            "// pinoc:constant(u8)\npub const BIG: u64 = 300;",
            "value 300 does not fit in `u8`",
        ),
        (
            "// pinoc:constant\npub const DERIVED: u64 = compute();",
            "not an integer expression pinoc can evaluate",
        ),
        (
            "// pinoc:constant\npub const HUGE: u64 = 1 << 60;",
            "beyond 2^53",
        ),
        (
            "// pinoc:constant(string)\npub const NAME: u8 = 1;",
            "is not an integer type",
        ),
        (
            "// pinoc:constant\n\npub const GAP: u8 = 1;",
            "is not directly above a `const` item",
        ),
        (
            "// pinoc:constant\npub fn not_a_const() {}",
            "is not directly above a `const` item",
        ),
    ] {
        let dir = temp_copy("client_program");
        let lib = dir.join("src/lib.rs");
        let original = std::fs::read_to_string(&lib).unwrap();
        std::fs::write(&lib, format!("{original}\n{source}\n")).unwrap();
        let out = pinoc(&dir, &["idl"]);
        assert!(!out.status.success(), "{source}");
        assert!(
            stderr(&out).contains(expected),
            "{source}: {}",
            stderr(&out)
        );
        assert!(!dir.join("target/idl").exists(), "{source}");
    }
}