use assert_cmd::Command;
use std::fs;
use tempfile::tempdir;
fn run(args: &[&str], input: &str, in_ext: &str, out_ext: &str) -> Result<String, String> {
let dir = tempdir().unwrap();
let inp = dir.path().join(format!("in.{in_ext}"));
let out = dir.path().join(format!("out.{out_ext}"));
fs::write(&inp, input).unwrap();
let mut cmd = Command::cargo_bin("sheeter").unwrap();
cmd.args(args);
cmd.arg(inp.to_str().unwrap());
cmd.arg(out.to_str().unwrap());
let result = cmd.output().unwrap();
if !result.status.success() {
return Err(String::from_utf8_lossy(&result.stderr).to_string());
}
Ok(fs::read_to_string(out).unwrap())
}
#[test]
fn sql_insert_to_csv() {
let sql = "CREATE TABLE pokemon (id INTEGER, name TEXT);\nINSERT INTO pokemon (id, name) VALUES (1, 'Bulbasaur'), (2, 'Ivysaur');";
let csv = run(
&["convert", "--from", "sql", "--to", "csv"],
sql,
"sql",
"csv",
)
.unwrap();
assert_eq!(csv, "id,name\n1,Bulbasaur\n2,Ivysaur\n");
}
#[test]
fn sql_without_create_table_uses_column_names() {
let sql = "INSERT INTO t (a, b) VALUES (1, 'x');";
let json = run(
&["convert", "--from", "sql", "--to", "json"],
sql,
"sql",
"json",
)
.unwrap();
assert!(json.contains(r#""a":1"#), "column names: {json}");
assert!(json.contains(r#""b":"x""#), "column names: {json}");
}
#[test]
fn sql_null_and_boolean() {
let sql = "INSERT INTO t (a, b, c) VALUES (NULL, TRUE, 1.5);";
let json = run(
&["convert", "--from", "sql", "--to", "json"],
sql,
"sql",
"json",
)
.unwrap();
assert!(json.contains("null"), "null: {json}");
assert!(json.contains("true"), "bool: {json}");
assert!(json.contains("1.5"), "float: {json}");
}
#[test]
fn csv_to_sql_derives_table_from_filename() {
let dir = tempdir().unwrap();
let inp = dir.path().join("in.csv");
let out = dir.path().join("users.sql");
fs::write(&inp, "id,name\n1,Alice\n2,Bob\n").unwrap();
Command::cargo_bin("sheeter")
.unwrap()
.args([
"convert",
"--from",
"csv",
"--to",
"sql",
inp.to_str().unwrap(),
out.to_str().unwrap(),
])
.assert()
.success();
let sql = fs::read_to_string(out).unwrap();
assert!(
sql.contains(r#"INSERT INTO "users""#),
"table from filename: {sql}"
);
assert!(sql.contains("'Alice'"), "values: {sql}");
}
#[test]
fn sql_escapes_single_quotes() {
let sql = "INSERT INTO t (name) VALUES ('O''Brien');";
let json = run(
&["convert", "--from", "sql", "--to", "json"],
sql,
"sql",
"json",
)
.unwrap();
assert!(json.contains("O'Brien"), "escaped quote: {json}");
}
#[test]
fn sql_multi_table_to_csv_n_outputs() {
let dir = tempdir().unwrap();
let inp = dir.path().join("in.sql");
let out = dir.path().join("out.csv");
fs::write(
&inp,
"INSERT INTO users (id) VALUES (1);\nINSERT INTO orders (id) VALUES (2);\n",
)
.unwrap();
Command::cargo_bin("sheeter")
.unwrap()
.args([
"convert",
"--from",
"sql",
"--to",
"csv",
inp.to_str().unwrap(),
out.to_str().unwrap(),
])
.assert()
.success();
assert!(dir.path().join("out.users.csv").exists());
assert!(dir.path().join("out.orders.csv").exists());
}
#[test]
fn dialect_flag_accepted() {
let sql = "INSERT INTO t (a) VALUES (1);";
let json = run(
&[
"convert",
"--from",
"sql",
"--to",
"json",
"--dialect",
"postgresql",
],
sql,
"sql",
"json",
)
.unwrap();
assert!(json.contains("\"a\":1"));
}
#[test]
fn select_without_insert_errors() {
let sql = "SELECT 1;";
let err = run(
&["convert", "--from", "sql", "--to", "json"],
sql,
"sql",
"json",
)
.unwrap_err();
assert!(
err.contains("INSERT") || err.contains("no INSERT"),
"expected no-data error: {err}"
);
}