zpl_toolchain_cli 0.1.16

Command-line interface for parsing, validating, formatting, and printing ZPL II label code (part of the zpl-toolchain project)
//! Ensure CLI command failures honor `--output json`.

use std::fs;
use std::process::Command;

use assert_cmd::cargo;

const SAMPLE_ZPL: &str = "^XA\n^FO50,50^A0N,30,30^FDHello^FS\n^XZ\n";

fn zpl_cmd() -> Command {
    Command::new(cargo::cargo_bin!("zpl"))
}

fn write_temp_zpl(content: &str) -> (tempfile::TempDir, String) {
    let dir = tempfile::tempdir().expect("tempdir");
    let path = dir.path().join("test.zpl");
    fs::write(&path, content).expect("write temp zpl");
    (dir, path.to_string_lossy().to_string())
}

#[test]
fn parse_missing_file_emits_json_error_envelope() {
    let output = zpl_cmd()
        .args(["parse", "nope-does-not-exist.zpl", "--output", "json"])
        .output()
        .expect("run parse command");

    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    let json: serde_json::Value = serde_json::from_str(&stdout).expect("valid json envelope");
    assert_eq!(json["success"], false);
    assert_eq!(json["error"], "command_failed");
    assert!(
        json["message"].as_str().is_some_and(|m| {
            m.contains("No such file")
                || m.contains("cannot find the file")
                || m.contains("os error 2")
                || m.contains("failed to")
        }),
        "unexpected message: {}",
        json["message"]
    );
}

#[test]
fn lint_invalid_tables_path_emits_json_error_envelope() {
    let (_dir, path) = write_temp_zpl(SAMPLE_ZPL);
    let output = zpl_cmd()
        .args([
            "lint",
            &path,
            "--tables",
            "missing-parser-tables.json",
            "--output",
            "json",
        ])
        .output()
        .expect("run lint command");

    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    let json: serde_json::Value = serde_json::from_str(&stdout).expect("valid json envelope");
    assert_eq!(json["success"], false);
    assert_eq!(json["error"], "command_failed");
    assert!(
        json["message"]
            .as_str()
            .is_some_and(|m| m.contains("tables file")),
        "unexpected message: {}",
        json["message"]
    );
}

#[test]
fn print_serial_usb_conflict_emits_json_error_envelope() {
    let (_dir, path) = write_temp_zpl(SAMPLE_ZPL);
    let output = zpl_cmd()
        .args([
            "print",
            &path,
            "--printer",
            "usb",
            "--serial",
            "--dry-run",
            "--no-lint",
            "--output",
            "json",
        ])
        .output()
        .expect("run print command");

    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    let json: serde_json::Value = serde_json::from_str(&stdout).expect("valid json envelope");
    assert_eq!(json["success"], false);
    assert_eq!(json["error"], "command_failed");
    assert!(
        json["message"]
            .as_str()
            .is_some_and(|m| m.contains("--serial") && m.contains("USB")),
        "unexpected message: {}",
        json["message"]
    );
}

#[test]
fn format_missing_file_emits_json_error_envelope() {
    let output = zpl_cmd()
        .args(["format", "missing-file.zpl", "--output", "json"])
        .output()
        .expect("run format command");

    assert!(!output.status.success());
    let stdout = String::from_utf8_lossy(&output.stdout);
    let json: serde_json::Value = serde_json::from_str(&stdout).expect("valid json envelope");
    assert_eq!(json["success"], false);
    assert_eq!(json["error"], "command_failed");
    assert!(
        json["message"].as_str().is_some_and(|m| {
            m.contains("No such file")
                || m.contains("cannot find the file")
                || m.contains("os error 2")
                || m.contains("failed to")
        }),
        "unexpected message: {}",
        json["message"]
    );
}