use assert_cmd::Command;
use predicates::prelude::*;
use tempfile::tempdir;
#[test]
fn converts_json_to_yaml_file() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
let output = dir.path().join("output.yaml");
std::fs::write(
&input,
r#"{"server":{"host":"localhost","port":8080},"debug":true}"#,
)
.unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"-o",
output.to_str().unwrap(),
])
.assert()
.success();
let rendered = std::fs::read_to_string(output).unwrap();
assert!(rendered.contains("server:"));
assert!(rendered.contains("host: localhost"));
assert!(rendered.contains("port: 8080"));
assert!(rendered.contains("debug: true"));
}
#[test]
fn converts_toml_to_json_stdout() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.toml");
std::fs::write(&input, "[server]\nhost = \"localhost\"\nport = 8080\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args(["convert", input.to_str().unwrap(), "--to", "json"])
.assert()
.success()
.stdout(predicate::str::contains(r#""server""#))
.stdout(predicate::str::contains(r#""host": "localhost""#))
.stdout(predicate::str::contains(r#""port": 8080"#));
}
#[test]
fn converts_yaml_to_toml_file() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.yaml");
let output = dir.path().join("output.toml");
std::fs::write(&input, "server:\n host: localhost\n port: 8080\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"-o",
output.to_str().unwrap(),
])
.assert()
.success();
let rendered = std::fs::read_to_string(output).unwrap();
assert!(rendered.contains("[server]"));
assert!(rendered.contains("host = \"localhost\""));
assert!(rendered.contains("port = 8080"));
}
#[test]
fn inspect_reports_detected_format_and_root() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.yaml");
std::fs::write(&input, "items:\n - one\n - two\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args(["inspect", input.to_str().unwrap()])
.assert()
.success()
.stdout(predicate::str::contains("format: yaml"))
.stdout(predicate::str::contains("root: object"));
}
#[test]
fn stdout_conversion_requires_to_format() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
std::fs::write(&input, r#"{"ok":true}"#).unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args(["convert", input.to_str().unwrap()])
.assert()
.failure()
.stderr(predicate::str::contains(
"--to is required when --output is not provided",
));
}
#[test]
fn toml_output_requires_object_root() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
let output = dir.path().join("output.toml");
std::fs::write(&input, r#"[1,2,3]"#).unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"-o",
output.to_str().unwrap(),
])
.assert()
.failure()
.stderr(predicate::str::contains(
"TOML output requires an object at the document root",
));
}
#[test]
fn rejects_json_unsigned_integer_over_i64_range() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
std::fs::write(&input, r#"{"too_large":18446744073709551615}"#).unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args(["convert", input.to_str().unwrap(), "--to", "yaml"])
.assert()
.failure()
.stderr(predicate::str::contains(
"JSON unsigned integer exceeds the supported i64 range",
));
}
#[test]
fn check_validates_conversion_without_writing_output() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.toml");
let output = dir.path().join("output.yaml");
std::fs::write(&input, "[server]\nhost = \"localhost\"\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"--to",
"yaml",
"--check",
"-o",
output.to_str().unwrap(),
])
.assert()
.success()
.stdout(predicate::str::contains("ok: conversion to yaml is valid"));
assert!(!output.exists());
}
#[test]
fn check_requires_explicit_output_format() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.toml");
std::fs::write(&input, "[server]\nhost = \"localhost\"\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args(["convert", input.to_str().unwrap(), "--check"])
.assert()
.failure()
.stderr(predicate::str::contains(
"output format is required for --check",
));
}
#[test]
fn refuses_to_overwrite_existing_output_by_default() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
let output = dir.path().join("output.yaml");
std::fs::write(&input, r#"{"ok":true}"#).unwrap();
std::fs::write(&output, "keep: true\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"-o",
output.to_str().unwrap(),
])
.assert()
.failure()
.stderr(predicate::str::contains(
"already exists; pass --overwrite to replace it",
));
assert_eq!(std::fs::read_to_string(output).unwrap(), "keep: true\n");
}
#[test]
fn overwrite_replaces_existing_output() {
let dir = tempdir().unwrap();
let input = dir.path().join("input.json");
let output = dir.path().join("output.yaml");
std::fs::write(&input, r#"{"ok":true}"#).unwrap();
std::fs::write(&output, "keep: true\n").unwrap();
Command::cargo_bin("config-forge")
.unwrap()
.args([
"convert",
input.to_str().unwrap(),
"-o",
output.to_str().unwrap(),
"--overwrite",
])
.assert()
.success();
assert_eq!(std::fs::read_to_string(output).unwrap(), "ok: true\n");
}