xl-cli-tools

CLI tools for viewing and editing Excel files
Log | Files | Refs | README | LICENSE

test_xlset.rs (6484B)


      1 mod common;
      2 
      3 use assert_cmd::Command;
      4 use predicates::prelude::*;
      5 use tempfile::TempDir;
      6 
      7 fn xlset() -> Command {
      8     Command::cargo_bin("xlset").unwrap()
      9 }
     10 
     11 fn xlcat() -> Command {
     12     Command::cargo_bin("xlcat").unwrap()
     13 }
     14 
     15 // ---------------------------------------------------------------------------
     16 // Happy path — single and multiple cell assignments
     17 // ---------------------------------------------------------------------------
     18 
     19 #[test]
     20 fn test_set_single_cell() {
     21     let dir = TempDir::new().unwrap();
     22     let path = dir.path().join("test.xlsx");
     23     common::create_simple(&path);
     24 
     25     xlset()
     26         .arg(path.to_str().unwrap())
     27         .arg("A2=Modified")
     28         .assert()
     29         .success()
     30         .stderr(predicate::str::contains("updated 1 cells"));
     31 
     32     xlcat()
     33         .arg(path.to_str().unwrap())
     34         .assert()
     35         .success()
     36         .stdout(predicate::str::contains("Modified"));
     37 }
     38 
     39 #[test]
     40 fn test_set_multiple_cells() {
     41     let dir = TempDir::new().unwrap();
     42     let path = dir.path().join("test.xlsx");
     43     common::create_simple(&path);
     44 
     45     xlset()
     46         .arg(path.to_str().unwrap())
     47         .arg("A2=Changed")
     48         .arg("B2=999")
     49         .assert()
     50         .success()
     51         .stderr(predicate::str::contains("updated 2 cells"));
     52 
     53     xlcat()
     54         .arg(path.to_str().unwrap())
     55         .assert()
     56         .success()
     57         .stdout(predicate::str::contains("Changed"))
     58         .stdout(predicate::str::contains("999"));
     59 }
     60 
     61 // ---------------------------------------------------------------------------
     62 // Type tag coercion
     63 // ---------------------------------------------------------------------------
     64 
     65 #[test]
     66 fn test_set_with_type_tag() {
     67     let dir = TempDir::new().unwrap();
     68     let path = dir.path().join("test.xlsx");
     69     common::create_simple(&path);
     70 
     71     xlset()
     72         .arg(path.to_str().unwrap())
     73         .arg("A2:str=07401")
     74         .assert()
     75         .success();
     76 
     77     xlcat()
     78         .arg(path.to_str().unwrap())
     79         .assert()
     80         .success()
     81         .stdout(predicate::str::contains("07401"));
     82 }
     83 
     84 // ---------------------------------------------------------------------------
     85 // Output file (in-place vs separate destination)
     86 // ---------------------------------------------------------------------------
     87 
     88 #[test]
     89 fn test_set_with_output_file() {
     90     let dir = TempDir::new().unwrap();
     91     let source = dir.path().join("source.xlsx");
     92     let output = dir.path().join("output.xlsx");
     93     common::create_simple(&source);
     94 
     95     xlset()
     96         .arg(source.to_str().unwrap())
     97         .arg("--output")
     98         .arg(output.to_str().unwrap())
     99         .arg("A2=New")
    100         .assert()
    101         .success();
    102 
    103     // Output file must contain the new value
    104     xlcat()
    105         .arg(output.to_str().unwrap())
    106         .assert()
    107         .success()
    108         .stdout(predicate::str::contains("New"));
    109 
    110     // Source file must still have the original value
    111     xlcat()
    112         .arg(source.to_str().unwrap())
    113         .assert()
    114         .success()
    115         .stdout(predicate::str::contains("Alice"));
    116 }
    117 
    118 // ---------------------------------------------------------------------------
    119 // Sheet selection
    120 // ---------------------------------------------------------------------------
    121 
    122 #[test]
    123 fn test_set_with_sheet_selection() {
    124     let dir = TempDir::new().unwrap();
    125     let path = dir.path().join("test.xlsx");
    126     common::create_multi_sheet(&path);
    127 
    128     xlset()
    129         .arg(path.to_str().unwrap())
    130         .arg("--sheet")
    131         .arg("Expenses")
    132         .arg("A2=Modified")
    133         .assert()
    134         .success();
    135 
    136     xlcat()
    137         .arg(path.to_str().unwrap())
    138         .arg("--sheet")
    139         .arg("Expenses")
    140         .assert()
    141         .success()
    142         .stdout(predicate::str::contains("Modified"));
    143 }
    144 
    145 // ---------------------------------------------------------------------------
    146 // --from CSV
    147 // ---------------------------------------------------------------------------
    148 
    149 #[test]
    150 fn test_set_from_csv() {
    151     let dir = TempDir::new().unwrap();
    152     let path = dir.path().join("test.xlsx");
    153     let csv_path = dir.path().join("updates.csv");
    154     common::create_simple(&path);
    155 
    156     std::fs::write(&csv_path, "cell,value\nA2,Updated\nB2,999\n").unwrap();
    157 
    158     xlset()
    159         .arg(path.to_str().unwrap())
    160         .arg("--from")
    161         .arg(csv_path.to_str().unwrap())
    162         .assert()
    163         .success()
    164         .stderr(predicate::str::contains("updated 2 cells"));
    165 }
    166 
    167 #[test]
    168 fn test_set_from_csv_no_header() {
    169     let dir = TempDir::new().unwrap();
    170     let path = dir.path().join("test.xlsx");
    171     let csv_path = dir.path().join("updates.csv");
    172     common::create_simple(&path);
    173 
    174     // No header row — first line is a valid cell reference
    175     std::fs::write(&csv_path, "A2,Updated\nB2,999\n").unwrap();
    176 
    177     xlset()
    178         .arg(path.to_str().unwrap())
    179         .arg("--from")
    180         .arg(csv_path.to_str().unwrap())
    181         .assert()
    182         .success()
    183         .stderr(predicate::str::contains("updated 2 cells"));
    184 }
    185 
    186 // ---------------------------------------------------------------------------
    187 // Error cases
    188 // ---------------------------------------------------------------------------
    189 
    190 #[test]
    191 fn test_error_no_assignments() {
    192     let dir = TempDir::new().unwrap();
    193     let path = dir.path().join("test.xlsx");
    194     common::create_simple(&path);
    195 
    196     xlset()
    197         .arg(path.to_str().unwrap())
    198         .assert()
    199         .code(2)
    200         .stderr(predicate::str::contains("no assignments"));
    201 }
    202 
    203 #[test]
    204 fn test_error_file_not_found() {
    205     xlset()
    206         .arg("/nonexistent.xlsx")
    207         .arg("A1=42")
    208         .assert()
    209         .code(1)
    210         .stderr(predicate::str::contains("not found"));
    211 }
    212 
    213 #[test]
    214 fn test_error_bad_cell_ref() {
    215     let dir = TempDir::new().unwrap();
    216     let path = dir.path().join("test.xlsx");
    217     common::create_simple(&path);
    218 
    219     // ZZZZZ exceeds the maximum column XFD (index 16383)
    220     xlset()
    221         .arg(path.to_str().unwrap())
    222         .arg("ZZZZZ1=42")
    223         .assert()
    224         .failure();
    225 }
    226 
    227 #[test]
    228 fn test_error_xls_not_supported() {
    229     let dir = TempDir::new().unwrap();
    230     let path = dir.path().join("test.xls");
    231     // Write a fake file with .xls extension
    232     std::fs::write(&path, b"PK fake content").unwrap();
    233 
    234     xlset()
    235         .arg(path.to_str().unwrap())
    236         .arg("A1=42")
    237         .assert()
    238         .failure()
    239         .stderr(predicate::str::contains("only supports .xlsx").or(predicate::str::contains(
    240             "legacy .xls format is not supported",
    241         )));
    242 }