Skip to content

Commit ddb82b3

Browse files
committed
test(linter): add rule configuration consistency test
1 parent b25406f commit ddb82b3

File tree

1 file changed

+84
-0
lines changed

1 file changed

+84
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
//! Test to ensure rule configuration consistency
2+
//!
3+
//! This test verifies that for all linter rules, the default configuration
4+
//! obtained via `Default::default()` matches the configuration obtained via
5+
//! `from_configuration(null)`. This helps prevent bugs where rules behave
6+
//! differently depending on how they are initialized.
7+
8+
use oxc_linter::rules::RULES;
9+
use serde_json::Value;
10+
11+
/// Test to ensure consistency between `rule::default()` and `rule::from_configuration(null)`.
12+
///
13+
/// This test helps prevent issues where a rule's default configuration differs from
14+
/// its configuration when initialized with a null value. Both should produce identical
15+
/// results to maintain consistency in rule behavior.
16+
///
17+
/// See issue #12718 for an example of why this test is important.
18+
#[test]
19+
fn test_rule_default_matches_from_configuration_null() {
20+
let mut failures = Vec::new();
21+
22+
// Rules that are known to have configuration mismatches and need to be fixed
23+
// TODO: These should be removed as the rules are fixed to have consistent defaults
24+
// When fixing a rule, ensure that either:
25+
// 1. The Default implementation returns the same values as from_configuration(null), or
26+
// 2. The from_configuration method is updated to return Default::default() when given null
27+
let exceptions = [
28+
"new-cap",
29+
"no-unneeded-ternary",
30+
"no-else-return",
31+
"extensions",
32+
"no-anonymous-default-export",
33+
"no-deprecated-functions",
34+
"no-large-snapshots",
35+
"prefer-lowercase-title",
36+
"jsx-filename-extension",
37+
"no-this-alias",
38+
"prefer-object-from-entries",
39+
"prefer-structured-clone",
40+
];
41+
42+
// Iterate through all available linter rules
43+
for rule in RULES.iter() {
44+
let rule_name = rule.name();
45+
46+
// Skip rules that are known to have issues
47+
if exceptions.contains(&rule_name) {
48+
continue;
49+
}
50+
51+
// Get the default configuration using rule::default
52+
let default_rule = rule.clone();
53+
54+
// Get the configuration using rule::from_configuration(null)
55+
let null_configured_rule = rule.read_json(Value::Null);
56+
57+
// Compare the two configurations
58+
// Since RuleEnum doesn't implement PartialEq for the inner rule types,
59+
// we need to compare them by running them and checking their behavior
60+
// For now, we'll use the debug representation as a proxy
61+
let default_debug = format!("{default_rule:?}");
62+
let null_debug = format!("{null_configured_rule:?}");
63+
64+
if default_debug != null_debug {
65+
failures.push(format!(
66+
"Rule '{rule_name}' has different configurations between default() and from_configuration(null).\n\
67+
Default: {default_debug}\n\
68+
From null: {null_debug}",
69+
70+
));
71+
}
72+
}
73+
74+
assert!(
75+
failures.is_empty(),
76+
"Found {} rules with configuration mismatches:\n\n{}\n\n\
77+
To fix these issues:\n\
78+
1. Update the Default implementation to match from_configuration(null), or\n\
79+
2. Update from_configuration to return Default::default() when given null\n\
80+
3. Add the rule to the exceptions list above if the mismatch is intentional",
81+
failures.len(),
82+
failures.join("\n\n")
83+
);
84+
}

0 commit comments

Comments
 (0)