• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

VolumeGraphics / havocompare / b57849ca3677b0739ea5d579a57c6f5cd7fed11e

05 Oct 2023 09:04PM UTC coverage: 85.246% (+0.4%) from 84.873%
b57849ca3677b0739ea5d579a57c6f5cd7fed11e

push

github

ChrisRega
Fix review comments

114 of 114 new or added lines in 2 files covered. (100.0%)

2941 of 3450 relevant lines covered (85.25%)

2616.18 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.48
/src/json.rs
1
use crate::report::{DiffDetail, Difference};
2
use crate::Error;
3
use itertools::Itertools;
4
use regex::Regex;
5
use schemars_derive::JsonSchema;
6
use serde::{Deserialize, Serialize};
7
use std::path::Path;
8
use tracing::error;
9

10
#[derive(Debug, Deserialize, Serialize, JsonSchema, Clone)]
2✔
11
/// configuration for the json compare module
12
pub struct JsonConfig {
13
    ignore_keys: Option<Vec<String>>,
14
}
15
impl JsonConfig {
16
    pub(crate) fn get_ignore_list(&self) -> Result<Vec<Regex>, regex::Error> {
6✔
17
        let exclusion_list: Option<Result<Vec<_>, regex::Error>> = self
6✔
18
            .ignore_keys
6✔
19
            .as_ref()
6✔
20
            .map(|v| v.iter().map(|v| Regex::new(v)).collect());
6✔
21
        return if let Some(result) = exclusion_list {
6✔
22
            result
2✔
23
        } else {
24
            Ok(Vec::new())
4✔
25
        };
26
    }
6✔
27
}
28

29
pub(crate) fn compare_files<P: AsRef<Path>>(
3✔
30
    nominal: P,
3✔
31
    actual: P,
3✔
32
    config: &JsonConfig,
3✔
33
) -> Result<Difference, Error> {
3✔
34
    let mut diff = Difference::new_for_file(&nominal, &actual);
3✔
35
    let compared_file_name = nominal.as_ref().to_string_lossy().into_owned();
3✔
36

37
    let nominal = vg_errortools::fat_io_wrap_std(&nominal, &std::fs::read_to_string)?;
3✔
38
    let actual = vg_errortools::fat_io_wrap_std(&actual, &std::fs::read_to_string)?;
3✔
39
    let ignores = config.get_ignore_list()?;
3✔
40

41
    let json_diff = json_diff::process::compare_jsons(&nominal, &actual);
3✔
42
    let json_diff = match json_diff {
3✔
43
        Ok(diff) => diff,
3✔
44
        Err(e) => {
×
45
            let error_message =
×
46
                format!("JSON deserialization failed for {compared_file_name} (error: {e})");
×
47
            error!("{}", error_message);
×
48
            diff.push_detail(DiffDetail::Error(error_message));
×
49
            diff.error();
×
50
            return Ok(diff);
×
51
        }
52
    };
53
    let filtered_diff: Vec<_> = json_diff
3✔
54
        .all_diffs()
3✔
55
        .into_iter()
3✔
56
        .filter(|(_d, v)| !ignores.iter().any(|excl| excl.is_match(v.get_key())))
15✔
57
        .collect();
3✔
58

3✔
59
    if !filtered_diff.is_empty() {
3✔
60
        for (d_type, key) in filtered_diff.iter() {
13✔
61
            error!("{d_type}: {key}");
13✔
62
        }
63
        let left = filtered_diff
3✔
64
            .iter()
3✔
65
            .filter_map(|(k, v)| {
3✔
66
                if matches!(k, json_diff::enums::DiffType::LeftExtra) {
13✔
67
                    Some(v.to_string())
2✔
68
                } else {
69
                    None
11✔
70
                }
71
            })
13✔
72
            .join("\n");
3✔
73
        let right = filtered_diff
3✔
74
            .iter()
3✔
75
            .filter_map(|(k, v)| {
3✔
76
                if matches!(k, json_diff::enums::DiffType::RightExtra) {
13✔
77
                    Some(v.to_string())
3✔
78
                } else {
79
                    None
10✔
80
                }
81
            })
13✔
82
            .join("\n");
3✔
83
        let differences = filtered_diff
3✔
84
            .iter()
3✔
85
            .filter_map(|(k, v)| {
3✔
86
                if matches!(k, json_diff::enums::DiffType::Mismatch) {
13✔
87
                    Some(v.to_string())
8✔
88
                } else {
89
                    None
5✔
90
                }
91
            })
13✔
92
            .join("\n");
3✔
93
        let root_mismatch = filtered_diff
3✔
94
            .iter()
3✔
95
            .find(|(k, _v)| matches!(k, json_diff::enums::DiffType::RootMismatch))
13✔
96
            .map(|(_, v)| v.to_string());
3✔
97

3✔
98
        diff.push_detail(DiffDetail::Json {
3✔
99
            differences,
3✔
100
            left,
3✔
101
            right,
3✔
102
            root_mismatch,
3✔
103
        });
3✔
104

3✔
105
        diff.error();
3✔
106
    }
×
107

108
    Ok(diff)
3✔
109
}
3✔
110

