Address fourth review round: observability, docs accuracy, dep hygiene, tests
- Warn when no timing data was found at all (empty --times-dir previously produced empty CSVs without any hint) — best_cars empty + non-empty car list now logs a warning in parse_all_times - Remove the inaccurate "known gap" doc from write_trash_csv: category 1 (not best on any track) already covers non-final cars, so the documented gap never existed - Gate env_logger behind the bin-logging feature (default-on) and mark the records binary as requiring it, so downstream lib consumers do not pull env_logger transitively; lib builds clean with --no-default-features - Switch fetch_car progress output from println! to log::info! so RUST_LOG controls it - atomic_write takes &Path instead of &PathBuf - Add direct unit tests for is_final_build (sum-8 finals, maxed builds, sub-8 non-finals, malformed inputs) and a parsing test covering missing/null/non-numeric "t" fields (warn + skip, valid entries still parsed)
This commit is contained in:
parent
58b8d23e68
commit
1a844e7aa8
4 changed files with 107 additions and 10 deletions
|
|
@ -336,6 +336,15 @@ pub async fn parse_all_times(
|
|||
});
|
||||
}
|
||||
|
||||
// Missing timing files are silent (expected for most cars), so a wrong or
|
||||
// empty --times-dir would otherwise produce empty CSVs without any hint.
|
||||
if ctx.best_cars.is_empty() && !car_list.is_empty() {
|
||||
log::warn!(
|
||||
"no timing data found in {times_dir} for {} car(s) — all output CSVs will be empty",
|
||||
car_list.len()
|
||||
);
|
||||
}
|
||||
|
||||
Ok(car_list)
|
||||
}
|
||||
|
||||
|
|
@ -394,7 +403,7 @@ async fn fetch_car(rid: &str, times_dir: &str) -> Result<(), AppError> {
|
|||
let file_name = rid.to_owned() + ".json";
|
||||
let url = format!("{CAR_TIMING_API_BASE}{rid}");
|
||||
let local_file_path = format!("{times_dir}/{file_name}");
|
||||
println!("Fetching {url}.");
|
||||
log::info!("Fetching {url}.");
|
||||
top_drives::get_file(&url, &local_file_path).await
|
||||
}
|
||||
|
||||
|
|
@ -544,6 +553,55 @@ mod tests {
|
|||
assert!(ctx.best_cars.contains_key(&("Monaco Streets".to_string(), 85, "Wet".to_string())));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_car_times_skips_missing_or_non_numeric_time() {
|
||||
let ctx = AnalysisContext::new();
|
||||
let car = Car {
|
||||
rid: "TestCar_2020".to_string(),
|
||||
brand: "TestBrand".to_string(),
|
||||
only_name: "TestModel".to_string(),
|
||||
year: 2020,
|
||||
rq: 85,
|
||||
prize: false,
|
||||
guid: "guid_test".to_string(),
|
||||
};
|
||||
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let times_dir = dir.path().to_str().unwrap();
|
||||
let file_path = dir.path().join("TestCar_2020.json");
|
||||
|
||||
// "t" missing entirely, null, and a string — all must be skipped
|
||||
// without panicking or inserting a record.
|
||||
let json_content = r#"{
|
||||
"data": {
|
||||
"111": {
|
||||
"times": {
|
||||
"laguna_a00": { "u": "Player1" },
|
||||
"mnCity_a01": { "t": null, "u": "Player2" },
|
||||
"testBowl_a02": { "t": "112.45", "u": "Player3" },
|
||||
"butte_a01": { "t": 50.0, "u": "Player4" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}"#;
|
||||
fs::write(&file_path, json_content).unwrap();
|
||||
|
||||
let my_cars: HashSet<String> = HashSet::new();
|
||||
parse_car_times(car, &my_cars, &ctx.best_cars, times_dir).unwrap();
|
||||
|
||||
// Only the valid butte entry survives.
|
||||
assert!(!ctx.best_cars.contains_key(&("Laguna Seca".to_string(), 85, "Dry".to_string())));
|
||||
assert!(!ctx.best_cars.contains_key(&(
|
||||
"Monaco Streets".to_string(),
|
||||
85,
|
||||
"Wet".to_string()
|
||||
)));
|
||||
assert!(
|
||||
ctx.best_cars.contains_key(&("Butte".to_string(), 85, "Wet".to_string())),
|
||||
"valid time entry should still be parsed"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_parse_car_times_filters_downvoted() {
|
||||
let ctx = AnalysisContext::new();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue