use assert_cmd::Command;
use sheeter_engine::{convert_workbook_with_options, Format, SqlDialect};
use std::fs;
use tempfile::tempdir;
fn run_binary(
args: &[&str],
input_bytes: &[u8],
in_ext: &str,
out_ext: &str,
) -> Result<Vec<u8>, 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_bytes).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(out).unwrap())
}
#[test]
fn csv_to_ods_roundtrip() {
let csv = "name,age\nAlice,30\nBob,25\n";
let ods = run_binary(
&["convert", "--from", "csv", "--to", "ods"],
csv.as_bytes(),
"csv",
"ods",
)
.unwrap();
assert!(ods.starts_with(b"PK\x03\x04"), "ODS must be a ZIP");
let back = run_binary(
&["convert", "--from", "ods", "--to", "csv"],
&ods,
"ods",
"csv",
)
.unwrap();
let back_str = String::from_utf8(back).unwrap();
assert!(back_str.contains("Alice"));
assert!(back_str.contains("30"));
assert!(back_str.contains("Bob"));
}
#[test]
fn ods_preserves_number_types() {
let csv = "id,value\n1,3.14\n2,42\n";
let ods = run_binary(
&["convert", "--from", "csv", "--to", "ods"],
csv.as_bytes(),
"csv",
"ods",
)
.unwrap();
let json = run_binary(
&["convert", "--from", "ods", "--to", "json"],
&ods,
"ods",
"json",
)
.unwrap();
let json_str = String::from_utf8(json).unwrap();
assert!(json_str.contains("3.14"), "float preserved: {json_str}");
assert!(json_str.contains("42"), "int preserved: {json_str}");
}
#[test]
fn ods_multi_sheet_to_csv_n_outputs() {
let dir = tempdir().unwrap();
let inp = dir.path().join("in.csv");
let out = dir.path().join("out.ods");
fs::write(&inp, "a,b\n1,2\n").unwrap();
Command::cargo_bin("sheeter")
.unwrap()
.args([
"convert",
"--from",
"csv",
"--to",
"ods",
inp.to_str().unwrap(),
out.to_str().unwrap(),
])
.assert()
.success();
assert!(out.exists());
assert!(fs::read(&out).unwrap().starts_with(b"PK\x03\x04"));
}
#[test]
fn ods_to_json_via_hub() {
let csv = "name\nAlice\nBob\n";
let ods = run_binary(
&["convert", "--from", "csv", "--to", "ods"],
csv.as_bytes(),
"csv",
"ods",
)
.unwrap();
let json = run_binary(
&["convert", "--from", "ods", "--to", "json"],
&ods,
"ods",
"json",
)
.unwrap();
let parsed: serde_json::Value = serde_json::from_slice(&json).unwrap();
let arr = parsed.as_array().expect("array");
assert_eq!(arr.len(), 2);
}
#[test]
fn ods_detected_by_content() {
let csv = "name\nAlice\n";
let ods = run_binary(
&["convert", "--from", "csv", "--to", "ods"],
csv.as_bytes(),
"csv",
"ods",
)
.unwrap();
let dir = tempdir().unwrap();
let path = dir.path().join("mystery.bin");
fs::write(&path, &ods).unwrap();
let output = Command::cargo_bin("sheeter")
.unwrap()
.args(["detect", path.to_str().unwrap()])
.output()
.unwrap();
assert!(output.status.success());
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Ods");
}
#[test]
fn ods_api_roundtrip() {
let csv = "x,y\n1,a\n2,b\n";
let hub = sheeter_engine::CanonicalValue::from_csv(csv).unwrap();
let ods = sheeter_engine::to_ods_workbook(&[("Sheet1".to_string(), hub)]).unwrap();
let parsed = sheeter_engine::CanonicalValue::from_ods(&ods).unwrap();
let json = parsed.to_json().unwrap();
assert!(json.contains("\"1\""), "1 preserved: {json}");
assert!(json.contains("\"a\""), "a preserved: {json}");
}
#[test]
fn ods_workbook_via_convert_workbook() {
let csv = "n\n1\n2\n3\n";
let ods = convert_workbook_with_options(
Format::Csv,
Format::Ods,
csv.as_bytes(),
SqlDialect::Generic,
None,
)
.unwrap();
assert_eq!(ods.len(), 1);
assert!(ods[0].1.starts_with(b"PK\x03\x04"));
}