commit d4c4c11ae42e56bab40e1b3b6ea79fb9486942a5
parent 9f852d4250367cf5f7c2dc7094098d504a6dc3ac
Author: Erik Loualiche <[email protected]>
Date: Fri, 13 Mar 2026 19:11:43 -0500
test: add xlset integration tests
11 integration tests covering single/multi-cell assignment, type tags,
--output, --sheet, --from CSV (with and without header), and all error
paths (no assignments, file not found, bad cell ref, .xls extension).
Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
Diffstat:
| A | tests/test_xlset.rs | | | 242 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 242 insertions(+), 0 deletions(-)
diff --git a/tests/test_xlset.rs b/tests/test_xlset.rs
@@ -0,0 +1,242 @@
+mod common;
+
+use assert_cmd::Command;
+use predicates::prelude::*;
+use tempfile::TempDir;
+
+fn xlset() -> Command {
+ Command::cargo_bin("xlset").unwrap()
+}
+
+fn xlcat() -> Command {
+ Command::cargo_bin("xlcat").unwrap()
+}
+
+// ---------------------------------------------------------------------------
+// Happy path — single and multiple cell assignments
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_set_single_cell() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_simple(&path);
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("A2=Modified")
+ .assert()
+ .success()
+ .stderr(predicate::str::contains("updated 1 cells"));
+
+ xlcat()
+ .arg(path.to_str().unwrap())
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Modified"));
+}
+
+#[test]
+fn test_set_multiple_cells() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_simple(&path);
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("A2=Changed")
+ .arg("B2=999")
+ .assert()
+ .success()
+ .stderr(predicate::str::contains("updated 2 cells"));
+
+ xlcat()
+ .arg(path.to_str().unwrap())
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Changed"))
+ .stdout(predicate::str::contains("999"));
+}
+
+// ---------------------------------------------------------------------------
+// Type tag coercion
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_set_with_type_tag() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_simple(&path);
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("A2:str=07401")
+ .assert()
+ .success();
+
+ xlcat()
+ .arg(path.to_str().unwrap())
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("07401"));
+}
+
+// ---------------------------------------------------------------------------
+// Output file (in-place vs separate destination)
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_set_with_output_file() {
+ let dir = TempDir::new().unwrap();
+ let source = dir.path().join("source.xlsx");
+ let output = dir.path().join("output.xlsx");
+ common::create_simple(&source);
+
+ xlset()
+ .arg(source.to_str().unwrap())
+ .arg("--output")
+ .arg(output.to_str().unwrap())
+ .arg("A2=New")
+ .assert()
+ .success();
+
+ // Output file must contain the new value
+ xlcat()
+ .arg(output.to_str().unwrap())
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("New"));
+
+ // Source file must still have the original value
+ xlcat()
+ .arg(source.to_str().unwrap())
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Alice"));
+}
+
+// ---------------------------------------------------------------------------
+// Sheet selection
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_set_with_sheet_selection() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_multi_sheet(&path);
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("--sheet")
+ .arg("Expenses")
+ .arg("A2=Modified")
+ .assert()
+ .success();
+
+ xlcat()
+ .arg(path.to_str().unwrap())
+ .arg("--sheet")
+ .arg("Expenses")
+ .assert()
+ .success()
+ .stdout(predicate::str::contains("Modified"));
+}
+
+// ---------------------------------------------------------------------------
+// --from CSV
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_set_from_csv() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ let csv_path = dir.path().join("updates.csv");
+ common::create_simple(&path);
+
+ std::fs::write(&csv_path, "cell,value\nA2,Updated\nB2,999\n").unwrap();
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("--from")
+ .arg(csv_path.to_str().unwrap())
+ .assert()
+ .success()
+ .stderr(predicate::str::contains("updated 2 cells"));
+}
+
+#[test]
+fn test_set_from_csv_no_header() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ let csv_path = dir.path().join("updates.csv");
+ common::create_simple(&path);
+
+ // No header row — first line is a valid cell reference
+ std::fs::write(&csv_path, "A2,Updated\nB2,999\n").unwrap();
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("--from")
+ .arg(csv_path.to_str().unwrap())
+ .assert()
+ .success()
+ .stderr(predicate::str::contains("updated 2 cells"));
+}
+
+// ---------------------------------------------------------------------------
+// Error cases
+// ---------------------------------------------------------------------------
+
+#[test]
+fn test_error_no_assignments() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_simple(&path);
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .assert()
+ .code(2)
+ .stderr(predicate::str::contains("no assignments"));
+}
+
+#[test]
+fn test_error_file_not_found() {
+ xlset()
+ .arg("/nonexistent.xlsx")
+ .arg("A1=42")
+ .assert()
+ .code(1)
+ .stderr(predicate::str::contains("not found"));
+}
+
+#[test]
+fn test_error_bad_cell_ref() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xlsx");
+ common::create_simple(&path);
+
+ // ZZZZZ exceeds the maximum column XFD (index 16383)
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("ZZZZZ1=42")
+ .assert()
+ .failure();
+}
+
+#[test]
+fn test_error_xls_not_supported() {
+ let dir = TempDir::new().unwrap();
+ let path = dir.path().join("test.xls");
+ // Write a fake file with .xls extension
+ std::fs::write(&path, b"PK fake content").unwrap();
+
+ xlset()
+ .arg(path.to_str().unwrap())
+ .arg("A1=42")
+ .assert()
+ .failure()
+ .stderr(predicate::str::contains("only supports .xlsx").or(predicate::str::contains(
+ "legacy .xls format is not supported",
+ )));
+}