@owenlamont/ryl 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/CODEOWNERS +1 -0
- package/.github/dependabot.yml +13 -0
- package/.github/workflows/ci.yml +107 -0
- package/.github/workflows/release.yml +613 -0
- package/.github/workflows/update_dependencies.yml +61 -0
- package/.github/workflows/update_linters.yml +56 -0
- package/.pre-commit-config.yaml +87 -0
- package/.yamllint +4 -0
- package/AGENTS.md +200 -0
- package/Cargo.lock +908 -0
- package/Cargo.toml +32 -0
- package/LICENSE +21 -0
- package/README.md +230 -0
- package/bin/ryl.js +1 -0
- package/clippy.toml +1 -0
- package/docs/config-presets.md +100 -0
- package/img/benchmark-5x5-5runs.svg +2176 -0
- package/package.json +28 -0
- package/pyproject.toml +42 -0
- package/ruff.toml +107 -0
- package/rumdl.toml +20 -0
- package/rust-toolchain.toml +3 -0
- package/rustfmt.toml +3 -0
- package/scripts/benchmark_perf_vs_yamllint.py +400 -0
- package/scripts/coverage-missing.ps1 +80 -0
- package/scripts/coverage-missing.sh +60 -0
- package/src/bin/discover_config_bin.rs +24 -0
- package/src/cli_support.rs +33 -0
- package/src/conf/mod.rs +85 -0
- package/src/config.rs +2099 -0
- package/src/decoder.rs +326 -0
- package/src/discover.rs +31 -0
- package/src/lib.rs +19 -0
- package/src/lint.rs +558 -0
- package/src/main.rs +535 -0
- package/src/migrate.rs +233 -0
- package/src/rules/anchors.rs +517 -0
- package/src/rules/braces.rs +77 -0
- package/src/rules/brackets.rs +77 -0
- package/src/rules/colons.rs +475 -0
- package/src/rules/commas.rs +372 -0
- package/src/rules/comments.rs +299 -0
- package/src/rules/comments_indentation.rs +243 -0
- package/src/rules/document_end.rs +175 -0
- package/src/rules/document_start.rs +84 -0
- package/src/rules/empty_lines.rs +152 -0
- package/src/rules/empty_values.rs +255 -0
- package/src/rules/float_values.rs +259 -0
- package/src/rules/flow_collection.rs +562 -0
- package/src/rules/hyphens.rs +104 -0
- package/src/rules/indentation.rs +803 -0
- package/src/rules/key_duplicates.rs +218 -0
- package/src/rules/key_ordering.rs +303 -0
- package/src/rules/line_length.rs +326 -0
- package/src/rules/mod.rs +25 -0
- package/src/rules/new_line_at_end_of_file.rs +23 -0
- package/src/rules/new_lines.rs +95 -0
- package/src/rules/octal_values.rs +121 -0
- package/src/rules/quoted_strings.rs +577 -0
- package/src/rules/span_utils.rs +37 -0
- package/src/rules/trailing_spaces.rs +65 -0
- package/src/rules/truthy.rs +420 -0
- package/tests/brackets_carriage_return.rs +114 -0
- package/tests/build_global_cfg_error.rs +23 -0
- package/tests/cli_anchors_rule.rs +143 -0
- package/tests/cli_braces_rule.rs +104 -0
- package/tests/cli_brackets_rule.rs +104 -0
- package/tests/cli_colons_rule.rs +65 -0
- package/tests/cli_commas_rule.rs +104 -0
- package/tests/cli_comments_indentation_rule.rs +61 -0
- package/tests/cli_comments_rule.rs +67 -0
- package/tests/cli_config_data_error.rs +30 -0
- package/tests/cli_config_flags.rs +66 -0
- package/tests/cli_config_migrate.rs +229 -0
- package/tests/cli_document_end_rule.rs +92 -0
- package/tests/cli_document_start_rule.rs +92 -0
- package/tests/cli_empty_lines_rule.rs +87 -0
- package/tests/cli_empty_values_rule.rs +68 -0
- package/tests/cli_env_config.rs +34 -0
- package/tests/cli_exit_and_errors.rs +41 -0
- package/tests/cli_file_encoding.rs +203 -0
- package/tests/cli_float_values_rule.rs +64 -0
- package/tests/cli_format_options.rs +316 -0
- package/tests/cli_global_cfg_relaxed.rs +20 -0
- package/tests/cli_hyphens_rule.rs +104 -0
- package/tests/cli_indentation_rule.rs +65 -0
- package/tests/cli_invalid_project_config.rs +39 -0
- package/tests/cli_key_duplicates_rule.rs +104 -0
- package/tests/cli_key_ordering_rule.rs +59 -0
- package/tests/cli_line_length_rule.rs +85 -0
- package/tests/cli_list_files.rs +29 -0
- package/tests/cli_new_line_rule.rs +141 -0
- package/tests/cli_new_lines_rule.rs +119 -0
- package/tests/cli_octal_values_rule.rs +60 -0
- package/tests/cli_quoted_strings_rule.rs +47 -0
- package/tests/cli_toml_config.rs +119 -0
- package/tests/cli_trailing_spaces_rule.rs +77 -0
- package/tests/cli_truthy_rule.rs +83 -0
- package/tests/cli_yaml_files_negation.rs +45 -0
- package/tests/colons_rule.rs +303 -0
- package/tests/common/compat.rs +114 -0
- package/tests/common/fake_env.rs +93 -0
- package/tests/common/mod.rs +1 -0
- package/tests/conf_builtin.rs +9 -0
- package/tests/config_anchors.rs +84 -0
- package/tests/config_braces.rs +121 -0
- package/tests/config_brackets.rs +127 -0
- package/tests/config_commas.rs +79 -0
- package/tests/config_comments.rs +65 -0
- package/tests/config_comments_indentation.rs +20 -0
- package/tests/config_deep_merge_nonstring_key.rs +24 -0
- package/tests/config_document_end.rs +54 -0
- package/tests/config_document_start.rs +55 -0
- package/tests/config_empty_lines.rs +48 -0
- package/tests/config_empty_values.rs +35 -0
- package/tests/config_env_errors.rs +23 -0
- package/tests/config_env_invalid_inline.rs +15 -0
- package/tests/config_env_missing.rs +63 -0
- package/tests/config_env_shim.rs +301 -0
- package/tests/config_explicit_file_parse_error.rs +55 -0
- package/tests/config_extended_features.rs +225 -0
- package/tests/config_extends_inline.rs +185 -0
- package/tests/config_extends_sequence.rs +18 -0
- package/tests/config_find_project_home_boundary.rs +54 -0
- package/tests/config_find_project_two_files_in_cwd.rs +47 -0
- package/tests/config_float_values.rs +34 -0
- package/tests/config_from_yaml_paths.rs +32 -0
- package/tests/config_hyphens.rs +51 -0
- package/tests/config_ignore_errors.rs +243 -0
- package/tests/config_ignore_overrides.rs +83 -0
- package/tests/config_indentation.rs +65 -0
- package/tests/config_invalid_globs.rs +16 -0
- package/tests/config_invalid_types.rs +19 -0
- package/tests/config_key_duplicates.rs +34 -0
- package/tests/config_key_ordering.rs +70 -0
- package/tests/config_line_length.rs +65 -0
- package/tests/config_locale.rs +111 -0
- package/tests/config_merge.rs +26 -0
- package/tests/config_new_lines.rs +89 -0
- package/tests/config_octal_values.rs +33 -0
- package/tests/config_quoted_strings.rs +195 -0
- package/tests/config_rule_level.rs +147 -0
- package/tests/config_rules_non_string_keys.rs +23 -0
- package/tests/config_scalar_overrides.rs +27 -0
- package/tests/config_to_toml.rs +110 -0
- package/tests/config_toml_coverage.rs +80 -0
- package/tests/config_toml_discovery.rs +304 -0
- package/tests/config_trailing_spaces.rs +152 -0
- package/tests/config_truthy.rs +77 -0
- package/tests/config_yaml_files.rs +62 -0
- package/tests/config_yaml_files_all_non_string.rs +15 -0
- package/tests/config_yaml_files_empty.rs +30 -0
- package/tests/coverage_commas.rs +46 -0
- package/tests/decoder_decode.rs +338 -0
- package/tests/discover_config_bin_all.rs +66 -0
- package/tests/discover_config_bin_env_invalid_yaml.rs +26 -0
- package/tests/discover_config_bin_project_config_parse_error.rs +24 -0
- package/tests/discover_config_bin_user_global_error.rs +26 -0
- package/tests/discover_module.rs +30 -0
- package/tests/discover_per_file_dir.rs +10 -0
- package/tests/discover_per_file_project_config_error.rs +21 -0
- package/tests/float_values.rs +43 -0
- package/tests/lint_multi_errors.rs +32 -0
- package/tests/main_yaml_ok_filtering.rs +30 -0
- package/tests/migrate_module.rs +259 -0
- package/tests/resolve_ctx_empty_parent.rs +16 -0
- package/tests/rule_anchors.rs +442 -0
- package/tests/rule_braces.rs +258 -0
- package/tests/rule_brackets.rs +217 -0
- package/tests/rule_commas.rs +205 -0
- package/tests/rule_comments.rs +197 -0
- package/tests/rule_comments_indentation.rs +127 -0
- package/tests/rule_document_end.rs +118 -0
- package/tests/rule_document_start.rs +60 -0
- package/tests/rule_empty_lines.rs +96 -0
- package/tests/rule_empty_values.rs +102 -0
- package/tests/rule_float_values.rs +109 -0
- package/tests/rule_hyphens.rs +65 -0
- package/tests/rule_indentation.rs +455 -0
- package/tests/rule_key_duplicates.rs +76 -0
- package/tests/rule_key_ordering.rs +207 -0
- package/tests/rule_line_length.rs +200 -0
- package/tests/rule_new_lines.rs +51 -0
- package/tests/rule_octal_values.rs +53 -0
- package/tests/rule_quoted_strings.rs +290 -0
- package/tests/rule_trailing_spaces.rs +41 -0
- package/tests/rule_truthy.rs +236 -0
- package/tests/user_global_invalid_yaml.rs +32 -0
- package/tests/yamllint_compat_anchors.rs +280 -0
- package/tests/yamllint_compat_braces.rs +411 -0
- package/tests/yamllint_compat_brackets.rs +364 -0
- package/tests/yamllint_compat_colons.rs +298 -0
- package/tests/yamllint_compat_colors.rs +80 -0
- package/tests/yamllint_compat_commas.rs +375 -0
- package/tests/yamllint_compat_comments.rs +167 -0
- package/tests/yamllint_compat_comments_indentation.rs +281 -0
- package/tests/yamllint_compat_config.rs +170 -0
- package/tests/yamllint_compat_document_end.rs +243 -0
- package/tests/yamllint_compat_document_start.rs +136 -0
- package/tests/yamllint_compat_empty_lines.rs +117 -0
- package/tests/yamllint_compat_empty_values.rs +179 -0
- package/tests/yamllint_compat_float_values.rs +216 -0
- package/tests/yamllint_compat_hyphens.rs +223 -0
- package/tests/yamllint_compat_indentation.rs +398 -0
- package/tests/yamllint_compat_key_duplicates.rs +139 -0
- package/tests/yamllint_compat_key_ordering.rs +170 -0
- package/tests/yamllint_compat_line_length.rs +375 -0
- package/tests/yamllint_compat_list.rs +127 -0
- package/tests/yamllint_compat_new_line.rs +133 -0
- package/tests/yamllint_compat_newline_types.rs +185 -0
- package/tests/yamllint_compat_octal_values.rs +172 -0
- package/tests/yamllint_compat_quoted_strings.rs +154 -0
- package/tests/yamllint_compat_syntax.rs +200 -0
- package/tests/yamllint_compat_trailing_spaces.rs +162 -0
- package/tests/yamllint_compat_truthy.rs +130 -0
- package/tests/yamllint_compat_yaml_files.rs +81 -0
- package/typos.toml +2 -0
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
use ryl::config::YamlLintConfig;
|
|
2
|
+
use ryl::rules::key_ordering::{self, Config};
|
|
3
|
+
|
|
4
|
+
fn build_config(yaml: &str) -> Config {
|
|
5
|
+
let cfg = YamlLintConfig::from_yaml_str(yaml).expect("config parses");
|
|
6
|
+
Config::resolve(&cfg)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn reports_block_mapping_out_of_order() {
|
|
11
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
12
|
+
let input = "---\nblock mapping:\n secondkey: a\n firstkey: b\n";
|
|
13
|
+
let hits = key_ordering::check(input, &cfg);
|
|
14
|
+
assert_eq!(hits.len(), 1, "expected single violation: {hits:?}");
|
|
15
|
+
let hit = &hits[0];
|
|
16
|
+
assert_eq!(hit.line, 4);
|
|
17
|
+
assert_eq!(hit.column, 3);
|
|
18
|
+
assert_eq!(hit.message, "wrong ordering of key \"firstkey\" in mapping");
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
#[test]
|
|
22
|
+
fn reports_flow_mapping_out_of_order() {
|
|
23
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
24
|
+
let input = "---\nflow mapping:\n {secondkey: a, firstkey: b}\n";
|
|
25
|
+
let hits = key_ordering::check(input, &cfg);
|
|
26
|
+
assert_eq!(hits.len(), 1, "expected single violation: {hits:?}");
|
|
27
|
+
let hit = &hits[0];
|
|
28
|
+
assert_eq!(hit.line, 3);
|
|
29
|
+
assert_eq!(hit.column, 18);
|
|
30
|
+
assert_eq!(hit.message, "wrong ordering of key \"firstkey\" in mapping");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn ordered_mapping_passes() {
|
|
35
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
36
|
+
let input = "---\nfirst: 1\nsecond: 2\nthird: 3\n";
|
|
37
|
+
let hits = key_ordering::check(input, &cfg);
|
|
38
|
+
assert!(
|
|
39
|
+
hits.is_empty(),
|
|
40
|
+
"ordered mapping should be accepted: {hits:?}"
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#[test]
|
|
45
|
+
fn ignored_keys_are_not_enforced() {
|
|
46
|
+
let cfg = build_config(
|
|
47
|
+
"rules:\n key-ordering:\n ignored-keys: [\"n(a|o)me\", \"^b\"]\n",
|
|
48
|
+
);
|
|
49
|
+
let input = "---\na:\nb:\nc:\nname: ignored\nfirst-name: ignored\nnome: ignored\ngnomes: ignored\nd:\ne:\nboat: ignored\n.boat: ERROR\ncall: ERROR\nf:\ng:\n";
|
|
50
|
+
let hits = key_ordering::check(input, &cfg);
|
|
51
|
+
assert_eq!(
|
|
52
|
+
hits.len(),
|
|
53
|
+
2,
|
|
54
|
+
"expected violations for keys outside ignore filters"
|
|
55
|
+
);
|
|
56
|
+
assert_eq!(hits[0].line, 12);
|
|
57
|
+
assert_eq!(hits[0].column, 1);
|
|
58
|
+
assert_eq!(
|
|
59
|
+
hits[0].message,
|
|
60
|
+
"wrong ordering of key \".boat\" in mapping"
|
|
61
|
+
);
|
|
62
|
+
assert_eq!(hits[1].line, 13);
|
|
63
|
+
assert_eq!(hits[1].column, 1);
|
|
64
|
+
assert_eq!(hits[1].message, "wrong ordering of key \"call\" in mapping");
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
#[test]
|
|
68
|
+
fn locale_enables_case_and_accent_friendly_ordering() {
|
|
69
|
+
let ascii_cfg = build_config("rules:\n key-ordering: enable\n");
|
|
70
|
+
let locale_cfg =
|
|
71
|
+
build_config("locale: en_US.UTF-8\nrules:\n key-ordering: enable\n");
|
|
72
|
+
let bare_locale_cfg =
|
|
73
|
+
build_config("locale: en_US\nrules:\n key-ordering: enable\n");
|
|
74
|
+
|
|
75
|
+
let case_input = "---\nT-shirt: 1\nT-shirts: 2\nt-shirt: 3\nt-shirts: 4\n";
|
|
76
|
+
let case_hits = key_ordering::check(case_input, &ascii_cfg);
|
|
77
|
+
assert!(
|
|
78
|
+
case_hits.is_empty(),
|
|
79
|
+
"uppercase-first ordering should pass: {case_hits:?}"
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
let case_fail = "---\nt-shirt: 1\nT-shirt: 2\nt-shirts: 3\nT-shirts: 4\n";
|
|
83
|
+
let ascii_fail = key_ordering::check(case_fail, &ascii_cfg);
|
|
84
|
+
assert!(
|
|
85
|
+
!ascii_fail.is_empty(),
|
|
86
|
+
"expected ascii failure when lowercase precedes uppercase"
|
|
87
|
+
);
|
|
88
|
+
assert!(
|
|
89
|
+
ascii_fail
|
|
90
|
+
.iter()
|
|
91
|
+
.any(|hit| hit.line == 3 && hit.message.contains("T-shirt")),
|
|
92
|
+
"expected violation on uppercase key: {ascii_fail:?}"
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
let locale_pass = key_ordering::check(case_fail, &locale_cfg);
|
|
96
|
+
assert!(
|
|
97
|
+
locale_pass.is_empty(),
|
|
98
|
+
"locale-aware comparison should accept case-insensitive order: {locale_pass:?}"
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
let bare_locale_pass = key_ordering::check(case_fail, &bare_locale_cfg);
|
|
102
|
+
assert!(
|
|
103
|
+
bare_locale_pass.is_empty(),
|
|
104
|
+
"locale-aware comparison should accept bare locale strings: {bare_locale_pass:?}"
|
|
105
|
+
);
|
|
106
|
+
|
|
107
|
+
let accent_fail = "---\nhaïr: true\nhais: true\n";
|
|
108
|
+
let ascii_accent = key_ordering::check(accent_fail, &ascii_cfg);
|
|
109
|
+
assert_eq!(ascii_accent.len(), 1, "expected accent-sensitive failure");
|
|
110
|
+
assert_eq!(ascii_accent[0].line, 3);
|
|
111
|
+
assert_eq!(ascii_accent[0].column, 1);
|
|
112
|
+
|
|
113
|
+
let accent_pass = "---\nhair: true\nhaïr: true\nhais: true\nhaïssable: true\n";
|
|
114
|
+
let locale_accent = key_ordering::check(accent_pass, &locale_cfg);
|
|
115
|
+
assert!(
|
|
116
|
+
locale_accent.is_empty(),
|
|
117
|
+
"locale-aware comparison should accept accent-friendly order: {locale_accent:?}"
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
let mixed_input = "---\n- t-shirt: 1\n T-shirt: 2\n t-shirts: 3\n T-shirts: 4\n- hair: true\n haïr: true\n hais: true\n haïssable: true\n";
|
|
121
|
+
let locale_mixed = key_ordering::check(mixed_input, &locale_cfg);
|
|
122
|
+
assert!(
|
|
123
|
+
locale_mixed.is_empty(),
|
|
124
|
+
"locale-aware comparison should tolerate mixed case/accent segments: {locale_mixed:?}"
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
#[test]
|
|
129
|
+
fn locale_still_enforces_order_within_single_mapping() {
|
|
130
|
+
let cfg = build_config("locale: en_US.UTF-8\nrules:\n key-ordering: enable\n");
|
|
131
|
+
let input = "---\nt-shirt: 1\nT-shirt: 2\nt-shirts: 3\nT-shirts: 4\nhair: true\nhaïr: true\nhais: true\nhaïssable: true\n";
|
|
132
|
+
let hits = key_ordering::check(input, &cfg);
|
|
133
|
+
assert_eq!(
|
|
134
|
+
hits.len(),
|
|
135
|
+
4,
|
|
136
|
+
"single mapping should report out-of-order keys even with locale"
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
#[test]
|
|
141
|
+
fn ignored_keys_accepts_scalar_configuration() {
|
|
142
|
+
let raw = YamlLintConfig::from_yaml_str(
|
|
143
|
+
"rules:\n key-ordering:\n ignored-keys: \"name\"\n",
|
|
144
|
+
)
|
|
145
|
+
.expect("config parses");
|
|
146
|
+
let option = raw
|
|
147
|
+
.rule_option(key_ordering::ID, "ignored-keys")
|
|
148
|
+
.expect("option present");
|
|
149
|
+
assert!(option.as_str().is_some(), "ignored-keys should be scalar");
|
|
150
|
+
let cfg = key_ordering::Config::resolve(&raw);
|
|
151
|
+
let hits = key_ordering::check("name: 1\nalpha: 2\n", &cfg);
|
|
152
|
+
assert!(
|
|
153
|
+
hits.is_empty(),
|
|
154
|
+
"scalar ignored key should be skipped: {hits:?}"
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
#[test]
|
|
159
|
+
fn c_locale_devolves_to_codepoint_ordering() {
|
|
160
|
+
let raw = YamlLintConfig::from_yaml_str(
|
|
161
|
+
"locale: C.UTF-8\nrules:\n key-ordering: enable\n",
|
|
162
|
+
)
|
|
163
|
+
.expect("config parses");
|
|
164
|
+
assert_eq!(raw.locale(), Some("C.UTF-8"));
|
|
165
|
+
let cfg = key_ordering::Config::resolve(&raw);
|
|
166
|
+
let hits = key_ordering::check("t-shirt: 1\nT-shirt: 2\n", &cfg);
|
|
167
|
+
assert!(
|
|
168
|
+
hits.iter().any(|hit| hit.message.contains("T-shirt")),
|
|
169
|
+
"C locale should behave like default ordering: {hits:?}"
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
#[test]
|
|
174
|
+
fn sequence_elements_are_checked_inside_lists() {
|
|
175
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
176
|
+
let input = "---\n- second: 1\n first: 0\n";
|
|
177
|
+
let hits = key_ordering::check(input, &cfg);
|
|
178
|
+
assert_eq!(
|
|
179
|
+
hits.len(),
|
|
180
|
+
1,
|
|
181
|
+
"expected violation inside sequence: {hits:?}"
|
|
182
|
+
);
|
|
183
|
+
assert_eq!(hits[0].line, 3);
|
|
184
|
+
assert_eq!(hits[0].column, 3);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
#[test]
|
|
188
|
+
fn sequences_of_scalars_are_ignored() {
|
|
189
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
190
|
+
let input = "---\n- bravo\n- alpha\n- charlie\n";
|
|
191
|
+
let hits = key_ordering::check(input, &cfg);
|
|
192
|
+
assert!(
|
|
193
|
+
hits.is_empty(),
|
|
194
|
+
"scalar sequences should not be checked: {hits:?}"
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
#[test]
|
|
199
|
+
fn complex_sequence_keys_do_not_break_tracking() {
|
|
200
|
+
let cfg = build_config("rules:\n key-ordering: enable\n");
|
|
201
|
+
let input = "---\n? [b, a]\n: value\n? {c: 1, d: 2}\n: other\n";
|
|
202
|
+
let hits = key_ordering::check(input, &cfg);
|
|
203
|
+
assert!(
|
|
204
|
+
hits.is_empty(),
|
|
205
|
+
"complex keys should not produce diagnostics: {hits:?}"
|
|
206
|
+
);
|
|
207
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
use ryl::config::YamlLintConfig;
|
|
2
|
+
use ryl::rules::line_length::{self, Config, Violation};
|
|
3
|
+
|
|
4
|
+
fn build_config(yaml: &str) -> Config {
|
|
5
|
+
let cfg = YamlLintConfig::from_yaml_str(yaml).expect("config parses");
|
|
6
|
+
Config::resolve(&cfg)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn flags_lines_longer_than_max() {
|
|
11
|
+
let resolved = build_config("rules:\n line-length: {max: 10}\n");
|
|
12
|
+
let input = "key: alpha beta gamma\n";
|
|
13
|
+
let hits = line_length::check(input, &resolved);
|
|
14
|
+
assert_eq!(
|
|
15
|
+
hits,
|
|
16
|
+
vec![Violation {
|
|
17
|
+
line: 1,
|
|
18
|
+
column: 11,
|
|
19
|
+
message: format!(
|
|
20
|
+
"line too long ({} > {} characters)",
|
|
21
|
+
input.trim_end_matches(['\n']).chars().count(),
|
|
22
|
+
10
|
|
23
|
+
),
|
|
24
|
+
}]
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
#[test]
|
|
29
|
+
fn allows_long_single_word_by_default() {
|
|
30
|
+
let resolved = build_config("rules:\n line-length: {max: 20}\n");
|
|
31
|
+
let input = format!("{}\n", "A".repeat(40));
|
|
32
|
+
let hits = line_length::check(&input, &resolved);
|
|
33
|
+
assert!(
|
|
34
|
+
hits.is_empty(),
|
|
35
|
+
"non-breakable words should be allowed by default: {hits:?}"
|
|
36
|
+
);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#[test]
|
|
40
|
+
fn respects_allow_non_breakable_words_flag() {
|
|
41
|
+
let resolved = build_config(
|
|
42
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-words: false\n",
|
|
43
|
+
);
|
|
44
|
+
let input = format!("{}\n", "B".repeat(40));
|
|
45
|
+
let hits = line_length::check(&input, &resolved);
|
|
46
|
+
assert_eq!(hits.len(), 1, "long words should be flagged when disabled");
|
|
47
|
+
assert_eq!(hits[0].line, 1);
|
|
48
|
+
assert_eq!(hits[0].column, 21);
|
|
49
|
+
assert!(
|
|
50
|
+
hits[0].message.starts_with("line too long"),
|
|
51
|
+
"unexpected message: {}",
|
|
52
|
+
hits[0].message
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
#[test]
|
|
57
|
+
fn allows_inline_mappings_when_requested() {
|
|
58
|
+
let strict = build_config(
|
|
59
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-words: false\n",
|
|
60
|
+
);
|
|
61
|
+
let inline_allowed = build_config(
|
|
62
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-inline-mappings: true\n",
|
|
63
|
+
);
|
|
64
|
+
let mapping = "url: http://localhost/very/very/long/path\n";
|
|
65
|
+
|
|
66
|
+
let strict_hits = line_length::check(mapping, &strict);
|
|
67
|
+
assert_eq!(
|
|
68
|
+
strict_hits.len(),
|
|
69
|
+
1,
|
|
70
|
+
"mapping should fail without inline support"
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
let inline_hits = line_length::check(mapping, &inline_allowed);
|
|
74
|
+
assert!(
|
|
75
|
+
inline_hits.is_empty(),
|
|
76
|
+
"inline mapping should pass when allowed"
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
#[test]
|
|
81
|
+
fn inline_mappings_still_fail_when_value_contains_spaces() {
|
|
82
|
+
let resolved = build_config(
|
|
83
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-inline-mappings: true\n",
|
|
84
|
+
);
|
|
85
|
+
let mapping = "url: http://localhost/short + extra words\n";
|
|
86
|
+
let hits = line_length::check(mapping, &resolved);
|
|
87
|
+
assert_eq!(
|
|
88
|
+
hits.len(),
|
|
89
|
+
1,
|
|
90
|
+
"spaces should prevent inline mapping exemption"
|
|
91
|
+
);
|
|
92
|
+
assert_eq!(hits[0].line, 1);
|
|
93
|
+
assert_eq!(hits[0].column, 21);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#[test]
|
|
97
|
+
fn handles_comment_prefixes_like_yamllint() {
|
|
98
|
+
let resolved = build_config(
|
|
99
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-words: true\n",
|
|
100
|
+
);
|
|
101
|
+
let allowed = "## http://example.com/super/long/url/with/no/spaces\n";
|
|
102
|
+
let disallowed = "# # http://example.com/super/long/url/with/no/spaces\n";
|
|
103
|
+
|
|
104
|
+
let allowed_hits = line_length::check(allowed, &resolved);
|
|
105
|
+
assert!(allowed_hits.is_empty(), "double hash comments should pass");
|
|
106
|
+
|
|
107
|
+
let disallowed_hits = line_length::check(disallowed, &resolved);
|
|
108
|
+
assert_eq!(disallowed_hits.len(), 1, "spaced hash comments should fail");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
#[test]
|
|
112
|
+
fn inline_option_implies_non_breakable_words() {
|
|
113
|
+
let resolved = build_config(
|
|
114
|
+
"rules:\n line-length:\n max: 20\n allow-non-breakable-inline-mappings: true\n",
|
|
115
|
+
);
|
|
116
|
+
let word = format!("{}\n", "C".repeat(40));
|
|
117
|
+
let hits = line_length::check(&word, &resolved);
|
|
118
|
+
assert!(
|
|
119
|
+
hits.is_empty(),
|
|
120
|
+
"inline flag should imply allow words: {hits:?}"
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
#[test]
|
|
125
|
+
fn diagnostic_column_handles_negative_max() {
|
|
126
|
+
let resolved = build_config(
|
|
127
|
+
"rules:\n line-length:\n max: -1\n allow-non-breakable-words: false\n",
|
|
128
|
+
);
|
|
129
|
+
let hits = line_length::check("abc\n", &resolved);
|
|
130
|
+
assert_eq!(
|
|
131
|
+
hits.len(),
|
|
132
|
+
1,
|
|
133
|
+
"negative max should still produce diagnostics"
|
|
134
|
+
);
|
|
135
|
+
assert_eq!(hits[0].column, 0, "column should clamp to zero");
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
#[test]
|
|
139
|
+
fn crlf_lines_are_checked() {
|
|
140
|
+
let resolved = build_config(
|
|
141
|
+
"rules:\n line-length:\n max: 3\n allow-non-breakable-words: false\n",
|
|
142
|
+
);
|
|
143
|
+
let hits = line_length::check("AAAA\r\n", &resolved);
|
|
144
|
+
assert_eq!(
|
|
145
|
+
hits.len(),
|
|
146
|
+
1,
|
|
147
|
+
"CRLF line should be counted with carriage return trimmed"
|
|
148
|
+
);
|
|
149
|
+
assert_eq!(hits[0].line, 1);
|
|
150
|
+
assert_eq!(hits[0].column, 4);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
#[test]
|
|
154
|
+
fn spaces_only_lines_flagged() {
|
|
155
|
+
let resolved = build_config("rules:\n line-length: {max: 2}\n");
|
|
156
|
+
let hits = line_length::check(" \n", &resolved);
|
|
157
|
+
assert_eq!(
|
|
158
|
+
hits.len(),
|
|
159
|
+
1,
|
|
160
|
+
"spaces should not qualify as non-breakable words"
|
|
161
|
+
);
|
|
162
|
+
assert_eq!(hits[0].line, 1);
|
|
163
|
+
assert_eq!(hits[0].column, 3);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
#[test]
|
|
167
|
+
fn list_indicator_allows_long_word() {
|
|
168
|
+
let resolved = build_config("rules:\n line-length: {max: 5}\n");
|
|
169
|
+
let input = format!("- {}\n", "a".repeat(30));
|
|
170
|
+
let hits = line_length::check(&input, &resolved);
|
|
171
|
+
assert!(
|
|
172
|
+
hits.is_empty(),
|
|
173
|
+
"list item with non-breakable word should be allowed by default"
|
|
174
|
+
);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
#[test]
|
|
178
|
+
fn dash_without_value_still_flags() {
|
|
179
|
+
let resolved = build_config("rules:\n line-length: {max: 1}\n");
|
|
180
|
+
let hits = line_length::check("- \n", &resolved);
|
|
181
|
+
assert_eq!(
|
|
182
|
+
hits.len(),
|
|
183
|
+
1,
|
|
184
|
+
"dash without content should not bypass limit"
|
|
185
|
+
);
|
|
186
|
+
assert_eq!(hits[0].column, 2);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
#[test]
|
|
190
|
+
fn inline_mapping_guard_handles_plain_text() {
|
|
191
|
+
let resolved = build_config(
|
|
192
|
+
"rules:\n line-length:\n max: 10\n allow-non-breakable-inline-mappings: true\n allow-non-breakable-words: false\n",
|
|
193
|
+
);
|
|
194
|
+
let hits = line_length::check("plain words here\n", &resolved);
|
|
195
|
+
assert_eq!(
|
|
196
|
+
hits.len(),
|
|
197
|
+
1,
|
|
198
|
+
"non-mapping lines should still report violations"
|
|
199
|
+
);
|
|
200
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
use ryl::config::YamlLintConfig;
|
|
2
|
+
use ryl::rules::new_lines::{self, Config, LineKind};
|
|
3
|
+
|
|
4
|
+
#[test]
|
|
5
|
+
fn default_config_uses_unix() {
|
|
6
|
+
let cfg = YamlLintConfig::from_yaml_str("rules:\n new-lines: enable\n")
|
|
7
|
+
.expect("config parses");
|
|
8
|
+
let resolved = Config::resolve(&cfg);
|
|
9
|
+
assert_eq!(resolved.kind, LineKind::Unix);
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
#[test]
|
|
13
|
+
fn platform_newline_can_be_overridden_for_tests() {
|
|
14
|
+
let cfg =
|
|
15
|
+
YamlLintConfig::from_yaml_str("rules:\n new-lines:\n type: platform\n")
|
|
16
|
+
.expect("config parses");
|
|
17
|
+
let resolved = Config::resolve(&cfg);
|
|
18
|
+
let violation = new_lines::check("key: 1\n", resolved, "\r\n");
|
|
19
|
+
let err = violation.expect("should report mismatch");
|
|
20
|
+
assert_eq!(err.line, 1);
|
|
21
|
+
assert_eq!(err.column, 7);
|
|
22
|
+
assert_eq!(err.message, "wrong new line character: expected \\r\\n");
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
#[test]
|
|
26
|
+
fn check_reports_column_in_chars() {
|
|
27
|
+
let cfg = YamlLintConfig::from_yaml_str("rules:\n new-lines:\n type: dos\n")
|
|
28
|
+
.expect("config parses");
|
|
29
|
+
let resolved = Config::resolve(&cfg);
|
|
30
|
+
let violation =
|
|
31
|
+
new_lines::check("héllo\n", resolved, new_lines::platform_newline());
|
|
32
|
+
let err = violation.expect("should report mismatch");
|
|
33
|
+
assert_eq!(err.column, 6, "unicode aware column");
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
#[test]
|
|
37
|
+
fn check_returns_none_when_newlines_match() {
|
|
38
|
+
let cfg = YamlLintConfig::from_yaml_str("rules:\n new-lines:\n type: unix\n")
|
|
39
|
+
.expect("config parses");
|
|
40
|
+
let resolved = Config::resolve(&cfg);
|
|
41
|
+
let outcome =
|
|
42
|
+
new_lines::check("key: value\n", resolved, new_lines::platform_newline());
|
|
43
|
+
assert!(outcome.is_none());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[test]
|
|
47
|
+
fn resolve_defaults_when_rule_missing() {
|
|
48
|
+
let cfg = YamlLintConfig::from_yaml_str("rules: {}\n").expect("config parses");
|
|
49
|
+
let resolved = Config::resolve(&cfg);
|
|
50
|
+
assert_eq!(resolved.kind, LineKind::Unix);
|
|
51
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
use ryl::config::YamlLintConfig;
|
|
2
|
+
use ryl::rules::octal_values::{self, Config};
|
|
3
|
+
|
|
4
|
+
fn build_config(yaml: &str) -> Config {
|
|
5
|
+
let cfg = YamlLintConfig::from_yaml_str(yaml).expect("config parses");
|
|
6
|
+
Config::resolve(&cfg)
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
#[test]
|
|
10
|
+
fn flags_plain_implicit_and_explicit_values() {
|
|
11
|
+
let resolved = build_config("rules:\n octal-values: enable\n");
|
|
12
|
+
let hits = octal_values::check("foo: 010\nbar: 0o10\n", &resolved);
|
|
13
|
+
assert_eq!(hits.len(), 2, "expected to flag both values");
|
|
14
|
+
|
|
15
|
+
assert_eq!(hits[0].line, 1);
|
|
16
|
+
assert_eq!(hits[0].column, 9);
|
|
17
|
+
assert_eq!(hits[0].message, "forbidden implicit octal value \"010\"");
|
|
18
|
+
|
|
19
|
+
assert_eq!(hits[1].line, 2);
|
|
20
|
+
assert_eq!(hits[1].column, 10);
|
|
21
|
+
assert_eq!(hits[1].message, "forbidden explicit octal value \"0o10\"");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#[test]
|
|
25
|
+
fn respects_forbid_implicit_override() {
|
|
26
|
+
let resolved =
|
|
27
|
+
build_config("rules:\n octal-values:\n forbid-implicit-octal: false\n");
|
|
28
|
+
let hits = octal_values::check("foo: 010\nbar: 0o10\n", &resolved);
|
|
29
|
+
assert_eq!(hits.len(), 1, "implicit octals should be allowed");
|
|
30
|
+
assert_eq!(hits[0].message, "forbidden explicit octal value \"0o10\"");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
#[test]
|
|
34
|
+
fn respects_forbid_explicit_override() {
|
|
35
|
+
let resolved =
|
|
36
|
+
build_config("rules:\n octal-values:\n forbid-explicit-octal: false\n");
|
|
37
|
+
let hits = octal_values::check("foo: 010\nbar: 0o10\n", &resolved);
|
|
38
|
+
assert_eq!(hits.len(), 1, "explicit octals should be allowed");
|
|
39
|
+
assert_eq!(hits[0].message, "forbidden implicit octal value \"010\"");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
#[test]
|
|
43
|
+
fn skips_quoted_and_tagged_values() {
|
|
44
|
+
let resolved = build_config("rules:\n octal-values: enable\n");
|
|
45
|
+
let hits = octal_values::check(
|
|
46
|
+
"quoted: '010'\ntagged: !!str 0o10\nflow: [010, '010']\n",
|
|
47
|
+
&resolved,
|
|
48
|
+
);
|
|
49
|
+
assert_eq!(hits.len(), 1, "only plain implicit value should remain");
|
|
50
|
+
assert_eq!(hits[0].line, 3);
|
|
51
|
+
assert_eq!(hits[0].column, 11);
|
|
52
|
+
assert_eq!(hits[0].message, "forbidden implicit octal value \"010\"");
|
|
53
|
+
}
|