compiler/crates/react_compiler_ast/tests/round_trip.rs RUST 152 lines View on github.com → Search inside
1use std::path::PathBuf;23fn get_fixture_json_dir() -> PathBuf {4    if let Ok(dir) = std::env::var("FIXTURE_JSON_DIR") {5        return PathBuf::from(dir);6    }7    // Default: fixtures checked in alongside the test8    PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("tests/fixtures")9}1011/// Recursively sort all keys in a JSON value for order-independent comparison.12fn normalize_json(value: &serde_json::Value) -> serde_json::Value {13    match value {14        serde_json::Value::Object(map) => {15            let mut sorted: Vec<(String, serde_json::Value)> = map16                .iter()17                .map(|(k, v)| (k.clone(), normalize_json(v)))18                .collect();19            sorted.sort_by(|a, b| a.0.cmp(&b.0));20            serde_json::Value::Object(sorted.into_iter().collect())21        }22        serde_json::Value::Array(arr) => {23            serde_json::Value::Array(arr.iter().map(normalize_json).collect())24        }25        // Normalize numbers: f64 values like 1.0 should compare equal to integer 126        serde_json::Value::Number(n) => {27            if let Some(f) = n.as_f64() {28                if f.fract() == 0.0 && f.is_finite() && f.abs() < (i64::MAX as f64) {29                    serde_json::Value::Number(serde_json::Number::from(f as i64))30                } else {31                    value.clone()32                }33            } else {34                value.clone()35            }36        }37        other => other.clone(),38    }39}4041fn compute_diff(original: &str, round_tripped: &str) -> String {42    use similar::ChangeTag;43    use similar::TextDiff;4445    let diff = TextDiff::from_lines(original, round_tripped);46    let mut output = String::new();47    let mut lines_written = 0;48    const MAX_DIFF_LINES: usize = 50;4950    for change in diff.iter_all_changes() {51        if lines_written >= MAX_DIFF_LINES {52            output.push_str("... (diff truncated)\n");53            break;54        }55        let sign = match change.tag() {56            ChangeTag::Delete => "-",57            ChangeTag::Insert => "+",58            ChangeTag::Equal => continue,59        };60        output.push_str(&format!("{sign} {change}"));61        lines_written += 1;62    }6364    output65}6667#[test]68fn round_trip_all_fixtures() {69    let json_dir = get_fixture_json_dir();7071    let mut failures: Vec<(String, String)> = Vec::new();72    let mut total = 0;73    let mut passed = 0;7475    // Fixtures with known issues that can't be fixed in the AST crate:76    // - lone-surrogate-string-values: contains lone Unicode surrogates (\uD800)77    //   that serde_json rejects during deserialization.78    let known_failures: &[&str] = &["lone-surrogate-string-values"];7980    for entry in walkdir::WalkDir::new(&json_dir)81        .into_iter()82        .filter_map(|e| e.ok())83        .filter(|e| {84            e.path().extension().is_some_and(|ext| ext == "json")85                && !e.path().to_string_lossy().ends_with(".scope.json")86                && !e.path().to_string_lossy().ends_with(".renamed.json")87        })88    {89        let fixture_name = entry90            .path()91            .strip_prefix(&json_dir)92            .unwrap()93            .display()94            .to_string();95        let original_json = std::fs::read_to_string(entry.path()).unwrap();9697        if known_failures.iter().any(|kf| fixture_name.contains(kf)) {98            continue;99        }100101        total += 1;102103        // Deserialize into our Rust types104        let ast: react_compiler_ast::File = match serde_json::from_str(&original_json) {105            Ok(ast) => ast,106            Err(e) => {107                failures.push((fixture_name, format!("Deserialization error: {e}")));108                continue;109            }110        };111112        // Re-serialize back to JSON113        let round_tripped = serde_json::to_string_pretty(&ast).unwrap();114115        // Normalize and compare116        let original_value: serde_json::Value = serde_json::from_str(&original_json).unwrap();117        let round_tripped_value: serde_json::Value = serde_json::from_str(&round_tripped).unwrap();118119        let original_normalized = normalize_json(&original_value);120        let round_tripped_normalized = normalize_json(&round_tripped_value);121122        if original_normalized != round_tripped_normalized {123            let orig_str = serde_json::to_string_pretty(&original_normalized).unwrap();124            let rt_str = serde_json::to_string_pretty(&round_tripped_normalized).unwrap();125            let diff = compute_diff(&orig_str, &rt_str);126            failures.push((fixture_name, diff));127        } else {128            passed += 1;129        }130    }131132    println!("\n{passed}/{total} fixtures passed round-trip");133134    if !failures.is_empty() {135        let show_count = failures.len().min(5);136        let mut msg = format!(137            "\n{} of {total} fixtures failed round-trip (showing first {show_count}):\n\n",138            failures.len()139        );140        for (name, diff) in failures.iter().take(show_count) {141            msg.push_str(&format!("--- {name} ---\n{diff}\n\n"));142        }143        if failures.len() > show_count {144            msg.push_str(&format!(145                "... and {} more failures\n",146                failures.len() - show_count147            ));148        }149        panic!("{msg}");150    }151}

Code quality findings 8

Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
.unwrap()
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let original_json = std::fs::read_to_string(entry.path()).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let round_tripped = serde_json::to_string_pretty(&ast).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let original_value: serde_json::Value = serde_json::from_str(&original_json).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let round_tripped_value: serde_json::Value = serde_json::from_str(&round_tripped).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let orig_str = serde_json::to_string_pretty(&original_normalized).unwrap();
Warning: '.unwrap()' will panic on None/Err variants. Prefer using pattern matching (match, if let), combinators (map, and_then), or the '?' operator for robust error handling.
warning correctness unwrap-usage
let rt_str = serde_json::to_string_pretty(&round_tripped_normalized).unwrap();
Info: Direct printing to stdout/stderr. For application logging, prefer using a logging facade like `log` or `tracing` for better control over levels, formatting, and output destinations.
info maintainability println-macro
println!("\n{passed}/{total} fixtures passed round-trip");

Security findings 1

Security Info: Reading environment variables. Treat them as untrusted input; validate or sanitize values before use, especially if controlling paths, commands, or network addresses.
security env-var-untrusted
if let Ok(dir) = std::env::var("FIXTURE_JSON_DIR") {

Get this view in your editor

Same data, no extra tab — call code_get_file + code_get_findings over MCP from Claude/Cursor/Copilot.