xl-cli-tools

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

test_integration.rs (11136B)


      1 mod common;
      2 
      3 use assert_cmd::Command;
      4 use predicates::prelude::*;
      5 use tempfile::TempDir;
      6 
      7 fn xlcat() -> Command {
      8     Command::cargo_bin("xlcat").unwrap()
      9 }
     10 
     11 #[test]
     12 fn test_simple_file_default() {
     13     let dir = TempDir::new().unwrap();
     14     let path = dir.path().join("simple.xlsx");
     15     common::create_simple(&path);
     16 
     17     xlcat()
     18         .arg(path.to_str().unwrap())
     19         .assert()
     20         .success()
     21         .stdout(predicate::str::contains("# File:"))
     22         .stdout(predicate::str::contains("# Sheets: 1"))
     23         .stdout(predicate::str::contains("## Sheet: Data"))
     24         .stdout(predicate::str::contains("| name"))
     25         .stdout(predicate::str::contains("| Alice"));
     26 }
     27 
     28 #[test]
     29 fn test_schema_mode() {
     30     let dir = TempDir::new().unwrap();
     31     let path = dir.path().join("simple.xlsx");
     32     common::create_simple(&path);
     33 
     34     xlcat()
     35         .arg("--schema")
     36         .arg(path.to_str().unwrap())
     37         .assert()
     38         .success()
     39         .stdout(predicate::str::contains("| Column"))
     40         .stdout(predicate::str::contains("| name"))
     41         .stdout(predicate::str::contains("Alice").not());
     42 }
     43 
     44 #[test]
     45 fn test_multi_sheet_default_lists_schemas() {
     46     let dir = TempDir::new().unwrap();
     47     let path = dir.path().join("multi.xlsx");
     48     common::create_multi_sheet(&path);
     49 
     50     xlcat()
     51         .arg(path.to_str().unwrap())
     52         .assert()
     53         .success()
     54         .stdout(predicate::str::contains("# Sheets: 3"))
     55         .stdout(predicate::str::contains("## Sheet: Revenue"))
     56         .stdout(predicate::str::contains("## Sheet: Expenses"))
     57         .stdout(predicate::str::contains("## Sheet: Summary"))
     58         .stdout(predicate::str::contains("Use --sheet"));
     59 }
     60 
     61 #[test]
     62 fn test_multi_sheet_select_by_name() {
     63     let dir = TempDir::new().unwrap();
     64     let path = dir.path().join("multi.xlsx");
     65     common::create_multi_sheet(&path);
     66 
     67     xlcat()
     68         .arg("--sheet")
     69         .arg("Revenue")
     70         .arg(path.to_str().unwrap())
     71         .assert()
     72         .success()
     73         .stdout(predicate::str::contains("| region |"))
     74         .stdout(predicate::str::contains("| Region 1 |"));
     75 }
     76 
     77 #[test]
     78 fn test_multi_sheet_select_by_index() {
     79     let dir = TempDir::new().unwrap();
     80     let path = dir.path().join("multi.xlsx");
     81     common::create_multi_sheet(&path);
     82 
     83     xlcat()
     84         .arg("--sheet")
     85         .arg("1")
     86         .arg(path.to_str().unwrap())
     87         .assert()
     88         .success()
     89         .stdout(predicate::str::contains("## Sheet: Expenses"));
     90 }
     91 
     92 #[test]
     93 fn test_head_tail_adaptive() {
     94     let dir = TempDir::new().unwrap();
     95     let path = dir.path().join("many.xlsx");
     96     common::create_many_rows(&path);
     97 
     98     xlcat()
     99         .arg(path.to_str().unwrap())
    100         .assert()
    101         .success()
    102         .stdout(predicate::str::contains("rows omitted"));
    103 }
    104 
    105 #[test]
    106 fn test_head_flag() {
    107     let dir = TempDir::new().unwrap();
    108     let path = dir.path().join("many.xlsx");
    109     common::create_many_rows(&path);
    110 
    111     xlcat()
    112         .arg("--head")
    113         .arg("3")
    114         .arg(path.to_str().unwrap())
    115         .assert()
    116         .success()
    117         .stdout(predicate::str::contains("omitted").not());
    118 }
    119 
    120 #[test]
    121 fn test_csv_mode() {
    122     let dir = TempDir::new().unwrap();
    123     let path = dir.path().join("simple.xlsx");
    124     common::create_simple(&path);
    125 
    126     xlcat()
    127         .arg("--csv")
    128         .arg(path.to_str().unwrap())
    129         .assert()
    130         .success()
    131         .stdout(predicate::str::contains("# File:").not())
    132         .stdout(predicate::str::contains("name,"));
    133 }
    134 
    135 #[test]
    136 fn test_invalid_flag_combo() {
    137     let dir = TempDir::new().unwrap();
    138     let path = dir.path().join("simple.xlsx");
    139     common::create_simple(&path);
    140 
    141     xlcat()
    142         .arg("--schema")
    143         .arg("--head")
    144         .arg("10")
    145         .arg(path.to_str().unwrap())
    146         .assert()
    147         .code(2)
    148         .stderr(predicate::str::contains("cannot be combined"));
    149 }
    150 
    151 #[test]
    152 fn test_file_not_found() {
    153     xlcat()
    154         .arg("/nonexistent.xlsx")
    155         .assert()
    156         .failure()
    157         .stderr(predicate::str::contains("Cannot"));
    158 }
    159 
    160 #[test]
    161 fn test_empty_sheet() {
    162     let dir = TempDir::new().unwrap();
    163     let path = dir.path().join("empty.xlsx");
    164     common::create_empty_sheet(&path);
    165 
    166     xlcat()
    167         .arg(path.to_str().unwrap())
    168         .assert()
    169         .success()
    170         .stdout(predicate::str::contains("empty"));
    171 }
    172 
    173 #[test]
    174 fn test_describe_mode() {
    175     let dir = TempDir::new().unwrap();
    176     let path = dir.path().join("simple.xlsx");
    177     common::create_simple(&path);
    178 
    179     xlcat()
    180         .arg(path.to_str().unwrap())
    181         .arg("--describe")
    182         .assert()
    183         .success()
    184         .stdout(predicate::str::contains("count"))
    185         .stdout(predicate::str::contains("mean"));
    186 }
    187 
    188 #[test]
    189 fn test_all_without_sheet_on_multi() {
    190     let dir = TempDir::new().unwrap();
    191     let path = dir.path().join("multi.xlsx");
    192     common::create_multi_sheet(&path);
    193 
    194     xlcat()
    195         .arg("--all")
    196         .arg(path.to_str().unwrap())
    197         .assert()
    198         .failure()
    199         .stderr(predicate::str::contains("Multiple sheets"));
    200 }
    201 
    202 #[test]
    203 fn test_large_file_gate_triggers() {
    204     let dir = TempDir::new().unwrap();
    205     let path = dir.path().join("many.xlsx");
    206     common::create_many_rows(&path);
    207 
    208     // Set max-size very low so gate triggers
    209     xlcat()
    210         .arg(path.to_str().unwrap())
    211         .arg("--max-size")
    212         .arg("1K")
    213         .assert()
    214         .success()
    215         .stdout(predicate::str::contains("Large file"));
    216 }
    217 
    218 #[test]
    219 fn test_large_file_gate_overridden_by_head() {
    220     let dir = TempDir::new().unwrap();
    221     let path = dir.path().join("many.xlsx");
    222     common::create_many_rows(&path);
    223 
    224     let output = xlcat()
    225         .arg(path.to_str().unwrap())
    226         .arg("--max-size")
    227         .arg("1K")
    228         .arg("--head")
    229         .arg("5")
    230         .assert()
    231         .success();
    232 
    233     let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
    234     assert!(!stdout.contains("Large file"));
    235 }
    236 
    237 #[test]
    238 fn test_large_file_gate_overridden_by_all() {
    239     let dir = TempDir::new().unwrap();
    240     let path = dir.path().join("many.xlsx");
    241     common::create_many_rows(&path);
    242 
    243     let output = xlcat()
    244         .arg(path.to_str().unwrap())
    245         .arg("--max-size")
    246         .arg("1K")
    247         .arg("--all")
    248         .assert()
    249         .success();
    250 
    251     let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
    252     assert!(!stdout.contains("Large file"));
    253 }
    254 
    255 #[test]
    256 fn test_empty_data_headers_only() {
    257     let dir = TempDir::new().unwrap();
    258     let path = dir.path().join("empty_data.xlsx");
    259     common::create_empty_data(&path);
    260 
    261     xlcat()
    262         .arg(path.to_str().unwrap())
    263         .assert()
    264         .success()
    265         .stdout(predicate::str::contains("no data rows"));
    266 }
    267 
    268 #[test]
    269 fn test_head_and_tail_together() {
    270     let dir = TempDir::new().unwrap();
    271     let path = dir.path().join("many.xlsx");
    272     common::create_many_rows(&path);
    273 
    274     xlcat()
    275         .arg(path.to_str().unwrap())
    276         .arg("--head")
    277         .arg("3")
    278         .arg("--tail")
    279         .arg("2")
    280         .assert()
    281         .success()
    282         .stdout(predicate::str::contains("omitted"));
    283 }
    284 
    285 #[test]
    286 fn test_tail_flag_alone() {
    287     let dir = TempDir::new().unwrap();
    288     let path = dir.path().join("many.xlsx");
    289     common::create_many_rows(&path);
    290 
    291     let output = xlcat()
    292         .arg(path.to_str().unwrap())
    293         .arg("--tail")
    294         .arg("3")
    295         .assert()
    296         .success();
    297 
    298     let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
    299     assert!(!stdout.contains("omitted"));
    300     // Should contain the last rows (ids near 80)
    301     assert!(stdout.contains("80"));
    302 }
    303 
    304 #[test]
    305 fn test_csv_respects_head() {
    306     let dir = TempDir::new().unwrap();
    307     let path = dir.path().join("many.xlsx");
    308     common::create_many_rows(&path);
    309 
    310     let output = xlcat()
    311         .arg(path.to_str().unwrap())
    312         .arg("--csv")
    313         .arg("--head")
    314         .arg("3")
    315         .assert()
    316         .success();
    317 
    318     let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
    319     // Header + 3 data rows = 4 non-empty lines
    320     let lines: Vec<&str> = stdout.trim().lines().collect();
    321     assert_eq!(lines.len(), 4, "Expected header + 3 data rows, got: {}", stdout);
    322 }
    323 
    324 #[test]
    325 fn test_head_tail_overlap_shows_all() {
    326     let dir = TempDir::new().unwrap();
    327     let path = dir.path().join("simple.xlsx");
    328     common::create_simple(&path);
    329 
    330     // 5 rows, head 3 + tail 3 = 6 > 5, so show all without duplication
    331     let output = xlcat()
    332         .arg(path.to_str().unwrap())
    333         .arg("--head")
    334         .arg("3")
    335         .arg("--tail")
    336         .arg("3")
    337         .assert()
    338         .success();
    339 
    340     let stdout = String::from_utf8(output.get_output().stdout.clone()).unwrap();
    341     assert!(!stdout.contains("omitted"));
    342     assert!(stdout.contains("Alice"));
    343     assert!(stdout.contains("Eve"));
    344 }
    345 
    346 #[test]
    347 fn test_wrong_extension() {
    348     let dir = TempDir::new().unwrap();
    349     let path = dir.path().join("data.csv");
    350     std::fs::write(&path, "a,b\n1,2\n").unwrap();
    351 
    352     xlcat()
    353         .arg(path.to_str().unwrap())
    354         .assert()
    355         .failure()
    356         .stderr(predicate::str::contains("Expected .xls or .xlsx"));
    357 }
    358 
    359 #[test]
    360 fn test_sheet_not_found() {
    361     let dir = TempDir::new().unwrap();
    362     let path = dir.path().join("simple.xlsx");
    363     common::create_simple(&path);
    364 
    365     xlcat()
    366         .arg(path.to_str().unwrap())
    367         .arg("--sheet")
    368         .arg("Nonexistent")
    369         .assert()
    370         .failure()
    371         .stderr(predicate::str::contains("not found"));
    372 }
    373 
    374 #[test]
    375 fn test_exit_code_success() {
    376     let dir = TempDir::new().unwrap();
    377     let path = dir.path().join("simple.xlsx");
    378     common::create_simple(&path);
    379 
    380     xlcat()
    381         .arg(path.to_str().unwrap())
    382         .assert()
    383         .code(0);
    384 }
    385 
    386 #[test]
    387 fn test_exit_code_runtime_error() {
    388     xlcat()
    389         .arg("/nonexistent.xlsx")
    390         .assert()
    391         .code(1);
    392 }
    393 
    394 #[test]
    395 fn test_exit_code_invalid_args() {
    396     let dir = TempDir::new().unwrap();
    397     let path = dir.path().join("simple.xlsx");
    398     common::create_simple(&path);
    399 
    400     xlcat()
    401         .arg(path.to_str().unwrap())
    402         .arg("--schema")
    403         .arg("--describe")
    404         .assert()
    405         .code(2);
    406 }
    407 
    408 #[test]
    409 fn test_describe_multi_sheet_no_sheet_flag() {
    410     let dir = TempDir::new().unwrap();
    411     let path = dir.path().join("multi.xlsx");
    412     common::create_multi_sheet(&path);
    413 
    414     xlcat()
    415         .arg(path.to_str().unwrap())
    416         .arg("--describe")
    417         .assert()
    418         .success()
    419         .stdout(predicate::str::contains("## Sheet: Revenue"))
    420         .stdout(predicate::str::contains("## Sheet: Expenses"))
    421         .stdout(predicate::str::contains("count"))
    422         .stdout(predicate::str::contains("mean"));
    423 }
    424 
    425 #[test]
    426 fn test_csv_multi_sheet_without_sheet_is_error() {
    427     let dir = TempDir::new().unwrap();
    428     let path = dir.path().join("multi.xlsx");
    429     common::create_multi_sheet(&path);
    430 
    431     xlcat()
    432         .arg(path.to_str().unwrap())
    433         .arg("--csv")
    434         .assert()
    435         .failure()
    436         .stderr(predicate::str::contains("Multiple sheets"));
    437 }