use switchy_fs::TempDir;
use clippier::OutputType;
use clippier::feature_validator::{FeatureValidator, ValidatorConfig};
fn create_multi_feature_workspace() -> TempDir {
let temp_dir = switchy_fs::tempdir().unwrap();
let root_path = temp_dir.path();
let workspace_cargo = r#"[workspace]
members = ["pkg_a", "pkg_b"]
[workspace.dependencies]
serde = "1.0"
"#;
switchy_fs::sync::write(root_path.join("Cargo.toml"), workspace_cargo).unwrap();
switchy_fs::sync::create_dir(root_path.join("pkg_a")).unwrap();
switchy_fs::sync::create_dir(root_path.join("pkg_a/src")).unwrap();
switchy_fs::sync::write(root_path.join("pkg_a/src/lib.rs"), "").unwrap();
let pkg_a_cargo = r#"[package]
name = "pkg_a"
version = "0.1.0"
[dependencies]
pkg_b = { path = "../pkg_b" }
serde = { workspace = true }
[features]
default = ["pkg_b/default"]
fail-on-warnings = ["pkg_b/fail-on-warnings"]
test-utils = ["pkg_b/test-utils"]
test-fixtures = ["pkg_b/test-fixtures"]
mp3-codec = []
flac-codec = []
opus-codec = []
"#;
switchy_fs::sync::write(root_path.join("pkg_a/Cargo.toml"), pkg_a_cargo).unwrap();
switchy_fs::sync::create_dir(root_path.join("pkg_b")).unwrap();
switchy_fs::sync::create_dir(root_path.join("pkg_b/src")).unwrap();
switchy_fs::sync::write(root_path.join("pkg_b/src/lib.rs"), "").unwrap();
let pkg_b_cargo = r#"[package]
name = "pkg_b"
version = "0.1.0"
[features]
default = []
fail-on-warnings = []
test-utils = []
test-fixtures = []
"#;
switchy_fs::sync::write(root_path.join("pkg_b/Cargo.toml"), pkg_b_cargo).unwrap();
temp_dir
}
#[switchy_async::test]
async fn test_cli_skip_features_default_behavior() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: None, workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
assert!(
result.errors.is_empty()
|| !result
.errors
.iter()
.any(|e| e.errors.iter().any(|fe| fe.feature == "default"))
);
}
#[switchy_async::test]
async fn test_cli_skip_features_empty_string() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: Some(vec![]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
assert!(result.total_packages > 0);
assert_eq!(result.errors.len(), 0);
}
#[switchy_async::test]
async fn test_cli_skip_features_wildcard_pattern() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: Some(vec!["test-*".to_string()]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
assert!(
!result
.errors
.iter()
.any(|e| e.errors.iter().any(|fe| fe.feature.starts_with("test-")))
);
}
#[switchy_async::test]
async fn test_cli_skip_features_multiple_patterns() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: Some(vec!["*-codec".to_string(), "test-*".to_string()]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
if !result.errors.is_empty() {
for error in &result.errors {
for feature_error in &error.errors {
assert!(
feature_error.feature == "default"
|| feature_error.feature == "fail-on-warnings",
"Unexpected feature validation: {}",
feature_error.feature
);
}
}
}
}
#[switchy_async::test]
async fn test_cli_skip_features_with_negation() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: Some(vec!["*".to_string(), "!fail-on-warnings".to_string()]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
if !result.errors.is_empty() {
for error in &result.errors {
for feature_error in &error.errors {
assert_eq!(
feature_error.feature, "fail-on-warnings",
"Should only validate fail-on-warnings, found: {}",
feature_error.feature
);
}
}
}
}
#[switchy_async::test]
async fn test_cli_skip_features_specific_list() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: None,
skip_features: Some(vec![
"default".to_string(),
"test-utils".to_string(),
"test-fixtures".to_string(),
]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
if !result.errors.is_empty() {
for error in &result.errors {
for feature_error in &error.errors {
assert!(
feature_error.feature != "default"
&& feature_error.feature != "test-utils"
&& feature_error.feature != "test-fixtures",
"Should not validate skipped feature: {}",
feature_error.feature
);
}
}
}
}
#[switchy_async::test]
async fn test_cli_skip_features_combined_with_explicit_features() {
let workspace = create_multi_feature_workspace();
let config = ValidatorConfig {
features: Some(vec![
"default".to_string(),
"fail-on-warnings".to_string(),
"test-utils".to_string(),
]),
skip_features: Some(vec!["default".to_string(), "test-*".to_string()]),
workspace_only: true,
output_format: OutputType::Json,
strict_optional_propagation: false,
..ValidatorConfig::test_default()
};
let validator = FeatureValidator::new(Some(workspace.path().to_path_buf()), config).unwrap();
let result = validator.validate().unwrap();
if !result.errors.is_empty() {
for error in &result.errors {
for feature_error in &error.errors {
assert_eq!(
feature_error.feature, "fail-on-warnings",
"Only fail-on-warnings should be validated, found: {}",
feature_error.feature
);
}
}
}
}