dbz-cli 0.2.2

Command-line utility for converting dbz files to text-based formats
Documentation
use std::fs;
use std::io::Read;

use assert_cmd::Command;
use predicates::str::{contains, ends_with, is_empty, starts_with};
use tempfile::{tempdir, NamedTempFile};

fn cmd() -> Command {
    Command::cargo_bin("dbz").unwrap()
}

const DBZ_PATH: &str = concat!(env!("CARGO_MANIFEST_DIR"), "/../../tests/data");

#[test]
fn write_json_to_path() {
    // create a directory whose contents will be cleaned up at the end of the test
    let output_dir = tempdir().unwrap();
    let output_path = format!("{}/a.json", output_dir.path().to_string_lossy());
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbp-1.dbz"),
            "--output",
            &output_path,
            "--json",
        ])
        .assert()
        .success()
        .stdout(is_empty());
    let contents = fs::read_to_string(output_path).unwrap();
    assert!(contents.contains(","));
    assert!(contents.contains("["));
    assert!(contents.contains("]"));
    assert!(contents.contains("{"));
    assert!(contents.contains("{"));
    assert!(contents.ends_with("\n"));
}

#[test]
fn write_to_stdout() {
    cmd()
        .args(&[&format!("{DBZ_PATH}/test_data.mbo.dbz"), "--csv"])
        .assert()
        .success()
        .stdout(contains("channel_id"));
}

#[test]
fn write_to_nonexistent_path() {
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--output",
            "./a/b/c/d/e",
            "-C", // CSV
        ])
        .assert()
        .failure()
        .stderr(contains("Unable to open output file './a/b/c/d/e'"));
}

#[test]
fn read_from_nonexistent_path() {
    let input_file = NamedTempFile::new().unwrap();
    let input_path = input_file.path().to_owned();
    // delete input_file and ensure it was cleaned up
    input_file.close().unwrap();
    let output_file = NamedTempFile::new().unwrap();
    cmd()
        .args(&[
            &input_path.to_string_lossy(),
            "--output",
            &output_file.path().to_string_lossy(),
        ])
        .assert()
        .failure()
        .stderr(contains("Error opening dbz file"));
}

#[test]
fn write_csv() {
    // create a directory whose contents will be cleaned up at the end of the test
    let output_dir = tempdir().unwrap();
    let output_path = format!("{}/a.json", output_dir.path().to_string_lossy());
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbp-1.dbz"),
            "--output",
            &output_path,
            "--csv",
        ])
        .assert()
        .success()
        .stdout(is_empty());
    let contents = fs::read_to_string(output_path).unwrap();
    assert!(contents.contains(","));
    assert!(!contents.contains("["));
    assert!(!contents.contains("]"));
    assert!(!contents.contains("{"));
    assert!(!contents.contains("{"));
    assert!(contents.ends_with("\n"));
}

#[test]
fn encoding_overrides_extension() {
    // create a directory whose contents will be cleaned up at the end of the test
    let output_dir = tempdir().unwrap();
    // output file extension is csv, but the encoding argument is json
    let output_path = format!("{}/a.csv", output_dir.path().to_string_lossy());
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbp-1.dbz"),
            "--output",
            &output_path,
            "-J", // JSON
        ])
        .assert()
        .success()
        .stdout(is_empty());
    let contents = fs::read_to_string(output_path).unwrap();
    assert!(contents.contains(","));
    assert!(contents.contains("["));
    assert!(contents.contains("]"));
    assert!(contents.contains("{"));
    assert!(contents.contains("{"));
    assert!(contents.ends_with("\n"));
}

#[test]
fn bad_infer() {
    let output_dir = tempdir().unwrap();
    let output_path = format!("{}/a.yaml", output_dir.path().to_string_lossy());
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--output",
            &output_path,
        ])
        .assert()
        .failure()
        .stderr(contains(
            "Unable to infer output encoding from output file with extension 'yaml'",
        ));
}

#[test]
fn no_extension_infer() {
    let output_dir = tempdir().unwrap();
    let output_path = format!("{}/a", output_dir.path().to_string_lossy());
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--output",
            &output_path,
        ])
        .assert()
        .failure()
        .stderr(contains(
            "Unable to infer output encoding from output file without an extension",
        ));
}

#[test]
fn overwrite_fails() {
    let output_file = NamedTempFile::new().unwrap();

    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--output",
            &output_file.path().to_string_lossy(),
            "--csv",
        ])
        .assert()
        .failure()
        .stderr(contains("Output file exists"));
}

#[test]
fn force_overwrite() {
    let output_file = NamedTempFile::new().unwrap();
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--output",
            &output_file.path().to_string_lossy(),
            "-C", // CSV
            "--force",
        ])
        .assert()
        .success()
        .stdout(is_empty());
    let mut contents = String::new();
    output_file.as_file().read_to_string(&mut contents).unwrap();
}

#[test]
fn cant_specify_json_and_csv() {
    cmd()
        .args(&[&format!("{DBZ_PATH}/test_data.mbo.dbz"), "--json", "--csv"])
        .assert()
        .failure()
        .stderr(contains("cannot be used with"));
}

#[test]
fn metadata() {
    cmd()
        .args(&[&format!("{DBZ_PATH}/test_data.ohlcv-1m.dbz"), "-J", "-m"])
        .assert()
        .success()
        .stdout(starts_with("{"))
        .stdout(ends_with("}\n"))
        .stderr(is_empty());
}

#[test]
fn no_csv_metadata() {
    cmd()
        .args(&[&format!("{DBZ_PATH}/test_data.ohlcv-1m.dbz"), "--csv", "-m"])
        .assert()
        .failure()
        .stdout(is_empty())
        .stderr(contains("unsupported"));
}

#[test]
fn pretty_print_data() {
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "--json",
            "--pretty-json",
        ])
        .assert()
        .success()
        .stdout(contains("    "))
        .stdout(contains(",\n"))
        .stderr(is_empty());
}

#[test]
fn pretty_print_data_metadata() {
    cmd()
        .args(&[
            &format!("{DBZ_PATH}/test_data.mbo.dbz"),
            "-J",
            "--metadata",
            "-p",
        ])
        .assert()
        .stderr(is_empty());
}

#[test]
fn read_from_stdin() {
    let path = format!("{DBZ_PATH}/test_data.mbp-10.dbz");
    let read_from_stdin_output = cmd()
        .args(&[
            "-", // STDIN
            "--json",
        ])
        // Pipe input from file
        .pipe_stdin(&path)
        .unwrap()
        .ok()
        .unwrap();
    let read_from_file_output = cmd().args(&[&path, "--json"]).ok().unwrap();
    assert_eq!(read_from_stdin_output.stdout, read_from_file_output.stdout);
    assert!(read_from_stdin_output.stderr.is_empty());
    assert!(read_from_file_output.stderr.is_empty());
}

#[test]
fn help() {
    cmd()
        .arg("--help")
        .assert()
        .success()
        .stdout(contains("USAGE:"));
}

#[test]
fn version() {
    cmd()
        .arg("--version")
        .assert()
        .success()
        .stdout(contains(env!("CARGO_PKG_VERSION")));
}