use serde_json::Value;
use serde_json::from_str;
use stern4rust::json_printer::JsonPrinter;
use stern4rust::offence::Offence;
use stern4rust::offence_threshold::OffenceThreshold;
fn parsed(files_scanned: usize, offences: &[Offence]) -> Value {
from_str(&JsonPrinter::new(files_scanned).render(offences)).expect("valid json")
}
fn plain() -> Offence {
Offence::new(
"src/a.rs",
12,
"header",
"expected something".to_string(),
"fix it".to_string(),
)
}
#[test]
fn render_carries_every_exclusion_with_its_count() {
let printer = JsonPrinter::new(4).with_exclusions(vec![
("fixture/**".to_string(), 27),
("gone/**".to_string(), 0),
]);
let document: Value = from_str(&printer.render(&[])).expect("valid json");
assert_eq!(document["files_excluded"], 27);
assert_eq!(document["exclusions"][0]["pattern"], "fixture/**");
assert_eq!(document["exclusions"][0]["files_excluded"], 27);
assert_eq!(document["exclusions"][1]["pattern"], "gone/**");
assert_eq!(document["exclusions"][1]["files_excluded"], 0);
}
#[test]
fn render_counts_one_broken_rule_for_two_offences_of_the_same_rule() {
let found = parsed(1, &[plain(), plain()]);
assert_eq!(found["offences_found"], 2);
assert_eq!(found["rules_broken"], 1);
}
#[test]
fn render_escapes_a_description_containing_quotes() {
let offence = Offence::new(
"src/a.rs",
1,
"header",
r#"expected "// Copyright" but found "// nope""#.to_string(),
r#"make line 1 read "// Copyright""#.to_string(),
);
let found = parsed(1, &[offence]);
assert_eq!(
found["offences"][0]["description"],
Value::String(r#"expected "// Copyright" but found "// nope""#.to_string())
);
}
#[test]
fn render_includes_the_summary_counts() {
let found = parsed(43, &[plain()]);
assert_eq!(found["files_scanned"], 43);
assert_eq!(found["offences_found"], 1);
assert_eq!(found["rules_broken"], 1);
}
#[test]
fn render_lists_the_rules_applied_skipped_and_unconfigured() {
let found = from_str::<Value>(
&JsonPrinter::new(1)
.with_rules(
vec!["header".to_string()],
vec!["tests-layout".to_string()],
vec!["readable-source".to_string()],
)
.render(&[]),
)
.expect("valid json");
assert_eq!(found["rules_applied"][0], "header");
assert_eq!(found["rules_skipped"][0], "tests-layout");
assert_eq!(found["rules_unconfigured"][0], "readable-source");
}
#[test]
fn render_names_every_field_of_an_offence() {
let offence = plain()
.with_subject("the constant `LIMIT`")
.with_expected("// Copyright");
let found = parsed(1, &[offence]);
assert_eq!(found["offences"][0]["file"], "src/a.rs");
assert_eq!(found["offences"][0]["line"], 12);
assert_eq!(found["offences"][0]["rule"], "header");
assert_eq!(found["offences"][0]["correction"], "fix it");
assert_eq!(found["offences"][0]["subject"], "the constant `LIMIT`");
assert_eq!(found["offences"][0]["expected"], "// Copyright");
}
#[test]
fn render_of_an_offence_without_extras_still_carries_both_keys() {
let found = parsed(1, &[plain()]);
assert_eq!(found["offences"][0]["subject"], Value::Null);
assert_eq!(found["offences"][0]["expected"], Value::Null);
}
#[test]
fn render_of_more_offences_than_the_threshold_reports_both_counts() {
let offences: Vec<Offence> = (1..=10).map(|_| plain()).collect();
let found = from_str::<Value>(
&JsonPrinter::new(1)
.with_threshold(OffenceThreshold::new(3))
.render(&offences),
)
.expect("valid json");
assert_eq!(found["offences_found"], 10);
assert_eq!(found["offences_reported"], 3);
assert_eq!(found["offences_omitted"], 7);
assert_eq!(found["offence_threshold"], 3);
assert_eq!(found["offences"].as_array().expect("array").len(), 3);
}
#[test]
fn render_of_no_offences_reports_an_empty_list() {
let found = parsed(43, &[]);
assert_eq!(found["offences"], Value::Array(vec![]));
assert_eq!(found["rules_broken"], 0);
}
#[test]
fn render_of_several_offences_keeps_the_order_it_was_given() {
let first = Offence::new(
"src/a.rs",
1,
"header",
"first".to_string(),
"fix it".to_string(),
);
let second = Offence::new(
"src/b.rs",
2,
"header",
"second".to_string(),
"fix it".to_string(),
);
let found = parsed(2, &[first, second]);
assert_eq!(found["offences"][0]["description"], "first");
assert_eq!(found["offences"][1]["description"], "second");
}