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 }