Skip to content

Commit eab166f

Browse files
add tests
1 parent 7d79543 commit eab166f

File tree

4 files changed

+586
-15
lines changed

4 files changed

+586
-15
lines changed

crates/pgt_suppressions/src/lib.rs

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,138 @@ impl Suppressions {
205205
.unwrap_or_default()
206206
}
207207
}
208+
209+
#[cfg(test)]
210+
mod tests {
211+
use pgt_diagnostics::{Diagnostic, MessageAndDescription};
212+
use pgt_text_size::TextRange;
213+
214+
use crate::suppression::SuppressionDiagnostic;
215+
216+
#[derive(Clone, Debug, Diagnostic)]
217+
#[diagnostic(category = "lint", severity = Error)]
218+
pub struct TestDiagnostic {
219+
#[location(span)]
220+
pub span: TextRange,
221+
}
222+
223+
#[test]
224+
fn correctly_suppresses_diagnostics_at_top_level() {
225+
let doc = r#"
226+
-- pgt-ignore-all lint
227+
228+
select 1;
229+
"#;
230+
231+
let len_doc: u32 = doc.len().try_into().unwrap();
232+
233+
let suppressions = super::Suppressions::from(doc);
234+
235+
assert!(suppressions.is_suppressed(&TestDiagnostic {
236+
span: TextRange::new((len_doc - 10).into(), len_doc.into()),
237+
}));
238+
}
239+
240+
#[test]
241+
fn correctly_suppresses_diagnostics_at_line() {
242+
let doc = r#"
243+
select 2;
244+
245+
-- pgt-ignore lint
246+
select 1;
247+
"#;
248+
249+
let suppressions = super::Suppressions::from(doc);
250+
251+
assert!(suppressions.is_suppressed(&TestDiagnostic {
252+
span: TextRange::new(89.into(), 98.into()),
253+
}));
254+
}
255+
256+
#[test]
257+
fn correctly_suppresses_with_multiple_line_diagnostics() {
258+
let doc = r#"
259+
select 2;
260+
261+
-- pgt-ignore lint
262+
-- pgt-ignore action
263+
select 1;
264+
"#;
265+
266+
let suppressions = super::Suppressions::from(doc);
267+
268+
assert!(suppressions.is_suppressed(&TestDiagnostic {
269+
span: TextRange::new(100.into(), 109.into()),
270+
}));
271+
}
272+
273+
#[test]
274+
fn correctly_suppresses_diagnostics_with_ranges() {
275+
let doc = r#"
276+
select 2;
277+
278+
-- pgt-ignore-start lint
279+
select 1;
280+
-- pgt-ignore-end lint
281+
"#;
282+
283+
let suppressions = super::Suppressions::from(doc);
284+
285+
assert!(suppressions.is_suppressed(&TestDiagnostic {
286+
span: TextRange::new(73.into(), 82.into()),
287+
}));
288+
}
289+
290+
#[test]
291+
fn marks_disabled_rule_suppressions_as_errors() {
292+
let doc = r#"
293+
select 2;
294+
295+
-- pgt-ignore lint/safety/banDropTable
296+
select 1;
297+
"#;
298+
299+
let suppressions = super::Suppressions::from(doc)
300+
.with_disabled_rules(&[pgt_analyse::RuleFilter::Group("safety")]);
301+
302+
assert!(!suppressions.is_suppressed(&TestDiagnostic {
303+
span: TextRange::new(89.into(), 98.into()),
304+
}));
305+
306+
assert_eq!(suppressions.diagnostics.len(), 1);
307+
308+
assert_eq!(
309+
suppressions.diagnostics[0],
310+
SuppressionDiagnostic {
311+
span: TextRange::new(36.into(), 74.into()),
312+
message: MessageAndDescription::from("This rule has been disabled via the configuration. The suppression has no effect.".to_string())
313+
}
314+
);
315+
}
316+
317+
#[test]
318+
fn marks_unused_suppressions_as_errors() {
319+
let doc = r#"
320+
select 2;
321+
322+
-- pgt-ignore lint
323+
select 1;
324+
"#;
325+
326+
// no diagnostics
327+
let diagnostics: Vec<TestDiagnostic> = vec![];
328+
329+
let suppressions =
330+
super::Suppressions::from(doc).with_unused_suppressions_as_errors(&diagnostics);
331+
332+
assert_eq!(suppressions.diagnostics.len(), 1);
333+
334+
assert_eq!(
335+
suppressions.diagnostics[0],
336+
SuppressionDiagnostic {
337+
span: TextRange::new(36.into(), 54.into()),
338+
message: MessageAndDescription::from("This suppression has no effect.".to_string())
339+
}
340+
);
341+
}
342+
}

