#![allow(missing_docs)]
use std::fs;
use std::io::Write;
use std::path::{Path, PathBuf};
use std::process::{Command, Stdio};
fn fmt_bin() -> &'static str {
env!("CARGO_BIN_EXE_noyafmt")
}
fn validate_bin() -> &'static str {
env!("CARGO_BIN_EXE_noyavalidate")
}
fn scratch(tag: &str) -> PathBuf {
let dir = std::env::temp_dir().join(format!("noya-cli-cov-{tag}-{}", std::process::id()));
fs::create_dir_all(&dir).expect("create scratch dir");
dir
}
fn write_file(dir: &Path, name: &str, body: &str) -> PathBuf {
let p = dir.join(name);
fs::write(&p, body).expect("write fixture");
p
}
fn run(bin: &str, args: &[&str]) -> (i32, String, String) {
let out = Command::new(bin).args(args).output().expect("spawn");
(
out.status.code().unwrap_or(-1),
String::from_utf8_lossy(&out.stdout).into_owned(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
}
fn run_stdin(bin: &str, args: &[&str], input: &str) -> (i32, String, String) {
let mut child = Command::new(bin)
.args(args)
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.spawn()
.expect("spawn");
child
.stdin
.as_mut()
.unwrap()
.write_all(input.as_bytes())
.unwrap();
let out = child.wait_with_output().unwrap();
(
out.status.code().unwrap_or(-1),
String::from_utf8_lossy(&out.stdout).into_owned(),
String::from_utf8_lossy(&out.stderr).into_owned(),
)
}
#[test]
fn noyafmt_unparsable_file_reports_and_exits_1() {
let d = scratch("fmt-bad");
let f = write_file(&d, "bad.yaml", "a: [1, 2\n");
let (code, _, stderr) = run(fmt_bin(), &[f.to_str().unwrap()]);
assert_eq!(code, 1, "stderr was: {stderr}");
assert!(
stderr.contains("bad.yaml"),
"error should name the offending file: {stderr}"
);
}
#[test]
fn noyafmt_missing_file_reports_and_exits_1() {
let (code, _, stderr) = run(fmt_bin(), &["definitely-not-here-12345.yaml"]);
assert_eq!(code, 1);
assert!(!stderr.is_empty(), "expected an error on stderr");
}
#[test]
fn noyafmt_unparsable_stdin_exits_1() {
let (code, _, stderr) = run_stdin(fmt_bin(), &["--stdin"], "a: [1, 2\n");
assert_eq!(code, 1);
assert!(stderr.contains("error"), "stderr was: {stderr}");
}
#[test]
fn noyafmt_write_rewrites_only_when_changed() {
let d = scratch("fmt-write");
let f = write_file(&d, "messy.yaml", "a: 1\nb: 2\n");
let (code, _, stderr) = run(fmt_bin(), &["--write", f.to_str().unwrap()]);
assert_eq!(code, 0, "stderr: {stderr}");
let after = fs::read_to_string(&f).unwrap();
assert_eq!(after, "a: 1\nb: 2\n", "--write should reformat in place");
let mtime_before = fs::metadata(&f).unwrap().len();
let (code2, _, _) = run(fmt_bin(), &["--write", f.to_str().unwrap()]);
assert_eq!(code2, 0);
assert_eq!(fs::read_to_string(&f).unwrap(), after);
assert_eq!(fs::metadata(&f).unwrap().len(), mtime_before);
}
#[test]
fn noyafmt_check_lists_unformatted_paths() {
let d = scratch("fmt-check");
let f = write_file(&d, "needs.yaml", "a: 1\n");
let (_, stdout, _) = run(fmt_bin(), &["--check", f.to_str().unwrap()]);
assert!(
stdout.contains("needs.yaml"),
"--check should print the path: {stdout:?}"
);
}
#[test]
fn noyavalidate_unparsable_schema_exits_1() {
let d = scratch("val-schema");
let doc = write_file(&d, "doc.yaml", "a: 1\n");
let schema = write_file(&d, "schema.yaml", "type: [object\n");
let (code, _, stderr) = run(
validate_bin(),
&["--schema", schema.to_str().unwrap(), doc.to_str().unwrap()],
);
assert_eq!(code, 1, "stderr: {stderr}");
assert!(
stderr.contains("schema"),
"error should mention the schema: {stderr}"
);
}
#[test]
fn noyavalidate_missing_schema_exits_nonzero() {
let d = scratch("val-noschema");
let doc = write_file(&d, "doc.yaml", "a: 1\n");
let (code, _, stderr) = run(
validate_bin(),
&["--schema", "no-such-schema.yaml", doc.to_str().unwrap()],
);
assert_ne!(code, 0);
assert!(!stderr.is_empty());
}
#[test]
fn noyavalidate_fix_on_unparsable_input_exits_1() {
let d = scratch("val-fix");
let f = write_file(&d, "broken.yaml", "a: [1, 2\n");
let (code, _, stderr) = run(validate_bin(), &["--fix", f.to_str().unwrap()]);
assert_eq!(code, 1, "stderr: {stderr}");
assert!(stderr.contains("fix") || stderr.contains("error"));
}
#[test]
fn noyavalidate_fix_with_broken_schema_exits_1() {
let d = scratch("val-fixschema");
let doc = write_file(&d, "doc2.yaml", "a: 1\n");
let schema = write_file(&d, "schema2.yaml", "type: [object\n");
let (code, _, stderr) = run(
validate_bin(),
&[
"--fix",
"--schema",
schema.to_str().unwrap(),
doc.to_str().unwrap(),
],
);
assert_eq!(code, 1, "stderr: {stderr}");
}
#[test]
fn noyavalidate_fix_reformats_valid_file() {
let d = scratch("val-fixok");
let f = write_file(&d, "messy.yaml", "a: 1\nb: 2\n");
let (code, _, stderr) = run(validate_bin(), &["--fix", f.to_str().unwrap()]);
assert_eq!(code, 0, "stderr: {stderr}");
let after = fs::read_to_string(&f).unwrap();
assert!(
!after.contains("a: 1"),
"--fix should normalise whitespace, got: {after:?}"
);
}
#[test]
fn noyavalidate_fix_with_valid_schema_coerces_and_writes() {
let d = scratch("val-fixcoerce");
let doc = write_file(&d, "cfg.yaml", "port: \"8080\"\n");
let schema = write_file(
&d,
"cfg.schema.yaml",
"type: object\nproperties:\n port:\n type: integer\n",
);
let (code, _, stderr) = run(
validate_bin(),
&[
"--fix",
"--schema",
schema.to_str().unwrap(),
doc.to_str().unwrap(),
],
);
assert_eq!(code, 0, "stderr: {stderr}");
let after = fs::read_to_string(&doc).unwrap();
assert!(
after.contains("8080"),
"value should survive the fix: {after:?}"
);
}
#[test]
fn noyavalidate_fix_with_schema_leaves_uncoercible_untouched() {
let d = scratch("val-fixresidue");
let doc = write_file(&d, "bad.yaml", "port: not-a-number\n");
let before = fs::read_to_string(&doc).unwrap();
let schema = write_file(
&d,
"bad.schema.yaml",
"type: object\nproperties:\n port:\n type: integer\n",
);
let (code, _, _) = run(
validate_bin(),
&[
"--fix",
"--schema",
schema.to_str().unwrap(),
doc.to_str().unwrap(),
],
);
assert_ne!(code, 0, "uncoercible input should not report success");
assert_eq!(
fs::read_to_string(&doc).unwrap(),
before,
"uncoercible input must not be rewritten"
);
}
#[test]
fn noyavalidate_schema_validation_passes_on_conforming_doc() {
let d = scratch("val-ok");
let doc = write_file(&d, "ok.yaml", "port: 8080\n");
let schema = write_file(
&d,
"ok.schema.yaml",
"type: object\nproperties:\n port:\n type: integer\n",
);
let (code, _, stderr) = run(
validate_bin(),
&["--schema", schema.to_str().unwrap(), doc.to_str().unwrap()],
);
assert_eq!(code, 0, "stderr: {stderr}");
}
#[test]
fn noyafmt_default_prints_to_stdout_without_touching_file() {
let d = scratch("fmt-default");
let f = write_file(&d, "plain.yaml", "a: 1\n");
let (code, stdout, stderr) = run(fmt_bin(), &[f.to_str().unwrap()]);
assert_eq!(code, 0, "stderr: {stderr}");
assert_eq!(stdout, "a: 1\n", "formatted source should go to stdout");
assert_eq!(
fs::read_to_string(&f).unwrap(),
"a: 1\n",
"default mode must not modify the file"
);
}
#[test]
fn noyavalidate_fix_from_stdin_writes_to_stdout() {
let (code, stdout, stderr) = run_stdin(validate_bin(), &["--fix"], "a: 1\nb: 2\n");
assert_eq!(code, 0, "stderr: {stderr}");
assert!(
stdout.contains("a: 1"),
"fixed output should reach stdout: {stdout:?}"
);
}
#[test]
fn noyavalidate_plain_stdin_check_succeeds() {
let (code, _, stderr) = run_stdin(validate_bin(), &[], "a: 1\n");
assert_eq!(code, 0, "stderr: {stderr}");
}