111
#[cfg(test)]
112
mod test {
113
    use super::*;
114

115
    fn trim_split(list: &str) -> Vec<&str> {
2✔
116
        list.split("\n").map(|e| e.trim()).collect()
5✔
117
    }
2✔
118

119
    #[test]
1✔
120
    fn no_filter() {
1✔
121
        let cfg = JsonConfig {
1✔
122
            ignore_keys: Some(vec![]),
1✔
123
        };
1✔
124
        let result = compare_files(
1✔
125
            "tests/integ/data/json/expected/guy.json",
1✔
126
            "tests/integ/data/json/actual/guy.json",
1✔
127
            &cfg,
1✔
128
        )
1✔
129
        .unwrap();
1✔
130
        if let DiffDetail::Json {
131
            differences,
1✔
132
            left,
1✔
133
            right,
1✔
134
            root_mismatch,
1✔
135
        } = result.detail.first().unwrap()
1✔
136
        {
137
            let differences = trim_split(differences);
1✔
138
            assert!(differences.contains(&"car -> [ \"RX7\" :: \"Panda Trueno\" ]"));
1✔
139
            assert!(differences.contains(&"age -> [ 21 :: 18 ]"));
1✔
140
            assert!(differences.contains(&"name -> [ \"Keisuke\" :: \"Takumi\" ]"));
1✔
141
            assert_eq!(differences.len(), 3);
1✔
142

143
            assert_eq!(left.as_str(), " brothers");
1✔
144
            assert!(right.is_empty());
1✔
145
            assert!(root_mismatch.is_none());
1✔
146
        } else {
147
            panic!("wrong diffdetail");
×
148
        }
149
    }
1✔
150

151
    #[test]
1✔
152
    fn filter_works() {
1✔
153
        let cfg = JsonConfig {
1✔
154
            ignore_keys: Some(vec!["name".to_string(), "brother(s?)".to_string()]),
1✔
155
        };
1✔
156
        let result = compare_files(
1✔
157
            "tests/integ/data/json/expected/guy.json",
1✔
158
            "tests/integ/data/json/actual/guy.json",
1✔
159
            &cfg,
1✔
160
        )
1✔
161
        .unwrap();
1✔
162
        if let DiffDetail::Json {
163
            differences,
1✔
164
            left,
1✔
165
            right,
1✔
166
            root_mismatch,
1✔
167
        } = result.detail.first().unwrap()
1✔
168
        {
169
            let differences = trim_split(differences);
1✔
170
            assert!(differences.contains(&"car -> [ \"RX7\" :: \"Panda Trueno\" ]"));
1✔
171
            assert!(differences.contains(&"age -> [ 21 :: 18 ]"));
1✔
172
            assert_eq!(differences.len(), 2);
1✔
173
            assert!(right.is_empty());
1✔
174
            assert!(left.is_empty());
1✔
175
            assert!(root_mismatch.is_none());
1✔
176
        } else {
177
            panic!("wrong diffdetail");
×
178
        }
179
    }
1✔
180
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc