use fallow_config::{ResolvedConfig, RulesConfig, Severity};
pub fn apply_rules(results: &mut fallow_core::results::AnalysisResults, config: &ResolvedConfig) {
let rules = &config.rules;
let has_overrides = !config.overrides.is_empty();
if has_overrides {
results
.unused_files
.retain(|f| config.resolve_rules_for_path(&f.path).unused_files != Severity::Off);
results
.unused_exports
.retain(|e| config.resolve_rules_for_path(&e.path).unused_exports != Severity::Off);
results
.unused_types
.retain(|e| config.resolve_rules_for_path(&e.path).unused_types != Severity::Off);
results.unused_enum_members.retain(|m| {
config.resolve_rules_for_path(&m.path).unused_enum_members != Severity::Off
});
results.unused_class_members.retain(|m| {
config.resolve_rules_for_path(&m.path).unused_class_members != Severity::Off
});
results
.unresolved_imports
.retain(|i| config.resolve_rules_for_path(&i.path).unresolved_imports != Severity::Off);
results
.stale_suppressions
.retain(|s| config.resolve_rules_for_path(&s.path).stale_suppressions != Severity::Off);
} else {
if rules.unused_files == Severity::Off {
results.unused_files.clear();
}
if rules.unused_exports == Severity::Off {
results.unused_exports.clear();
}
if rules.unused_types == Severity::Off {
results.unused_types.clear();
}
if rules.unused_enum_members == Severity::Off {
results.unused_enum_members.clear();
}
if rules.unused_class_members == Severity::Off {
results.unused_class_members.clear();
}
if rules.unresolved_imports == Severity::Off {
results.unresolved_imports.clear();
}
if rules.stale_suppressions == Severity::Off {
results.stale_suppressions.clear();
}
}
if rules.unused_dependencies == Severity::Off {
results.unused_dependencies.clear();
}
if rules.unused_dev_dependencies == Severity::Off {
results.unused_dev_dependencies.clear();
}
if rules.unused_optional_dependencies == Severity::Off {
results.unused_optional_dependencies.clear();
}
if rules.unlisted_dependencies == Severity::Off {
results.unlisted_dependencies.clear();
}
if rules.duplicate_exports == Severity::Off {
results.duplicate_exports.clear();
}
if rules.type_only_dependencies == Severity::Off {
results.type_only_dependencies.clear();
}
if rules.test_only_dependencies == Severity::Off {
results.test_only_dependencies.clear();
}
if rules.circular_dependencies == Severity::Off {
results.circular_dependencies.clear();
}
if rules.boundary_violation == Severity::Off {
results.boundary_violations.clear();
}
}
pub fn has_error_severity_issues(
results: &fallow_core::results::AnalysisResults,
rules: &RulesConfig,
config: Option<&ResolvedConfig>,
) -> bool {
let has_overrides = config.is_some_and(|c| !c.overrides.is_empty());
let file_scoped_errors = if has_overrides {
let config = config.unwrap();
results
.unused_files
.iter()
.any(|f| config.resolve_rules_for_path(&f.path).unused_files == Severity::Error)
|| results
.unused_exports
.iter()
.any(|e| config.resolve_rules_for_path(&e.path).unused_exports == Severity::Error)
|| results
.unused_types
.iter()
.any(|e| config.resolve_rules_for_path(&e.path).unused_types == Severity::Error)
|| results.unused_enum_members.iter().any(|m| {
config.resolve_rules_for_path(&m.path).unused_enum_members == Severity::Error
})
|| results.unused_class_members.iter().any(|m| {
config.resolve_rules_for_path(&m.path).unused_class_members == Severity::Error
})
|| results.unresolved_imports.iter().any(|i| {
config.resolve_rules_for_path(&i.path).unresolved_imports == Severity::Error
})
|| results.stale_suppressions.iter().any(|s| {
config.resolve_rules_for_path(&s.path).stale_suppressions == Severity::Error
})
} else {
(rules.unused_files == Severity::Error && !results.unused_files.is_empty())
|| (rules.unused_exports == Severity::Error && !results.unused_exports.is_empty())
|| (rules.unused_types == Severity::Error && !results.unused_types.is_empty())
|| (rules.unused_enum_members == Severity::Error
&& !results.unused_enum_members.is_empty())
|| (rules.unused_class_members == Severity::Error
&& !results.unused_class_members.is_empty())
|| (rules.unresolved_imports == Severity::Error
&& !results.unresolved_imports.is_empty())
|| (rules.stale_suppressions == Severity::Error
&& !results.stale_suppressions.is_empty())
};
file_scoped_errors
|| (rules.unused_dependencies == Severity::Error && !results.unused_dependencies.is_empty())
|| (rules.unused_dev_dependencies == Severity::Error
&& !results.unused_dev_dependencies.is_empty())
|| (rules.unused_optional_dependencies == Severity::Error
&& !results.unused_optional_dependencies.is_empty())
|| (rules.unlisted_dependencies == Severity::Error
&& !results.unlisted_dependencies.is_empty())
|| (rules.duplicate_exports == Severity::Error && !results.duplicate_exports.is_empty())
|| (rules.type_only_dependencies == Severity::Error
&& !results.type_only_dependencies.is_empty())
|| (rules.test_only_dependencies == Severity::Error
&& !results.test_only_dependencies.is_empty())
|| (rules.circular_dependencies == Severity::Error
&& !results.circular_dependencies.is_empty())
|| (rules.boundary_violation == Severity::Error && !results.boundary_violations.is_empty())
}
pub fn promote_warns_to_errors(rules: &mut RulesConfig) {
if rules.unused_files == Severity::Warn {
rules.unused_files = Severity::Error;
}
if rules.unused_exports == Severity::Warn {
rules.unused_exports = Severity::Error;
}
if rules.unused_types == Severity::Warn {
rules.unused_types = Severity::Error;
}
if rules.unused_dependencies == Severity::Warn {
rules.unused_dependencies = Severity::Error;
}
if rules.unused_dev_dependencies == Severity::Warn {
rules.unused_dev_dependencies = Severity::Error;
}
if rules.unused_optional_dependencies == Severity::Warn {
rules.unused_optional_dependencies = Severity::Error;
}
if rules.unused_enum_members == Severity::Warn {
rules.unused_enum_members = Severity::Error;
}
if rules.unused_class_members == Severity::Warn {
rules.unused_class_members = Severity::Error;
}
if rules.unresolved_imports == Severity::Warn {
rules.unresolved_imports = Severity::Error;
}
if rules.unlisted_dependencies == Severity::Warn {
rules.unlisted_dependencies = Severity::Error;
}
if rules.duplicate_exports == Severity::Warn {
rules.duplicate_exports = Severity::Error;
}
if rules.type_only_dependencies == Severity::Warn {
rules.type_only_dependencies = Severity::Error;
}
if rules.test_only_dependencies == Severity::Warn {
rules.test_only_dependencies = Severity::Error;
}
if rules.circular_dependencies == Severity::Warn {
rules.circular_dependencies = Severity::Error;
}
if rules.boundary_violation == Severity::Warn {
rules.boundary_violation = Severity::Error;
}
if rules.coverage_gaps == Severity::Warn {
rules.coverage_gaps = Severity::Error;
}
if rules.stale_suppressions == Severity::Warn {
rules.stale_suppressions = Severity::Error;
}
}
#[cfg(test)]
mod tests {
use super::*;
type RuleFieldSetter = fn(&mut RulesConfig);
type ResultFieldCheck = fn(&AnalysisResults) -> bool;
use fallow_core::extract::MemberKind;
use fallow_core::results::*;
use std::path::PathBuf;
fn make_results() -> AnalysisResults {
let mut r = AnalysisResults::default();
r.unused_files.push(UnusedFile {
path: PathBuf::from("/project/src/a.ts"),
});
r.unused_exports.push(UnusedExport {
path: PathBuf::from("/project/src/b.ts"),
export_name: "foo".into(),
is_type_only: false,
line: 1,
col: 0,
span_start: 0,
is_re_export: false,
});
r.unused_types.push(UnusedExport {
path: PathBuf::from("/project/src/c.ts"),
export_name: "MyType".into(),
is_type_only: true,
line: 5,
col: 0,
span_start: 0,
is_re_export: false,
});
r.unused_dependencies.push(UnusedDependency {
package_name: "lodash".into(),
location: DependencyLocation::Dependencies,
path: PathBuf::from("/project/package.json"),
line: 5,
});
r.unused_dev_dependencies.push(UnusedDependency {
package_name: "jest".into(),
location: DependencyLocation::DevDependencies,
path: PathBuf::from("/project/package.json"),
line: 5,
});
r.unused_enum_members.push(UnusedMember {
path: PathBuf::from("/project/src/d.ts"),
parent_name: "Status".into(),
member_name: "Pending".into(),
kind: MemberKind::EnumMember,
line: 3,
col: 0,
});
r.unused_class_members.push(UnusedMember {
path: PathBuf::from("/project/src/e.ts"),
parent_name: "Service".into(),
member_name: "helper".into(),
kind: MemberKind::ClassMethod,
line: 10,
col: 0,
});
r.unresolved_imports.push(UnresolvedImport {
path: PathBuf::from("/project/src/f.ts"),
specifier: "./missing".into(),
line: 1,
col: 0,
specifier_col: 0,
});
r.unlisted_dependencies.push(UnlistedDependency {
package_name: "chalk".into(),
imported_from: vec![ImportSite {
path: PathBuf::from("/project/src/g.ts"),
line: 1,
col: 0,
}],
});
r.duplicate_exports.push(DuplicateExport {
export_name: "helper".into(),
locations: vec![
DuplicateLocation {
path: PathBuf::from("/project/src/h.ts"),
line: 15,
col: 0,
},
DuplicateLocation {
path: PathBuf::from("/project/src/i.ts"),
line: 30,
col: 0,
},
],
});
r
}
fn config_with_rules(rules: RulesConfig) -> ResolvedConfig {
fallow_config::FallowConfig {
schema: None,
extends: vec![],
entry: vec![],
ignore_patterns: vec![],
framework: vec![],
workspaces: None,
ignore_dependencies: vec![],
ignore_exports: vec![],
used_class_members: vec![],
duplicates: fallow_config::DuplicatesConfig::default(),
health: fallow_config::HealthConfig::default(),
rules,
boundaries: fallow_config::BoundaryConfig::default(),
production: false,
plugins: vec![],
dynamically_loaded: vec![],
overrides: vec![],
regression: None,
codeowners: None,
public_packages: vec![],
flags: fallow_config::FlagsConfig::default(),
sealed: false,
}
.resolve(
PathBuf::from("/project"),
fallow_config::OutputFormat::Human,
1,
true,
true,
)
}
#[test]
fn apply_rules_default_error_preserves_all() {
let mut results = make_results();
let config = config_with_rules(RulesConfig::default());
let original_total = results.total_issues();
apply_rules(&mut results, &config);
assert_eq!(results.total_issues(), original_total);
}
#[test]
fn apply_rules_off_clears_that_issue_type() {
let mut results = make_results();
let rules = RulesConfig {
unused_files: Severity::Off,
..RulesConfig::default()
};
let config = config_with_rules(rules);
apply_rules(&mut results, &config);
assert!(results.unused_files.is_empty());
assert!(!results.unused_exports.is_empty());
}
#[test]
fn apply_rules_warn_preserves_issues() {
let mut results = make_results();
let rules = RulesConfig {
unused_exports: Severity::Warn,
..RulesConfig::default()
};
let config = config_with_rules(rules);
apply_rules(&mut results, &config);
assert_eq!(results.unused_exports.len(), 1);
}
#[test]
fn apply_rules_all_off_clears_everything() {
let mut results = make_results();
let rules = RulesConfig {
unused_files: Severity::Off,
unused_exports: Severity::Off,
unused_types: Severity::Off,
unused_dependencies: Severity::Off,
unused_dev_dependencies: Severity::Off,
unused_optional_dependencies: Severity::Off,
unused_enum_members: Severity::Off,
unused_class_members: Severity::Off,
unresolved_imports: Severity::Off,
unlisted_dependencies: Severity::Off,
duplicate_exports: Severity::Off,
type_only_dependencies: Severity::Off,
test_only_dependencies: Severity::Off,
boundary_violation: Severity::Error,
circular_dependencies: Severity::Off,
coverage_gaps: Severity::Off,
feature_flags: Severity::Off,
stale_suppressions: Severity::Off,
};
let config = config_with_rules(rules);
apply_rules(&mut results, &config);
assert_eq!(results.total_issues(), 0);
}
#[test]
fn apply_rules_off_each_type_individually() {
let field_setters: Vec<(RuleFieldSetter, ResultFieldCheck)> = vec![
(
|r| r.unused_files = Severity::Off,
|res| res.unused_files.is_empty(),
),
(
|r| r.unused_exports = Severity::Off,
|res| res.unused_exports.is_empty(),
),
(
|r| r.unused_types = Severity::Off,
|res| res.unused_types.is_empty(),
),
(
|r| r.unused_dependencies = Severity::Off,
|res| res.unused_dependencies.is_empty(),
),
(
|r| r.unused_dev_dependencies = Severity::Off,
|res| res.unused_dev_dependencies.is_empty(),
),
(
|r| r.unused_enum_members = Severity::Off,
|res| res.unused_enum_members.is_empty(),
),
(
|r| r.unused_class_members = Severity::Off,
|res| res.unused_class_members.is_empty(),
),
(
|r| r.unresolved_imports = Severity::Off,
|res| res.unresolved_imports.is_empty(),
),
(
|r| r.unlisted_dependencies = Severity::Off,
|res| res.unlisted_dependencies.is_empty(),
),
(
|r| r.duplicate_exports = Severity::Off,
|res| res.duplicate_exports.is_empty(),
),
];
for (set_off, check_empty) in field_setters {
let mut results = make_results();
let mut rules = RulesConfig::default();
set_off(&mut rules);
let config = config_with_rules(rules);
apply_rules(&mut results, &config);
assert!(
check_empty(&results),
"Setting a rule to Off should clear the corresponding results"
);
}
}
#[test]
fn empty_results_no_error_issues() {
let results = AnalysisResults::default();
let rules = RulesConfig::default();
assert!(!has_error_severity_issues(&results, &rules, None));
}
#[test]
fn error_severity_with_issues_returns_true() {
let results = make_results();
let rules = RulesConfig::default(); assert!(has_error_severity_issues(&results, &rules, None));
}
#[test]
fn warn_severity_with_issues_returns_false() {
let results = make_results();
let rules = RulesConfig {
unused_files: Severity::Warn,
unused_exports: Severity::Warn,
unused_types: Severity::Warn,
unused_dependencies: Severity::Warn,
unused_dev_dependencies: Severity::Warn,
unused_optional_dependencies: Severity::Warn,
unused_enum_members: Severity::Warn,
unused_class_members: Severity::Warn,
unresolved_imports: Severity::Warn,
unlisted_dependencies: Severity::Warn,
duplicate_exports: Severity::Warn,
type_only_dependencies: Severity::Warn,
test_only_dependencies: Severity::Warn,
boundary_violation: Severity::Error,
circular_dependencies: Severity::Warn,
coverage_gaps: Severity::Warn,
feature_flags: Severity::Warn,
stale_suppressions: Severity::Warn,
};
assert!(!has_error_severity_issues(&results, &rules, None));
}
#[test]
fn mixed_severity_returns_true_for_error_with_issues() {
let mut results = AnalysisResults::default();
results.unused_files.push(UnusedFile {
path: PathBuf::from("/project/src/a.ts"),
});
let mut rules = RulesConfig {
unused_files: Severity::Warn,
unused_exports: Severity::Warn,
unused_types: Severity::Warn,
unused_dependencies: Severity::Warn,
unused_dev_dependencies: Severity::Warn,
unused_optional_dependencies: Severity::Warn,
unused_enum_members: Severity::Warn,
unused_class_members: Severity::Warn,
unresolved_imports: Severity::Warn,
unlisted_dependencies: Severity::Warn,
duplicate_exports: Severity::Warn,
type_only_dependencies: Severity::Warn,
test_only_dependencies: Severity::Warn,
boundary_violation: Severity::Error,
circular_dependencies: Severity::Warn,
coverage_gaps: Severity::Warn,
feature_flags: Severity::Warn,
stale_suppressions: Severity::Warn,
};
assert!(!has_error_severity_issues(&results, &rules, None));
rules.unused_files = Severity::Error;
assert!(has_error_severity_issues(&results, &rules, None));
}
#[test]
fn off_severity_with_issues_returns_false() {
let mut results = AnalysisResults::default();
results.unresolved_imports.push(UnresolvedImport {
path: PathBuf::from("/project/src/a.ts"),
specifier: "./missing".into(),
line: 1,
col: 0,
specifier_col: 0,
});
let rules = RulesConfig {
unresolved_imports: Severity::Off,
..RulesConfig::default()
};
assert!(!has_error_severity_issues(&results, &rules, None));
}
fn config_with_test_override() -> ResolvedConfig {
fallow_config::FallowConfig {
schema: None,
extends: vec![],
entry: vec![],
ignore_patterns: vec![],
framework: vec![],
workspaces: None,
ignore_dependencies: vec![],
ignore_exports: vec![],
used_class_members: vec![],
duplicates: fallow_config::DuplicatesConfig::default(),
health: fallow_config::HealthConfig::default(),
rules: RulesConfig::default(), boundaries: fallow_config::BoundaryConfig::default(),
production: false,
plugins: vec![],
dynamically_loaded: vec![],
regression: None,
codeowners: None,
public_packages: vec![],
flags: fallow_config::FlagsConfig::default(),
sealed: false,
overrides: vec![fallow_config::ConfigOverride {
files: vec!["**/*.test.ts".to_string()],
rules: fallow_config::PartialRulesConfig {
unused_exports: Some(Severity::Off),
..Default::default()
},
}],
}
.resolve(
PathBuf::from("/project"),
fallow_config::OutputFormat::Human,
1,
true,
true,
)
}
#[test]
fn apply_rules_with_override_filters_matching_files() {
let mut results = AnalysisResults::default();
results.unused_exports.push(UnusedExport {
path: PathBuf::from("/project/src/utils.test.ts"),
export_name: "testHelper".into(),
is_type_only: false,
line: 1,
col: 0,
span_start: 0,
is_re_export: false,
});
results.unused_exports.push(UnusedExport {
path: PathBuf::from("/project/src/utils.ts"),
export_name: "realExport".into(),
is_type_only: false,
line: 5,
col: 0,
span_start: 0,
is_re_export: false,
});
let config = config_with_test_override();
apply_rules(&mut results, &config);
assert_eq!(results.unused_exports.len(), 1);
assert_eq!(results.unused_exports[0].export_name, "realExport");
}
#[test]
fn apply_rules_with_override_preserves_non_matching_files() {
let mut results = AnalysisResults::default();
results.unused_files.push(UnusedFile {
path: PathBuf::from("/project/src/dead.ts"),
});
let config = config_with_test_override();
apply_rules(&mut results, &config);
assert_eq!(results.unused_files.len(), 1);
}
#[test]
fn has_error_with_override_per_file_resolution() {
let mut results = AnalysisResults::default();
results.unused_exports.push(UnusedExport {
path: PathBuf::from("/project/src/utils.test.ts"),
export_name: "testHelper".into(),
is_type_only: false,
line: 1,
col: 0,
span_start: 0,
is_re_export: false,
});
let config = config_with_test_override();
let rules = &config.rules;
assert!(
!has_error_severity_issues(&results, rules, Some(&config)),
"test file override should suppress error"
);
}
#[test]
fn has_error_with_override_non_matching_file_still_error() {
let mut results = AnalysisResults::default();
results.unused_exports.push(UnusedExport {
path: PathBuf::from("/project/src/utils.ts"),
export_name: "realExport".into(),
is_type_only: false,
line: 1,
col: 0,
span_start: 0,
is_re_export: false,
});
let config = config_with_test_override();
let rules = &config.rules;
assert!(
has_error_severity_issues(&results, rules, Some(&config)),
"non-test file should still have Error severity"
);
}
#[test]
fn promote_warns_to_errors_promotes_all_warns() {
let mut rules = RulesConfig {
unused_files: Severity::Warn,
unused_exports: Severity::Warn,
unused_types: Severity::Warn,
unused_dependencies: Severity::Warn,
unused_dev_dependencies: Severity::Warn,
unused_optional_dependencies: Severity::Warn,
unused_enum_members: Severity::Warn,
unused_class_members: Severity::Warn,
unresolved_imports: Severity::Warn,
unlisted_dependencies: Severity::Warn,
duplicate_exports: Severity::Warn,
type_only_dependencies: Severity::Warn,
test_only_dependencies: Severity::Warn,
boundary_violation: Severity::Error,
circular_dependencies: Severity::Warn,
coverage_gaps: Severity::Warn,
feature_flags: Severity::Warn,
stale_suppressions: Severity::Warn,
};
promote_warns_to_errors(&mut rules);
assert_eq!(rules.unused_files, Severity::Error);
assert_eq!(rules.unused_exports, Severity::Error);
assert_eq!(rules.unused_types, Severity::Error);
assert_eq!(rules.unused_dependencies, Severity::Error);
assert_eq!(rules.unused_dev_dependencies, Severity::Error);
assert_eq!(rules.unused_optional_dependencies, Severity::Error);
assert_eq!(rules.unused_enum_members, Severity::Error);
assert_eq!(rules.unused_class_members, Severity::Error);
assert_eq!(rules.unresolved_imports, Severity::Error);
assert_eq!(rules.unlisted_dependencies, Severity::Error);
assert_eq!(rules.duplicate_exports, Severity::Error);
assert_eq!(rules.type_only_dependencies, Severity::Error);
assert_eq!(rules.test_only_dependencies, Severity::Error);
assert_eq!(rules.circular_dependencies, Severity::Error);
assert_eq!(rules.coverage_gaps, Severity::Error);
}
#[test]
fn promote_warns_to_errors_preserves_off() {
let mut rules = RulesConfig {
unused_files: Severity::Off,
unused_exports: Severity::Off,
unused_types: Severity::Off,
unused_dependencies: Severity::Off,
unused_dev_dependencies: Severity::Off,
unused_optional_dependencies: Severity::Off,
unused_enum_members: Severity::Off,
unused_class_members: Severity::Off,
unresolved_imports: Severity::Off,
unlisted_dependencies: Severity::Off,
duplicate_exports: Severity::Off,
type_only_dependencies: Severity::Off,
test_only_dependencies: Severity::Off,
boundary_violation: Severity::Error,
circular_dependencies: Severity::Off,
coverage_gaps: Severity::Off,
feature_flags: Severity::Off,
stale_suppressions: Severity::Off,
};
promote_warns_to_errors(&mut rules);
assert_eq!(rules.unused_files, Severity::Off);
assert_eq!(rules.unused_exports, Severity::Off);
assert_eq!(rules.unused_types, Severity::Off);
assert_eq!(rules.circular_dependencies, Severity::Off);
assert_eq!(rules.coverage_gaps, Severity::Off);
}
#[test]
fn promote_warns_to_errors_preserves_existing_errors() {
let mut rules = RulesConfig::default(); promote_warns_to_errors(&mut rules);
assert_eq!(rules.unused_files, Severity::Error);
assert_eq!(rules.unused_exports, Severity::Error);
}
#[test]
fn promote_warns_to_errors_mixed_severities() {
let mut rules = RulesConfig {
unused_files: Severity::Error,
unused_exports: Severity::Warn,
unused_types: Severity::Off,
..RulesConfig::default()
};
promote_warns_to_errors(&mut rules);
assert_eq!(rules.unused_files, Severity::Error);
assert_eq!(rules.unused_exports, Severity::Error);
assert_eq!(rules.unused_types, Severity::Off);
}
#[test]
fn has_error_circular_deps_detected() {
let mut results = AnalysisResults::default();
results.circular_dependencies.push(CircularDependency {
files: vec![
PathBuf::from("/project/src/a.ts"),
PathBuf::from("/project/src/b.ts"),
],
length: 2,
line: 1,
col: 0,
is_cross_package: false,
});
let rules = RulesConfig::default();
assert!(has_error_severity_issues(&results, &rules, None));
}
#[test]
fn has_error_circular_deps_warn_not_detected() {
let mut results = AnalysisResults::default();
results.circular_dependencies.push(CircularDependency {
files: vec![
PathBuf::from("/project/src/a.ts"),
PathBuf::from("/project/src/b.ts"),
],
length: 2,
line: 1,
col: 0,
is_cross_package: false,
});
let rules = RulesConfig {
circular_dependencies: Severity::Warn,
..RulesConfig::default()
};
assert!(!has_error_severity_issues(&results, &rules, None));
}
#[test]
fn has_error_optional_deps_warn_by_default() {
let mut results = AnalysisResults::default();
results.unused_optional_dependencies.push(UnusedDependency {
package_name: "optional-pkg".into(),
location: DependencyLocation::OptionalDependencies,
path: PathBuf::from("/project/package.json"),
line: 5,
});
let rules = RulesConfig::default();
assert!(!has_error_severity_issues(&results, &rules, None));
}
#[test]
fn has_error_optional_deps_detected_when_error() {
let mut results = AnalysisResults::default();
results.unused_optional_dependencies.push(UnusedDependency {
package_name: "optional-pkg".into(),
location: DependencyLocation::OptionalDependencies,
path: PathBuf::from("/project/package.json"),
line: 5,
});
let rules = RulesConfig {
unused_optional_dependencies: Severity::Error,
..RulesConfig::default()
};
assert!(has_error_severity_issues(&results, &rules, None));
}
#[test]
fn has_error_type_only_deps_warn_by_default() {
let mut results = AnalysisResults::default();
results.type_only_dependencies.push(TypeOnlyDependency {
package_name: "zod".into(),
path: PathBuf::from("/project/package.json"),
line: 8,
});
let rules = RulesConfig::default();
assert!(!has_error_severity_issues(&results, &rules, None));
}
#[test]
fn has_error_type_only_deps_detected_when_error() {
let mut results = AnalysisResults::default();
results.type_only_dependencies.push(TypeOnlyDependency {
package_name: "zod".into(),
path: PathBuf::from("/project/package.json"),
line: 8,
});
let rules = RulesConfig {
type_only_dependencies: Severity::Error,
..RulesConfig::default()
};
assert!(has_error_severity_issues(&results, &rules, None));
}
}