|
| 1 | +use assert_cmd::prelude::*; |
| 2 | +use predicates::prelude::*; |
| 3 | +use std::{error::Error, process::Command}; |
| 4 | + |
| 5 | +mod common; |
| 6 | + |
| 7 | +use common::*; |
| 8 | + |
| 9 | +#[test] |
| 10 | +fn test_validate_with_owned_files() -> Result<(), Box<dyn Error>> { |
| 11 | + run_codeowners( |
| 12 | + "valid_project", |
| 13 | + &["validate", "ruby/app/models/payroll.rb", "ruby/app/models/bank_account.rb"], |
| 14 | + true, |
| 15 | + OutputStream::Stdout, |
| 16 | + predicate::eq(""), |
| 17 | + )?; |
| 18 | + |
| 19 | + Ok(()) |
| 20 | +} |
| 21 | + |
| 22 | +#[test] |
| 23 | +fn test_validate_with_unowned_file() -> Result<(), Box<dyn Error>> { |
| 24 | + run_codeowners( |
| 25 | + "valid_project", |
| 26 | + &["validate", "ruby/app/unowned.rb"], |
| 27 | + false, |
| 28 | + OutputStream::Stdout, |
| 29 | + predicate::str::contains("ruby/app/unowned.rb").and(predicate::str::contains("Unowned")), |
| 30 | + )?; |
| 31 | + |
| 32 | + Ok(()) |
| 33 | +} |
| 34 | + |
| 35 | +#[test] |
| 36 | +fn test_validate_with_mixed_files() -> Result<(), Box<dyn Error>> { |
| 37 | + run_codeowners( |
| 38 | + "valid_project", |
| 39 | + &["validate", "ruby/app/models/payroll.rb", "ruby/app/unowned.rb"], |
| 40 | + false, |
| 41 | + OutputStream::Stdout, |
| 42 | + predicate::str::contains("ruby/app/unowned.rb").and(predicate::str::contains("Unowned")), |
| 43 | + )?; |
| 44 | + |
| 45 | + Ok(()) |
| 46 | +} |
| 47 | + |
| 48 | +#[test] |
| 49 | +fn test_validate_with_no_files() -> Result<(), Box<dyn Error>> { |
| 50 | + // Existing behavior - validates entire project |
| 51 | + run_codeowners("valid_project", &["validate"], true, OutputStream::Stdout, predicate::eq(""))?; |
| 52 | + |
| 53 | + Ok(()) |
| 54 | +} |
| 55 | + |
| 56 | +#[test] |
| 57 | +fn test_generate_and_validate_with_owned_files() -> Result<(), Box<dyn Error>> { |
| 58 | + let fixture_root = std::path::Path::new("tests/fixtures/valid_project"); |
| 59 | + let temp_dir = setup_fixture_repo(fixture_root); |
| 60 | + let project_root = temp_dir.path(); |
| 61 | + git_add_all_files(project_root); |
| 62 | + |
| 63 | + let codeowners_path = project_root.join("tmp/CODEOWNERS"); |
| 64 | + |
| 65 | + Command::cargo_bin("codeowners")? |
| 66 | + .arg("--project-root") |
| 67 | + .arg(project_root) |
| 68 | + .arg("--codeowners-file-path") |
| 69 | + .arg(&codeowners_path) |
| 70 | + .arg("--no-cache") |
| 71 | + .arg("generate-and-validate") |
| 72 | + .arg("ruby/app/models/payroll.rb") |
| 73 | + .arg("ruby/app/models/bank_account.rb") |
| 74 | + .assert() |
| 75 | + .success(); |
| 76 | + |
| 77 | + Ok(()) |
| 78 | +} |
| 79 | + |
| 80 | +#[test] |
| 81 | +fn test_generate_and_validate_with_unowned_file() -> Result<(), Box<dyn Error>> { |
| 82 | + let fixture_root = std::path::Path::new("tests/fixtures/valid_project"); |
| 83 | + let temp_dir = setup_fixture_repo(fixture_root); |
| 84 | + let project_root = temp_dir.path(); |
| 85 | + git_add_all_files(project_root); |
| 86 | + |
| 87 | + let codeowners_path = project_root.join("tmp/CODEOWNERS"); |
| 88 | + |
| 89 | + Command::cargo_bin("codeowners")? |
| 90 | + .arg("--project-root") |
| 91 | + .arg(project_root) |
| 92 | + .arg("--codeowners-file-path") |
| 93 | + .arg(&codeowners_path) |
| 94 | + .arg("--no-cache") |
| 95 | + .arg("generate-and-validate") |
| 96 | + .arg("ruby/app/unowned.rb") |
| 97 | + .assert() |
| 98 | + .failure() |
| 99 | + .stdout(predicate::str::contains("ruby/app/unowned.rb")) |
| 100 | + .stdout(predicate::str::contains("Unowned")); |
| 101 | + |
| 102 | + Ok(()) |
| 103 | +} |
| 104 | + |
| 105 | +#[test] |
| 106 | +fn test_validate_with_absolute_path() -> Result<(), Box<dyn Error>> { |
| 107 | + let fixture_root = std::path::Path::new("tests/fixtures/valid_project"); |
| 108 | + let temp_dir = setup_fixture_repo(fixture_root); |
| 109 | + let project_root = temp_dir.path(); |
| 110 | + git_add_all_files(project_root); |
| 111 | + |
| 112 | + let file_absolute_path = project_root.join("ruby/app/models/payroll.rb").canonicalize()?; |
| 113 | + |
| 114 | + Command::cargo_bin("codeowners")? |
| 115 | + .arg("--project-root") |
| 116 | + .arg(project_root) |
| 117 | + .arg("--no-cache") |
| 118 | + .arg("validate") |
| 119 | + .arg(file_absolute_path.to_str().unwrap()) |
| 120 | + .assert() |
| 121 | + .success(); |
| 122 | + |
| 123 | + Ok(()) |
| 124 | +} |
| 125 | + |
| 126 | +#[test] |
| 127 | +fn test_validate_only_checks_codeowners_file() -> Result<(), Box<dyn Error>> { |
| 128 | + // This test demonstrates that `validate` with files only checks the CODEOWNERS file |
| 129 | + // It does NOT check file annotations or other ownership sources |
| 130 | + // |
| 131 | + // If a file has an annotation but is missing from CODEOWNERS, `validate` will report it as unowned |
| 132 | + // This is why `generate-and-validate` should be used for accuracy |
| 133 | + |
| 134 | + // ruby/app/models/bank_account.rb has @team Payments annotation and is in CODEOWNERS |
| 135 | + run_codeowners( |
| 136 | + "valid_project", |
| 137 | + &["validate", "ruby/app/models/bank_account.rb"], |
| 138 | + true, |
| 139 | + OutputStream::Stdout, |
| 140 | + predicate::eq(""), |
| 141 | + )?; |
| 142 | + |
| 143 | + Ok(()) |
| 144 | +} |
0 commit comments