diff --git a/CHANGELOG.md b/CHANGELOG.md index 9c4a58ec..cf0822bd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] ### 🚀 Added - Add colored output feature and `--no-color` flag to disable colors [#307](https://github.com/dotenv-linter/dotenv-linter/pull/307) ([@Nikhil0487](https://github.com/Nikhil0487)) +- Display linted files when run [#311](https://github.com/dotenv-linter/dotenv-linter/pull/311) ([@Anthuang](https://github.com/anthuang)) ### 🔧 Changed @@ -68,7 +69,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Actions uses cache@v2 [#262](https://github.com/dotenv-linter/dotenv-linter/pull/262) ([@gillespiecd](https://github.com/gillespiecd)) - Update logic for IncorrectDelimiterCheck [#267](https://github.com/dotenv-linter/dotenv-linter/pull/267) ([@baile320](https://github.com/baile320)) - Add tests for default implementation of Fix::fix_warnings [#266](https://github.com/dotenv-linter/dotenv-linter/pull/266) ([@kilotaras](https://github.com/kilotaras)) -- Modularize common.rs [#264](https://github.com/dotenv-linter/dotenv-linter/pull/264) ([@gillespeicd](https://github.com/gillespiecd)) +- Modularize common.rs [#264](https://github.com/dotenv-linter/dotenv-linter/pull/264) ([@gillespiecd](https://github.com/gillespiecd)) ## [v2.1.0] - 2020-07-13 ### 🚀 Added diff --git a/src/common.rs b/src/common.rs index 31262e12..ee452e0e 100644 --- a/src/common.rs +++ b/src/common.rs @@ -1,11 +1,13 @@ pub(crate) mod comment; mod file_entry; mod line_entry; +pub(crate) mod output; mod warning; use colored::*; pub use file_entry::FileEntry; pub use line_entry::LineEntry; +pub use output::Output; pub use warning::Warning; pub const LF: &str = "\n"; diff --git a/src/common/output.rs b/src/common/output.rs new file mode 100644 index 00000000..4022e1c2 --- /dev/null +++ b/src/common/output.rs @@ -0,0 +1,123 @@ +use crate::common::{FileEntry, Warning}; +use std::ffi::OsString; +use std::fmt; + +/// Mode in which the program is run. +#[derive(Clone, Copy)] +pub enum Mode { + Fix, + Check, +} + +/// Prefix for the backup output. +const BACKUP_PREFIX: &str = "Original file was backed up to: "; + +/// Wraps warnings to provide more information when printing. +pub struct Output { + /// Path of the file the warnings originated from. + path: FileEntry, + + /// Path of the file's backup. + backup_path: Option, + + /// List of warnings. + pub warnings: Vec, + + /// Mode of the program. + mode: Mode, +} + +impl Output { + pub fn new( + path: FileEntry, + backup_path: Option, + warnings: Vec, + mode: Mode, + ) -> Self { + Self { + path, + backup_path, + warnings, + mode, + } + } + + /// Prints warnings without any additional information. + pub fn print_warnings(&self) { + self.warnings.iter().for_each(|w| println!("{}", w)); + } + + /// Prints the backup file's path. + pub fn print_backup(&self) { + if let Some(p) = &self.backup_path { + println!("{}{:?}", BACKUP_PREFIX, p); + } + } +} + +impl fmt::Display for Output { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.mode { + Mode::Fix => { + write!(f, "Fixing {}", self.path)?; + } + Mode::Check => { + write!(f, "Checking {}", self.path)?; + } + } + if let Some(p) = &self.backup_path { + writeln!(f, "\n{}{:?}", BACKUP_PREFIX, p)?; + } + if !self.warnings.is_empty() { + writeln!(f)?; + } + for w in self.warnings.iter() { + writeln!(f, "{}", w)?; + } + Ok(()) + } +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::common::tests::*; + use crate::common::*; + #[test] + fn output_fmt_test() { + let line = line_entry(1, 1, "FOO=BAR"); + let warning = Warning::new( + line.clone(), + "DuplicatedKey", + String::from("The FOO key is duplicated"), + ); + let output = Output::new(line.file, None, vec![warning], Mode::Check); + + assert_eq!( + format!( + "Checking .env\n{} {}: The FOO key is duplicated\n", + format!("{}:{}", ".env", "1").italic(), + "DuplicatedKey".red().bold() + ), + format!("{}", output) + ); + } + + #[test] + fn fix_output_fmt_test() { + let line = line_entry(1, 1, "FOO=BAR"); + let warning = Warning::new( + line.clone(), + "DuplicatedKey", + String::from("The FOO key is duplicated"), + ); + + let backup_path = OsString::from(".env_1234"); + let output = Output::new(line.file, Some(backup_path), vec![warning], Mode::Fix); + + assert_eq!( + format!("Fixing .env\nOriginal file was backed up to: \".env_1234\"\n\n{} {}: The FOO key is duplicated\n",format!("{}:{}", ".env", "1").italic(), "DuplicatedKey".red().bold()), + format!("{}", output) + ); + } +} diff --git a/src/lib.rs b/src/lib.rs index 1870c4ab..a7e84bde 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -11,7 +11,7 @@ mod fs_utils; pub use checks::available_check_names; #[allow(clippy::redundant_closure)] -pub fn run(args: &clap::ArgMatches, current_dir: &PathBuf) -> Result, Box> { +pub fn run(args: &clap::ArgMatches, current_dir: &PathBuf) -> Result, Box> { let mut file_paths: Vec = Vec::new(); let mut skip_checks: Vec<&str> = Vec::new(); let mut excluded_paths: Vec = Vec::new(); @@ -37,7 +37,12 @@ pub fn run(args: &clap::ArgMatches, current_dir: &PathBuf) -> Result = Vec::new(); + let mut outputs: Vec = Vec::new(); + let mode = if is_fix { + output::Mode::Fix + } else { + output::Mode::Check + }; for path in file_paths { let relative_path = match fs_utils::get_relative_path(&path, ¤t_dir) { @@ -51,6 +56,7 @@ pub fn run(args: &clap::ArgMatches, current_dir: &PathBuf) -> Result Result) { + for (i, output) in outputs.iter().enumerate() { + print!("{}", output); + if i != outputs.len() - 1 || (i == outputs.len() - 1 && output.warnings.is_empty()) { + println!(); + } + } } fn get_file_paths( diff --git a/src/main.rs b/src/main.rs index 10623eda..bf89a331 100644 --- a/src/main.rs +++ b/src/main.rs @@ -15,9 +15,9 @@ fn main() -> Result<(), Box> { process::exit(0); } - let warnings = dotenv_linter::run(&args, ¤t_dir)?; + let outputs = dotenv_linter::run(&args, ¤t_dir)?; - if warnings.is_empty() { + if outputs.is_empty() { process::exit(0); } @@ -27,33 +27,45 @@ fn main() -> Result<(), Box> { } #[cfg(windows)] set_windows_virtual_terminal(); - let total = warnings.len(); + let total = outputs.iter().map(|o| o.warnings.len()).sum(); let is_not_quiet = !args.is_present("quiet"); if args.is_present("fix") { if is_not_quiet { - warnings.iter().for_each(|w| println!("{}", w)); - println!(); + dotenv_linter::print_outputs(outputs); + } else { + outputs.iter().for_each(|w| w.print_backup()); } - - println!("All warnings are fixed. Total: {}", total); + print_fix_total(total); process::exit(0); - } else { - warnings.iter().for_each(|w| println!("{}", w)); + } - if is_not_quiet { - print_total(total); - } + if is_not_quiet { + dotenv_linter::print_outputs(outputs); + print_check_total(total); + } else { + outputs.iter().for_each(|w| w.print_warnings()); } + // Ensure the exit code is 0 if there were no warnings + if total == 0 { + process::exit(0); + } process::exit(1); } -fn print_total(total: usize) { +fn print_fix_total(total: usize) { + if total != 0 { + println!("\nAll warnings are fixed. Total: {}", total); + } else { + println!("\nNo warnings found"); + } +} + +fn print_check_total(total: usize) { let mut problems = String::from("problem"); if total > 1 { problems += "s"; } - println!( "\n{}", format!( diff --git a/tests/args/current_dir.rs b/tests/args/current_dir.rs index 9df48626..bdeba2b0 100644 --- a/tests/args/current_dir.rs +++ b/tests/args/current_dir.rs @@ -1,10 +1,11 @@ -use crate::common::TestDir; +use crate::common::*; #[test] fn exits_with_0_on_no_warnings() { let test_dir = TestDir::new(); test_dir.create_testfile(".env", "FOO=bar\n"); - test_dir.test_command_success(); + let expected_output = check_output(&[(".env", &[])]); + test_dir.test_command_success(expected_output); } #[test] @@ -12,24 +13,26 @@ fn checks_current_dir() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "FOO\n"); - testdir.test_command_fail( - format!( - "{}:1 KeyWithoutValue: The FOO key should be with a value or have an equal sign\n\nFound 1 problem\n", + testdir.test_command_fail(check_output(&[( + testfile.shortname_as_str(), + &[format!( + "{}:1 KeyWithoutValue: The FOO key should be with a value or have an equal sign", testfile.shortname_as_str() ) - ); + .as_str()], + )])); } #[test] fn checks_current_dir_with_dot_arg() { let testdir = TestDir::new(); - let testfile = testdir.create_testfile("test.env", "foo=\n"); + testdir.create_testfile("test.env", "foo=\n"); let args = &["."]; - let expected_output = format!( - "{}:1 LowercaseKey: The foo key should be in uppercase\n\nFound 1 problem\n", - testfile.shortname_as_str(), - ); + let expected_output = check_output(&[( + "test.env", + &["test.env:1 LowercaseKey: The foo key should be in uppercase"], + )]); testdir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/args/specific_path.rs b/tests/args/specific_path.rs index fa61539f..1e04faca 100644 --- a/tests/args/specific_path.rs +++ b/tests/args/specific_path.rs @@ -1,4 +1,4 @@ -use crate::common::TestDir; +use crate::common::*; use std::path::Path; #[test] @@ -8,15 +8,21 @@ fn checks_one_specific_path() { let subdir = testdir.subdir(); let testfile_2 = subdir.create_testfile(".env.test", "1FOO=\n"); + let testfile_2_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[subdir.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 1 problem\n", - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_2.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[( + testfile_2_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_2_path + ) + .as_str()], + )]); testdir.test_command_fail_with_args(args, expected_output); } @@ -28,20 +34,39 @@ fn checks_two_specific_paths() { let subdir_1 = testdir.subdir(); let testfile_2 = subdir_1.create_testfile(".env", " FOO=\n"); + let testfile_2_pathbuf = + Path::new(&testdir.relative_path(&subdir_1)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let subdir_2 = subdir_1.subdir(); let testfile_3 = subdir_2.create_testfile(".env", " FOO=\n"); + let testfile_3_pathbuf = + Path::new(&testdir.relative_path(&subdir_2)).join(testfile_3.shortname_as_str()); + let testfile_3_path = testfile_3_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[subdir_1.as_str(), subdir_2.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 2 problems\n", - Path::new(&testdir.relative_path(&subdir_1)) - .join(testfile_2.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - Path::new(&testdir.relative_path(&subdir_2)) - .join(testfile_3.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - ); + let expected_output = check_output(&[ + ( + testfile_2_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_2_path + ) + .as_str()], + ), + ( + testfile_3_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_3_path + ) + .as_str()], + ), + ]); testdir.test_command_fail_with_args(args, expected_output); } @@ -53,10 +78,14 @@ fn checks_one_specific_file() { let testfile_2 = test_dir.create_testfile("test-env-file", "FOO =\n"); let args = &[testfile_2.as_str()]; - let expected_output = format!( - "{}:1 SpaceCharacter: The line has spaces around equal sign\n\nFound 1 problem\n", - testfile_2.shortname_as_str() - ); + let expected_output = check_output(&[( + testfile_2.shortname_as_str(), + &[format!( + "{}:1 SpaceCharacter: The line has spaces around equal sign", + testfile_2.shortname_as_str() + ) + .as_str()], + )]); test_dir.test_command_fail_with_args(args, expected_output); } @@ -69,15 +98,31 @@ fn checks_two_specific_files() { let subdir = testdir.subdir(); let testfile_3 = subdir.create_testfile("another_test_file", "FOO=BAR\nFOO=BAR\n"); + let testfile_3_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_3.shortname_as_str()); + let testfile_3_path = testfile_3_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[testfile_2.as_str(), testfile_3.as_str()]; - let expected_output = format!( - "{}:2 DuplicatedKey: The FOO key is duplicated\n{}:1 SpaceCharacter: The line has spaces around equal sign\n\nFound 2 problems\n", - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_3.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - testfile_2.shortname_as_str(), - ); + let expected_output = check_output(&[ + ( + testfile_3_path, + &[format!( + "{}:2 DuplicatedKey: The FOO key is duplicated", + testfile_3_path + ) + .as_str()], + ), + ( + testfile_2.shortname_as_str(), + &[format!( + "{}:1 SpaceCharacter: The line has spaces around equal sign", + testfile_2.shortname_as_str() + ) + .as_str()], + ), + ]); testdir.test_command_fail_with_args(args, expected_output); } @@ -88,18 +133,38 @@ fn checks_each_file_only_once_when_listing_same_path_twice() { let subdir = testdir.subdir(); let testfile_1 = subdir.create_testfile(".env.a", " FOO=\n"); + let testfile_1_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_1.shortname_as_str()); + let testfile_1_path = testfile_1_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); + let testfile_2 = subdir.create_testfile(".env.b", "FOO=BAR\nBAR=foo\n"); + let testfile_2_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[subdir.as_str(), subdir.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n", - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_1.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_2.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[ + ( + testfile_1_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_1_path + ) + .as_str()], + ), + ( + testfile_2_path, + &[format!( + "{}:2 UnorderedKey: The BAR key should go before the FOO key", + testfile_2_path + ) + .as_str()], + ), + ]); testdir.test_command_fail_with_args(args, expected_output); } @@ -110,18 +175,37 @@ fn checks_each_file_only_once_when_listing_one_path_and_one_file() { let subdir = testdir.subdir(); let testfile_1 = subdir.create_testfile(".env.a", " FOO=\n"); + let testfile_1_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_1.shortname_as_str()); + let testfile_1_path = testfile_1_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let testfile_2 = subdir.create_testfile(".env.b", "FOO=val\nBAR=foo\n"); + let testfile_2_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[subdir.as_str(), testfile_2.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n", - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_1.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_2.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[ + ( + testfile_1_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_1_path + ) + .as_str()], + ), + ( + testfile_2_path, + &[format!( + "{}:2 UnorderedKey: The BAR key should go before the FOO key", + testfile_2_path + ) + .as_str()], + ), + ]); testdir.test_command_fail_with_args(args, expected_output); } @@ -134,15 +218,31 @@ fn checks_one_specific_file_and_one_path() { let subdir = testdir.subdir(); let testfile_3 = subdir.create_testfile("test.env", "FOO=BAR\nFOO=BAR\n"); + let testfile_3_pathbuf = + Path::new(&testdir.relative_path(&subdir)).join(testfile_3.shortname_as_str()); + let testfile_3_path = testfile_3_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &[testfile_2.as_str(), subdir.as_str()]; - let expected_output = format!( - "{}:2 DuplicatedKey: The FOO key is duplicated\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n", - Path::new(&testdir.relative_path(&subdir)) - .join(testfile_3.shortname_as_str()) - .to_str().expect("multi-platform path to test .env file"), - testfile_2.shortname_as_str(), - ); + let expected_output = check_output(&[ + ( + testfile_3_path, + &[format!( + "{}:2 DuplicatedKey: The FOO key is duplicated", + testfile_3_path + ) + .as_str()], + ), + ( + testfile_2.shortname_as_str(), + &[format!( + "{}:2 UnorderedKey: The BAR key should go before the FOO key", + testfile_2.shortname_as_str() + ) + .as_str()], + ), + ]); testdir.test_command_fail_with_args(args, expected_output); } @@ -154,10 +254,14 @@ fn checks_one_specific_file_twice() { let testfile_2 = test_dir.create_testfile("test-env-file", "1FOO=\n"); let args = &[testfile_2.as_str(), testfile_2.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 1 problem\n", - testfile_2.shortname_as_str() - ); + let expected_output = check_output(&[( + testfile_2.shortname_as_str(), + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_2.shortname_as_str() + ) + .as_str()], + )]); test_dir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/checks/ending_blank_line.rs b/tests/checks/ending_blank_line.rs index abc4b646..892823a9 100644 --- a/tests/checks/ending_blank_line.rs +++ b/tests/checks/ending_blank_line.rs @@ -13,7 +13,9 @@ fn correct_files() { let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - testdir.test_command_success_with_args(args); + let expected_output = check_output(&[(".env", &[])]); + + testdir.test_command_success_with_args(args, expected_output); } } @@ -31,11 +33,14 @@ fn incorrect_files() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - let expected_output = check_output(&[format!( - ".env:{} EndingBlankLine: No blank line at the end of the file", - expected_line_numbers[i] - ) - .as_str()]); + let expected_output = check_output(&[( + ".env", + &[format!( + ".env:{} EndingBlankLine: No blank line at the end of the file", + expected_line_numbers[i] + ) + .as_str()], + )]); testdir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/checks/extra_blank_line.rs b/tests/checks/extra_blank_line.rs index 76530bff..a046cf5f 100644 --- a/tests/checks/extra_blank_line.rs +++ b/tests/checks/extra_blank_line.rs @@ -13,7 +13,9 @@ fn correct_files() { let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - testdir.test_command_success_with_args(args); + let expected_output = check_output(&[(".env", &[])]); + + testdir.test_command_success_with_args(args, expected_output); } } @@ -24,7 +26,10 @@ fn two_blank_lines_at_the_beginning() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - let expected_output = check_output(&[".env:2 ExtraBlankLine: Extra blank line detected"]); + let expected_output = check_output(&[( + ".env", + &[".env:2 ExtraBlankLine: Extra blank line detected"], + )]); testdir.test_command_fail_with_args(args, expected_output); } @@ -36,7 +41,10 @@ fn two_blank_lines_in_the_middle() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - let expected_output = check_output(&[".env:4 ExtraBlankLine: Extra blank line detected"]); + let expected_output = check_output(&[( + ".env", + &[".env:4 ExtraBlankLine: Extra blank line detected"], + )]); testdir.test_command_fail_with_args(args, expected_output); } @@ -48,7 +56,10 @@ fn two_blank_lines_at_the_end() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", content); let args = &[testfile.as_str()]; - let expected_output = check_output(&[".env:5 ExtraBlankLine: Extra blank line detected"]); + let expected_output = check_output(&[( + ".env", + &[".env:5 ExtraBlankLine: Extra blank line detected"], + )]); testdir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/cli.rs b/tests/cli.rs index b1f82bbb..cb0cf674 100644 --- a/tests/cli.rs +++ b/tests/cli.rs @@ -4,3 +4,4 @@ mod common; mod fixes; mod flags; mod options; +mod output; diff --git a/tests/common/output.rs b/tests/common/output.rs index 6e957af5..5586bced 100644 --- a/tests/common/output.rs +++ b/tests/common/output.rs @@ -1,17 +1,83 @@ -const FIX_SENTENCE: &str = "All warnings are fixed. Total:"; - -pub fn fix_output(warnings: &[&str]) -> String { - let total = warnings.len(); - let output: &str = &warnings.join("\n"); - format!("{}\n\n{} {}\n", output, FIX_SENTENCE, total) +enum Mode { + Fix, + Check, } -pub fn check_output(warnings: &[&str]) -> String { - let total = warnings.len(); - let output: &str = &warnings.join("\n"); - let mut problems = String::from("problem"); - if total > 1 { - problems += "s"; +/// Builds test output for validation. +/// +/// # Arguments +/// +/// * `blocks` - A slice of tuples, each containing a file path and a slice of +/// warnings for that file +/// * `mode` - Mode in which the program is run. +fn build_output(blocks: &[(&str, &[&str])], mode: Mode) -> String { + let mut output = String::new(); + let mut count = 0; + + if blocks.is_empty() { + return String::new(); + } + + for (i, b) in blocks.iter().enumerate() { + // Print using `b.0` which is the file path + match mode { + Mode::Fix => { + output.push_str(&format!("Fixing {}\n", b.0)); + } + Mode::Check => { + output.push_str(&format!("Checking {}\n", b.0)); + } + } + if !b.1.is_empty() { + // Print using `b.1` which is a slice of warnings + for w in b.1 { + count += 1; + output.push_str(&format!("{}\n", w)); + } + if i != blocks.len() - 1 { + output.push_str("\n"); + } + } + } + + match mode { + Mode::Fix => { + if count > 0 { + output.push_str(&format!("\nAll warnings are fixed. Total: {}\n", count)); + } else { + output.push_str("\nNo warnings found\n"); + } + } + Mode::Check => { + if count == 0 { + output.push_str("\nNo problems found\n"); + } else if count == 1 { + output.push_str(&format!("\nFound {} problem\n", count)); + } else { + output.push_str(&format!("\nFound {} problems\n", count)); + } + } } - format!("{}\n\nFound {} {}\n", output, total, problems) + + output +} + +/// Builds test output for fixes. +/// +/// # Arguments +/// +/// * `blocks` - A slice of tuples, each containing a file path and a slice of +/// warnings for that file +pub fn fix_output(blocks: &[(&str, &[&str])]) -> String { + build_output(blocks, Mode::Fix) +} + +/// Builds test output for checks. +/// +/// # Arguments +/// +/// * `blocks` - A slice of tuples, each containing a file path and a slice of +/// warnings for that file +pub fn check_output(blocks: &[(&str, &[&str])]) -> String { + build_output(blocks, Mode::Check) } diff --git a/tests/common/test_dir.rs b/tests/common/test_dir.rs index daac9eaf..84acbd0e 100644 --- a/tests/common/test_dir.rs +++ b/tests/common/test_dir.rs @@ -9,6 +9,7 @@ use crate::common::test_file::TestFile; use crate::common::test_link::create_test_symlink; #[cfg(not(windows))] use std::fs::canonicalize; +use std::str::from_utf8; /// Use to test commands in temporary directories pub struct TestDir { @@ -68,9 +69,9 @@ impl TestDir { /// Run the default CLI binary in this TestDir and check it succeeds. /// /// This method removes the TestDir when command has finished. - pub fn test_command_success(self) { + pub fn test_command_success(self, expected_output: String) { let args: &[&str; 0] = &[]; - self.test_command_success_with_args(args); + self.test_command_success_with_args(args, expected_output); } /// Run the default CLI binary in this TestDir and check it fails. @@ -85,7 +86,7 @@ impl TestDir { /// in this TestDir and check it succeeds. /// /// This method removes the TestDir when command has finished. - pub fn test_command_success_with_args(self, args: I) + pub fn test_command_success_with_args(self, args: I, expected_output: String) where I: IntoIterator, S: AsRef, @@ -95,7 +96,8 @@ impl TestDir { cmd.current_dir(&canonical_current_dir) .args(args) .assert() - .success(); + .success() + .stdout(expected_output); self.close(); } @@ -182,6 +184,32 @@ impl TestDir { .success(); } + /// Run the default CLI binary, with command line arguments, in this TestDir + /// and check it succeeds. Return the output from the command. + /// + /// This method does NOT remove TestDir when finished + pub fn test_command_success_and_get_output(&self, args: I) -> String + where + I: IntoIterator, + S: AsRef, + { + let mut cmd = Self::init_cmd(); + let canonical_current_dir = canonicalize(&self.current_dir).expect("canonical current dir"); + String::from( + from_utf8( + cmd.current_dir(&canonical_current_dir) + .args(args) + .assert() + .success() + .get_output() + .stdout + .clone() + .as_slice(), + ) + .expect("convert to &str"), + ) + } + fn init_cmd() -> Command { Command::cargo_bin(env!("CARGO_PKG_NAME")).expect("command from binary name") } diff --git a/tests/fixes/duplicated_key.rs b/tests/fixes/duplicated_key.rs index b698acaa..3cf84938 100644 --- a/tests/fixes/duplicated_key.rs +++ b/tests/fixes/duplicated_key.rs @@ -4,10 +4,13 @@ use crate::common::*; fn duplicated_key() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=DEF\nABC=XYZ\nFOO=BAR\nFOO=BAZ\n"); - let expected_output = fix_output(&[ - ".env:2 DuplicatedKey: The ABC key is duplicated", - ".env:4 DuplicatedKey: The FOO key is duplicated", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:2 DuplicatedKey: The ABC key is duplicated", + ".env:4 DuplicatedKey: The FOO key is duplicated", + ], + )]); testdir.test_command_fix_success(expected_output); diff --git a/tests/fixes/ending_blank_line.rs b/tests/fixes/ending_blank_line.rs index 0d765f8f..68f54af4 100644 --- a/tests/fixes/ending_blank_line.rs +++ b/tests/fixes/ending_blank_line.rs @@ -4,8 +4,10 @@ use crate::common::*; fn ending_blank_line() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=DEF\nFOO=BAR"); - let expected_output = - fix_output(&[".env:2 EndingBlankLine: No blank line at the end of the file"]); + let expected_output = fix_output(&[( + ".env", + &[".env:2 EndingBlankLine: No blank line at the end of the file"], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\nFOO=BAR\n"); diff --git a/tests/fixes/extra_blank_line.rs b/tests/fixes/extra_blank_line.rs index 05384589..ad91f760 100644 --- a/tests/fixes/extra_blank_line.rs +++ b/tests/fixes/extra_blank_line.rs @@ -4,7 +4,10 @@ use crate::common::*; fn extra_blank_line() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=DEF\n\n\nFOO=BAR\n"); - let expected_output = fix_output(&[".env:3 ExtraBlankLine: Extra blank line detected"]); + let expected_output = fix_output(&[( + ".env", + &[".env:3 ExtraBlankLine: Extra blank line detected"], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n"); @@ -19,10 +22,13 @@ fn extra_blank_line_with_control_comments() { ".env", "FOO=BAR\n\n# dotenv-linter:off ExtraBlankLine\n\n\n# dotenv-linter:on ExtraBlankLine\nBAR=FOO\n\n\n", ); - let expected_output = fix_output(&[ - ".env:9 ExtraBlankLine: Extra blank line detected", - ".env:10 ExtraBlankLine: Extra blank line detected", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:9 ExtraBlankLine: Extra blank line detected", + ".env:10 ExtraBlankLine: Extra blank line detected", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "FOO=BAR\n\n# dotenv-linter:off ExtraBlankLine\n\n\n# dotenv-linter:on ExtraBlankLine\nBAR=FOO\n"); diff --git a/tests/fixes/incorrect_delimiter.rs b/tests/fixes/incorrect_delimiter.rs index 7d1e4362..754f33c6 100644 --- a/tests/fixes/incorrect_delimiter.rs +++ b/tests/fixes/incorrect_delimiter.rs @@ -4,10 +4,13 @@ use crate::common::*; fn incorrect_delimiter() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "RAILS-ENV=development\n\nSECRET,KEY*=xyz\n"); - let expected_output = fix_output(&[ - ".env:1 IncorrectDelimiter: The RAILS-ENV key has incorrect delimiter", - ".env:3 IncorrectDelimiter: The SECRET,KEY* key has incorrect delimiter", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 IncorrectDelimiter: The RAILS-ENV key has incorrect delimiter", + ".env:3 IncorrectDelimiter: The SECRET,KEY* key has incorrect delimiter", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!( diff --git a/tests/fixes/key_without_value.rs b/tests/fixes/key_without_value.rs index 4c274520..c9bc1692 100644 --- a/tests/fixes/key_without_value.rs +++ b/tests/fixes/key_without_value.rs @@ -4,9 +4,10 @@ use crate::common::*; fn key_without_value() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "FOO\n\nBAR=\n\nBAZ=QUX\n"); - let expected_output = fix_output(&[ - ".env:1 KeyWithoutValue: The FOO key should be with a value or have an equal sign", - ]); + let expected_output = fix_output(&[( + ".env", + &[".env:1 KeyWithoutValue: The FOO key should be with a value or have an equal sign"], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "FOO=\n\nBAR=\n\nBAZ=QUX\n"); diff --git a/tests/fixes/leading_character.rs b/tests/fixes/leading_character.rs index f03041e6..07bff22e 100644 --- a/tests/fixes/leading_character.rs +++ b/tests/fixes/leading_character.rs @@ -4,11 +4,14 @@ use crate::common::*; fn leading_character() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "*BAR=BAZ\n.ABC=DEF\n1QUX=QUUX\n_FOO=BAR\n"); - let expected_output = fix_output(&[ - ".env:1 LeadingCharacter: Invalid leading character detected", - ".env:2 LeadingCharacter: Invalid leading character detected", - ".env:3 LeadingCharacter: Invalid leading character detected", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 LeadingCharacter: Invalid leading character detected", + ".env:2 LeadingCharacter: Invalid leading character detected", + ".env:3 LeadingCharacter: Invalid leading character detected", + ], + )]); testdir.test_command_fix_success(expected_output); diff --git a/tests/fixes/lowercase_key.rs b/tests/fixes/lowercase_key.rs index 03af14bc..ccbf49ec 100644 --- a/tests/fixes/lowercase_key.rs +++ b/tests/fixes/lowercase_key.rs @@ -4,10 +4,13 @@ use crate::common::*; fn lowercase_key() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "abc=DEF\n\nfOO=BAR\n"); - let expected_output = fix_output(&[ - ".env:1 LowercaseKey: The abc key should be in uppercase", - ".env:3 LowercaseKey: The fOO key should be in uppercase", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 LowercaseKey: The abc key should be in uppercase", + ".env:3 LowercaseKey: The fOO key should be in uppercase", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n"); diff --git a/tests/fixes/mod.rs b/tests/fixes/mod.rs index cce3386f..d091c785 100644 --- a/tests/fixes/mod.rs +++ b/tests/fixes/mod.rs @@ -19,7 +19,7 @@ fn correct_file() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=DEF\nD=BAR\n\nFOO=BAR\n"); - testdir.test_command_fix_success(String::new()); + testdir.test_command_fix_success(fix_output(&[(".env", &[])])); assert_eq!(testfile.contents().as_str(), "ABC=DEF\nD=BAR\n\nFOO=BAR\n"); @@ -31,10 +31,13 @@ fn skip_checks() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "A1=1\nA2=2\na0=0\na2=2\n"); - let expected_output = fix_output(&[ - ".env:3 LowercaseKey: The a0 key should be in uppercase", - ".env:4 LowercaseKey: The a2 key should be in uppercase", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:3 LowercaseKey: The a0 key should be in uppercase", + ".env:4 LowercaseKey: The a2 key should be in uppercase", + ], + )]); testdir.test_command_fix_success_with_args( expected_output, @@ -55,11 +58,22 @@ fn multiple_files() { let testfile3 = testdir.create_testfile("3.env", "A=b \nab=DEF\n\nA=c\n"); let expected_output = fix_output(&[ - "2.env:1 LowercaseKey: The abc key should be in uppercase", - "2.env:4 UnorderedKey: The B key should go before the F key", - "3.env:1 TrailingWhitespace: Trailing whitespace detected", - "3.env:2 LowercaseKey: The ab key should be in uppercase", - "3.env:4 DuplicatedKey: The A key is duplicated", + ("1.env", &[]), + ( + "2.env", + &[ + "2.env:1 LowercaseKey: The abc key should be in uppercase", + "2.env:4 UnorderedKey: The B key should go before the F key", + ], + ), + ( + "3.env", + &[ + "3.env:1 TrailingWhitespace: Trailing whitespace detected", + "3.env:2 LowercaseKey: The ab key should be in uppercase", + "3.env:4 DuplicatedKey: The A key is duplicated", + ], + ), ]); testdir.test_command_fix_success(expected_output); @@ -101,6 +115,6 @@ fn fixtures() { assert_eq!(testfile.contents(), expected_content); // Check the fixed file again and then clean up - testdir.test_command_success(); + testdir.test_command_success(check_output(&[(".env", &[])])); } } diff --git a/tests/fixes/quote_character.rs b/tests/fixes/quote_character.rs index 02437ef1..94b82039 100644 --- a/tests/fixes/quote_character.rs +++ b/tests/fixes/quote_character.rs @@ -4,10 +4,13 @@ use crate::common::*; fn quote_character() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=\"DEF\"\n\nFOO=\'B\"AR\'\n"); - let expected_output = fix_output(&[ - ".env:1 QuoteCharacter: The value has quote characters (\', \")", - ".env:3 QuoteCharacter: The value has quote characters (\', \")", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 QuoteCharacter: The value has quote characters (\', \")", + ".env:3 QuoteCharacter: The value has quote characters (\', \")", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n"); diff --git a/tests/fixes/space_character.rs b/tests/fixes/space_character.rs index e2b38d87..20a76fab 100644 --- a/tests/fixes/space_character.rs +++ b/tests/fixes/space_character.rs @@ -4,10 +4,13 @@ use crate::common::*; fn space_character() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC = DEF\n\nFOO= BAR\n"); - let expected_output = fix_output(&[ - ".env:1 SpaceCharacter: The line has spaces around equal sign", - ".env:3 SpaceCharacter: The line has spaces around equal sign", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 SpaceCharacter: The line has spaces around equal sign", + ".env:3 SpaceCharacter: The line has spaces around equal sign", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n"); diff --git a/tests/fixes/trailing_whitespace.rs b/tests/fixes/trailing_whitespace.rs index 38934eaf..98e47245 100644 --- a/tests/fixes/trailing_whitespace.rs +++ b/tests/fixes/trailing_whitespace.rs @@ -4,10 +4,13 @@ use crate::common::*; fn trailing_whitespace() { let testdir = TestDir::new(); let testfile = testdir.create_testfile(".env", "ABC=DEF \n\nFOO=BAR \n"); - let expected_output = fix_output(&[ - ".env:1 TrailingWhitespace: Trailing whitespace detected", - ".env:3 TrailingWhitespace: Trailing whitespace detected", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:1 TrailingWhitespace: Trailing whitespace detected", + ".env:3 TrailingWhitespace: Trailing whitespace detected", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!(testfile.contents().as_str(), "ABC=DEF\n\nFOO=BAR\n"); diff --git a/tests/fixes/unordered_key.rs b/tests/fixes/unordered_key.rs index 5e6ab9c4..696eeaa1 100644 --- a/tests/fixes/unordered_key.rs +++ b/tests/fixes/unordered_key.rs @@ -27,11 +27,14 @@ fn unordered_key() { \n\ # end comment\n", ); - let expected_output = fix_output(&[ - ".env:5 UnorderedKey: The A key should go before the C key", - ".env:13 UnorderedKey: The K key should go before the M key", - ".env:16 UnorderedKey: The I key should go before the K key", - ]); + let expected_output = fix_output(&[( + ".env", + &[ + ".env:5 UnorderedKey: The A key should go before the C key", + ".env:13 UnorderedKey: The K key should go before the M key", + ".env:16 UnorderedKey: The I key should go before the K key", + ], + )]); testdir.test_command_fix_success(expected_output); assert_eq!( diff --git a/tests/flags/quiet.rs b/tests/flags/quiet.rs index 5bb03b0c..c96afa3a 100644 --- a/tests/flags/quiet.rs +++ b/tests/flags/quiet.rs @@ -20,7 +20,7 @@ fn fix_output_in_quiet_mode() { let _ = test_dir.create_testfile(".env", "abc=DEF\n\nF=BAR\nB=bbb\n"); let args = &["--quiet"]; - let expected_output = format!("All warnings are fixed. Total: {}\n", 2); + let expected_output = format!("\nAll warnings are fixed. Total: {}\n", 2); test_dir.test_command_fix_success_with_args(expected_output, args); test_dir.close(); diff --git a/tests/flags/recursive.rs b/tests/flags/recursive.rs index 0004a003..6f85a01e 100644 --- a/tests/flags/recursive.rs +++ b/tests/flags/recursive.rs @@ -1,4 +1,4 @@ -use crate::common::TestDir; +use crate::common::{check_output, TestDir}; use std::path::Path; #[test] @@ -7,15 +7,24 @@ fn checks_one_in_subdir() { test_dir.create_testfile("correct.env", "FOO=BAR\n"); let test_subdir = test_dir.subdir(); let testfile_2 = test_subdir.create_testfile(".incorrect.env", "1BAR=\n"); + let testfile_2_pathbuf = + Path::new(&test_dir.relative_path(&test_subdir)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &["-r"]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 1 problem\n", - Path::new(&test_dir.relative_path(&test_subdir)) - .join(testfile_2.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[ + ( + testfile_2_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_2_path + ) + .as_str()], + ), + ("correct.env", &[]), + ]); test_dir.test_command_fail_with_args(args, expected_output); } @@ -27,22 +36,40 @@ fn checks_files_in_deep_subdirs() { let test_subdir_2 = test_dir.subdir(); let testfile_2 = test_subdir_2.create_testfile("incorrect.sub_1.env", "FOO=BAR\nBAR=FOO\n"); + let testfile_2_pathbuf = + Path::new(&test_dir.relative_path(&test_subdir_2)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let test_subdir_3 = test_subdir_2.subdir(); let testfile_3 = test_subdir_3.create_testfile(".incorrect.env", "FOO="); + let testfile_3_pathbuf = + Path::new(&test_dir.relative_path(&test_subdir_3)).join(testfile_3.shortname_as_str()); + let testfile_3_path = testfile_3_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let args = &["--recursive"]; - let expected_output = format!( - "{}:1 EndingBlankLine: No blank line at the end of the file\n{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 2 problems\n", - Path::new(&test_dir.relative_path(&test_subdir_3)) - .join(testfile_3.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file"), - Path::new(&test_dir.relative_path(&test_subdir_2)) - .join(testfile_2.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[ + ( + testfile_3_path, + &[format!( + "{}:1 EndingBlankLine: No blank line at the end of the file", + testfile_3_path + ) + .as_str()], + ), + ( + testfile_2_path, + &[format!( + "{}:2 UnorderedKey: The BAR key should go before the FOO key", + testfile_2_path + ) + .as_str()], + ), + ("correct.env", &[]), + ]); test_dir.test_command_fail_with_args(args, expected_output); } @@ -54,8 +81,10 @@ fn checks_without_recursive_flag() { let test_subdir = test_dir.subdir(); test_subdir.create_testfile(".incorrect.env", "1BAR=\n"); + let expected_output = check_output(&[("correct.env", &[])]); + // incorrect file located in a subdirectory should not be checked - test_dir.test_command_success(); + test_dir.test_command_success(expected_output); } #[test] @@ -65,18 +94,27 @@ fn checks_recursive_with_exclude_subdir() { let test_subdir_2 = test_dir.subdir(); let testfile_2 = test_subdir_2.create_testfile("incorrect.sub_1.env", "FOO=BAR\nBAR=FOO\n"); + let testfile_2_pathbuf = + Path::new(&test_dir.relative_path(&test_subdir_2)).join(testfile_2.shortname_as_str()); + let testfile_2_path = testfile_2_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); let test_subdir_3 = test_subdir_2.subdir(); let testfile_to_exclude = test_subdir_3.create_testfile(".incorrect.env", "FOO="); let args = &["--exclude", testfile_to_exclude.as_str(), "--recursive"]; - let expected_output = format!( - "{}:2 UnorderedKey: The BAR key should go before the FOO key\n\nFound 1 problem\n", - Path::new(&test_dir.relative_path(&test_subdir_2)) - .join(testfile_2.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file"), - ); + let expected_output = check_output(&[ + ( + testfile_2_path, + &[format!( + "{}:2 UnorderedKey: The BAR key should go before the FOO key", + testfile_2_path, + ) + .as_str()], + ), + ("correct.env", &[]), + ]); test_dir.test_command_fail_with_args(args, expected_output); } @@ -86,17 +124,23 @@ fn checks_nofollow_subdir_symlinks() { let test_dir = TestDir::new(); let test_subdir = test_dir.subdir(); let testfile = test_subdir.create_testfile(".incorrect.env", "1BAR=\n"); + let testfile_pathbuf = + Path::new(&test_dir.relative_path(&test_subdir)).join(testfile.shortname_as_str()); + let testfile_path = testfile_pathbuf + .to_str() + .expect("multi-platform path to test .env file"); // create a symbolic link to its containing directory test_subdir.create_symlink(&test_subdir, "symlink"); let args = &["-r"]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 1 problem\n", - Path::new(&test_dir.relative_path(&test_subdir)) - .join(testfile.shortname_as_str()) - .to_str() - .expect("multi-platform path to test .env file") - ); + let expected_output = check_output(&[( + testfile_path, + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_path + ) + .as_str()], + )]); test_dir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/options/exclude.rs b/tests/options/exclude.rs index a04313b2..dc783f64 100644 --- a/tests/options/exclude.rs +++ b/tests/options/exclude.rs @@ -1,24 +1,27 @@ -use crate::common::TestDir; +use crate::common::{check_output, TestDir}; #[test] fn exclude_one_file() { let test_dir = TestDir::new(); let testfile = test_dir.create_testfile(".env", " FOO=\n"); - test_dir.test_command_success_with_args(&["--exclude", testfile.as_str()]); + + let expected_output = check_output(&[]); + + test_dir.test_command_success_with_args(&["--exclude", testfile.as_str()], expected_output); } #[test] fn exclude_two_files() { let test_dir = TestDir::new(); let testfile_1 = test_dir.create_testfile(".env", " FOO=\n"); - let testfile_2 = test_dir.create_testfile(".loacl.env", " BAR=\n"); - - test_dir.test_command_success_with_args(&[ - "-e", - testfile_1.as_str(), - "-e", - testfile_2.as_str(), - ]); + let testfile_2 = test_dir.create_testfile(".local.env", " BAR=\n"); + + let expected_output = check_output(&[]); + + test_dir.test_command_success_with_args( + &["-e", testfile_1.as_str(), "-e", testfile_2.as_str()], + expected_output, + ); } #[test] @@ -28,10 +31,14 @@ fn exclude_one_file_check_one_file() { let testfile_to_exclude = test_dir.create_testfile(".exclude-me.env", " BAR=\n"); let args = &["--exclude", testfile_to_exclude.as_str()]; - let expected_output = format!( - "{}:1 LeadingCharacter: Invalid leading character detected\n\nFound 1 problem\n", - testfile_to_check.shortname_as_str() - ); + let expected_output = check_output(&[( + testfile_to_check.shortname_as_str(), + &[format!( + "{}:1 LeadingCharacter: Invalid leading character detected", + testfile_to_check.shortname_as_str() + ) + .as_str()], + )]); test_dir.test_command_fail_with_args(args, expected_output); } diff --git a/tests/output/check.rs b/tests/output/check.rs new file mode 100644 index 00000000..65ae6319 --- /dev/null +++ b/tests/output/check.rs @@ -0,0 +1,146 @@ +//! Tests that output from checks are correct. Mainly needed to ensure that +//! newlines are printed correctly. +use crate::common::*; + +#[test] +fn problems() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + + let expected_output = String::from( + r#"Checking .env +.env:1 LowercaseKey: The abc key should be in uppercase + +Found 1 problem +"#, + ); + + test_dir.test_command_fail(expected_output); +} + +#[test] +fn problems_multiple_files() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\n\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nABC=DEF\n"); + + let expected_output = String::from( + r#"Checking .env +.env:1 LowercaseKey: The abc key should be in uppercase + +Checking .env_1 +.env_1:3 ExtraBlankLine: Extra blank line detected + +Checking .env_2 +.env_2:2 DuplicatedKey: The ABC key is duplicated + +Found 3 problems +"#, + ); + + test_dir.test_command_fail(expected_output); +} + +#[test] +fn problems_first_and_last_file() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nABC=DEF\n"); + + let expected_output = String::from( + r#"Checking .env +.env:1 LowercaseKey: The abc key should be in uppercase + +Checking .env_1 +Checking .env_2 +.env_2:2 DuplicatedKey: The ABC key is duplicated + +Found 2 problems +"#, + ); + + test_dir.test_command_fail(expected_output); +} + +#[test] +fn problems_middle_file() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\n\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\n"); + + let expected_output = String::from( + r#"Checking .env +Checking .env_1 +.env_1:3 ExtraBlankLine: Extra blank line detected + +Checking .env_2 + +Found 1 problem +"#, + ); + + test_dir.test_command_fail(expected_output); +} + +#[test] +fn no_problems() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let expected_output = String::from( + r#"Checking .env + +No problems found +"#, + ); + + test_dir.test_command_success(expected_output); +} + +#[test] +fn no_problems_multiple_files() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\nB=bbb\nF=BAR\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let expected_output = String::from( + r#"Checking .env +Checking .env_1 +Checking .env_2 + +No problems found +"#, + ); + + test_dir.test_command_success(expected_output); +} + +#[test] +fn quiet() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n\nF=BAR\nB=bbb\n"); + + let args = &["--quiet"]; + let expected_output = String::from( + r#".env:1 LowercaseKey: The abc key should be in uppercase +.env:4 UnorderedKey: The B key should go before the F key +"#, + ); + + test_dir.test_command_fail_with_args(args, expected_output); +} + +#[test] +fn quiet_no_problems() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let args = &["--quiet"]; + let expected_output = String::from(""); + + test_dir.test_command_success_with_args(args, expected_output); +} diff --git a/tests/output/fix.rs b/tests/output/fix.rs new file mode 100644 index 00000000..24b4f4e8 --- /dev/null +++ b/tests/output/fix.rs @@ -0,0 +1,236 @@ +//! Tests that output from fixes are correct. Mainly needed to ensure that +//! newlines are printed correctly. +use crate::common::*; +use std::fs; + +#[test] +fn warnings() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + + let args: &[&str] = &[]; + let expected_output = String::from( + r#"Fixing .env +.env:1 LowercaseKey: The abc key should be in uppercase + +All warnings are fixed. Total: 1 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn warnings_multiple_files() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\n\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nABC=DEF\n"); + + let args: &[&str] = &[]; + let expected_output = String::from( + r#"Fixing .env +.env:1 LowercaseKey: The abc key should be in uppercase + +Fixing .env_1 +.env_1:3 ExtraBlankLine: Extra blank line detected + +Fixing .env_2 +.env_2:2 DuplicatedKey: The ABC key is duplicated + +All warnings are fixed. Total: 3 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn no_warnings() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let args: &[&str] = &[]; + let expected_output = String::from( + r#"Fixing .env + +No warnings found +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn no_warnings_multiple_files() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\nB=bbb\nF=BAR\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let args: &[&str] = &[]; + let expected_output = String::from( + r#"Fixing .env +Fixing .env_1 +Fixing .env_2 + +No warnings found +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn mixed_warnings_multiple_files() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n"); + test_dir.create_testfile(".env_1", "ABC=DEF\n"); + test_dir.create_testfile(".env_2", "ABC=DEF\nABC=DEF\n"); + + let args: &[&str] = &[]; + let expected_output = String::from( + r#"Fixing .env +.env:1 LowercaseKey: The abc key should be in uppercase + +Fixing .env_1 +Fixing .env_2 +.env_2:2 DuplicatedKey: The ABC key is duplicated + +All warnings are fixed. Total: 2 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + + test_dir.create_testfile(".env_3", "ABC=DEF\n\n"); + let expected_output = String::from( + r#"Fixing .env +Fixing .env_1 +Fixing .env_2 +Fixing .env_3 +.env_3:3 ExtraBlankLine: Extra blank line detected + +All warnings are fixed. Total: 1 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + + test_dir.create_testfile(".env", "ABC=DEF\n\n"); + let expected_output = String::from( + r#"Fixing .env +.env:3 ExtraBlankLine: Extra blank line detected + +Fixing .env_1 +Fixing .env_2 +Fixing .env_3 + +All warnings are fixed. Total: 1 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + + test_dir.close(); +} + +#[test] +fn quiet() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "abc=DEF\n\nF=BAR\nB=bbb\n"); + + let args = &["--quiet"]; + let expected_output = String::from( + r#" +All warnings are fixed. Total: 2 +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn quiet_no_warnings() { + let test_dir = TestDir::new(); + test_dir.create_testfile(".env", "ABC=DEF\nB=bbb\nF=BAR\n"); + + let args = &["--quiet"]; + let expected_output = String::from( + r#" +No warnings found +"#, + ); + + test_dir.test_command_fix_success_with_args(expected_output, args); + test_dir.close(); +} + +#[test] +fn backup() { + let test_dir = TestDir::new(); + let test_file = test_dir.create_testfile(".env", "abc=DEF\n\nF=BAR\nB=bbb\n"); + + let args = &["-f"]; + let output = test_dir.test_command_success_and_get_output(args); + + let backup_file = fs::read_dir(&test_dir.as_str()) + .expect("read dir") + .filter_map(|e| e.ok()) + .filter(|e| e.path().as_os_str() != test_file.as_str()) + .last() + .expect("get backup file"); + let backup_filename = backup_file + .file_name() + .into_string() + .expect("convert to string"); + let expected_output = format!( + r#"Fixing .env +Original file was backed up to: "{}" + +.env:1 LowercaseKey: The abc key should be in uppercase +.env:4 UnorderedKey: The B key should go before the F key + +All warnings are fixed. Total: 2 +"#, + backup_filename + ); + assert_eq!(output, expected_output); + + test_dir.close(); +} + +#[test] +fn quiet_backup() { + let test_dir = TestDir::new(); + let test_file = test_dir.create_testfile(".env", "abc=DEF\n\nF=BAR\nB=bbb\n"); + + let args = &["-f", "-q"]; + let output = test_dir.test_command_success_and_get_output(args); + + let backup_file = fs::read_dir(&test_dir.as_str()) + .expect("read dir") + .filter_map(|e| e.ok()) + .filter(|e| e.path().as_os_str() != test_file.as_str()) + .last() + .expect("get backup file"); + let backup_filename = backup_file + .file_name() + .into_string() + .expect("convert to string"); + let expected_output = format!( + r#"Original file was backed up to: "{}" + +All warnings are fixed. Total: 2 +"#, + backup_filename + ); + assert_eq!(output, expected_output); + + test_dir.close(); +} diff --git a/tests/output/mod.rs b/tests/output/mod.rs new file mode 100644 index 00000000..7edb210b --- /dev/null +++ b/tests/output/mod.rs @@ -0,0 +1,2 @@ +mod check; +mod fix;