crates/pgt_suppressions/src/line_index.rs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,13 @@ impl LineIndex {
3131
self.line_offset
3232
.iter()
3333
.enumerate()
34-
.find(|(_, line_offset)| **line_offset >= offset)
35-
.map(|(i, _)| i)
34+
.filter_map(|(i, line_offset)| {
35+
if offset >= *line_offset {
36+
Some(i)
37+
} else {
38+
None
39+
}
40+
})
41+
.last()
3642
}
3743
}

crates/pgt_suppressions/src/parser.rs

Lines changed: 204 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ impl<'a> SuppressionsParser<'a> {
5151
line_suppressions: parser.line_suppressions,
5252
range_suppressions: parser.range_suppressions,
5353
diagnostics: parser.diagnostics,
54-
line_index: LineIndex::new(doc),
54+
line_index: parser.line_index,
5555
}
5656
}
5757

@@ -60,6 +60,11 @@ impl<'a> SuppressionsParser<'a> {
6060
/// suppression, this will stop.
6161
fn parse_file_suppressions(&mut self) {
6262
while let Some((_, preview)) = self.lines.peek() {
63+
if preview.trim().is_empty() {
64+
self.lines.next();
65+
continue;
66+
}
67+
6368
if !preview.trim().starts_with("-- pgt-ignore-all") {
6469
return;
6570
}
@@ -109,9 +114,16 @@ impl<'a> SuppressionsParser<'a> {
109114
SuppressionKind::End => {
110115
let matching_start_idx = self
111116
.start_suppressions_stack
112-
.iter_mut()
113-
.rev()
114-
.position(|start| start.rule_specifier == suppr.rule_specifier);
117+
.iter()
118+
.enumerate()
119+
.filter_map(|(idx, s)| {
120+
if s.rule_specifier == suppr.rule_specifier {
121+
Some(idx)
122+
} else {
123+
None
124+
}
125+
})
126+
.last();
115127

116128
if let Some(start_idx) = matching_start_idx {
117129
let start = self.start_suppressions_stack.remove(start_idx);
@@ -153,3 +165,191 @@ impl<'a> SuppressionsParser<'a> {
153165
}
154166
}
155167
}
168+
169+
#[cfg(test)]
170+
mod tests {
171+
use pgt_analyse::RuleCategory;
172+
173+
use super::*;
174+
use crate::suppression::{RuleSpecifier, SuppressionKind};
175+
176+
#[test]
177+
fn test_parse_line_suppressions() {
178+
let doc = r#"
179+
SELECT 1;
180+
-- pgt-ignore lint/safety/banDropColumn
181+
SELECT 2;
182+
"#;
183+
let suppressions = SuppressionsParser::parse(doc);
184+
185+
// Should have a line suppression on line 1 (0-based index)
186+
let suppression = suppressions
187+
.line_suppressions
188+
.get(&2)
189+
.expect("no suppression found");
190+
191+
assert_eq!(suppression.kind, SuppressionKind::Line);
192+
assert_eq!(
193+
suppression.rule_specifier,
194+
RuleSpecifier::Rule(
195+
RuleCategory::Lint,
196+
"safety".to_string(),
197+
"banDropColumn".to_string()
198+
)
199+
);
200+
}
201+
202+
#[test]
203+
fn test_parse_multiple_line_suppressions() {
204+
let doc = r#"
205+
SELECT 1;
206+
-- pgt-ignore lint/safety/banDropColumn
207+
-- pgt-ignore lint/safety/banDropTable
208+
-- pgt-ignore lint/safety/banDropSomething
209+
"#;
210+
211+
let suppressions = SuppressionsParser::parse(doc);
212+
213+
assert_eq!(suppressions.line_suppressions.len(), 3);
214+
215+
assert_eq!(
216+
suppressions
217+
.line_suppressions
218+
.get(&2)
219+
.unwrap()
220+
.rule_specifier
221+
.rule(),
222+
Some("banDropColumn")
223+
);
224+
225+
assert_eq!(
226+
suppressions
227+
.line_suppressions
228+
.get(&3)
229+
.unwrap()
230+
.rule_specifier
231+
.rule(),
232+
Some("banDropTable")
233+
);
234+
235+
assert_eq!(
236+
suppressions
237+
.line_suppressions
238+
.get(&4)
239+
.unwrap()
240+
.rule_specifier
241+
.rule(),
242+
Some("banDropSomething")
243+
);
244+
}
245+
246+
#[test]
247+
fn parses_file_level_suppressions() {
248+
let doc = r#"
249+
-- pgt-ignore-all lint
250+
-- pgt-ignore-all action
251+
252+
SELECT 1;
253+
-- pgt-ignore-all lint/safety
254+
"#;
255+
256+
let suppressions = SuppressionsParser::parse(doc);
257+
258+
assert_eq!(suppressions.diagnostics.len(), 1);
259+
assert_eq!(suppressions.file_suppressions.len(), 2);
260+
261+
assert_eq!(
262+
suppressions.file_suppressions[0].rule_specifier,
263+
RuleSpecifier::Category(RuleCategory::Lint)
264+
);
265+
assert_eq!(
266+
suppressions.file_suppressions[1].rule_specifier,
267+
RuleSpecifier::Category(RuleCategory::Action)
268+
);
269+
270+
assert_eq!(
271+
suppressions.diagnostics[0].message.to_string(),
272+
String::from("File suppressions should be at the top of the file.")
273+
);
274+
}
275+
276+
#[test]
277+
fn parses_range_suppressions() {
278+
let doc = r#"
279+
-- pgt-ignore-start lint/safety/banDropTable
280+
drop table users;
281+
drop table auth;
282+
drop table posts;
283+
-- pgt-ignore-end lint/safety/banDropTable
284+
"#;
285+
286+
let suppressions = SuppressionsParser::parse(doc);
287+
288+
assert_eq!(suppressions.range_suppressions.len(), 1);
289+
290+
assert_eq!(
291+
suppressions.range_suppressions[0],
292+
RangeSuppression {
293+
suppressed_range: TextRange::new(1.into(), 141.into()),
294+
start_suppression: Suppression {
295+
kind: SuppressionKind::Start,
296+
rule_specifier: RuleSpecifier::Rule(
297+
RuleCategory::Lint,
298+
"safety".to_string(),
299+
"banDropTable".to_string()
300+
),
301+
suppression_range: TextRange::new(1.into(), 45.into()),
302+
explanation: None,
303+
},
304+
}
305+
);
306+
}
307+
308+
#[test]
309+
fn parses_range_suppressions_with_errors() {
310+
let doc = r#"
311+
-- pgt-ignore-start lint/safety/banDropTable
312+
drop table users;
313+
-- pgt-ignore-start lint/safety/banDropTable
314+
drop table auth;
315+
drop table posts;
316+
-- pgt-ignore-end lint/safety/banDropTable
317+
-- pgt-ignore-end lint/safety/banDropColumn
318+
"#;
319+
320+
let suppressions = SuppressionsParser::parse(doc);
321+
322+
assert_eq!(suppressions.range_suppressions.len(), 1);
323+
assert_eq!(suppressions.diagnostics.len(), 2);
324+
325+
// the inner, nested start/end combination is recognized.
326+
assert_eq!(
327+
suppressions.range_suppressions[0],
328+
RangeSuppression {
329+
suppressed_range: TextRange::new(64.into(), 186.into()),
330+
start_suppression: Suppression {
331+
kind: SuppressionKind::Start,
332+
rule_specifier: RuleSpecifier::Rule(
333+
RuleCategory::Lint,
334+
"safety".to_string(),
335+
"banDropTable".to_string()
336+
),
337+
suppression_range: TextRange::new(64.into(), 108.into()),
338+
explanation: None,
339+
},
340+
}
341+
);
342+
343+
// the outer end is an error
344+
assert_eq!(
345+
suppressions.diagnostics[0].message.to_string(),
346+
String::from("This end suppression does not have a matching start.")
347+
);
348+
349+
// the outer start is an error
350+
assert_eq!(
351+
suppressions.diagnostics[1].message.to_string(),
352+
String::from("This start suppression does not have a matching end.")
353+
);
354+
}
355+
}

0 commit comments

Comments
 (0)