Compare two JSON result files. Returns (status, summary, details).
(before_path, after_path)
| 71 | |
| 72 | |
| 73 | def compare_file(before_path, after_path): |
| 74 | """Compare two JSON result files. Returns (status, summary, details).""" |
| 75 | before = load_json(before_path) |
| 76 | after = load_json(after_path) |
| 77 | |
| 78 | if before is None and after is None: |
| 79 | return 'ERR', 'both files failed to parse', [] |
| 80 | if before is None: |
| 81 | return 'ERR', 'before file failed to parse', [] |
| 82 | if after is None: |
| 83 | return 'ERR', 'after file failed to parse', [] |
| 84 | |
| 85 | if not isinstance(before, list) or not isinstance(after, list): |
| 86 | if before == after: |
| 87 | return 'OK', 'identical objects', [] |
| 88 | return 'DIFF', 'object-level diff', [] |
| 89 | |
| 90 | if len(before) == 0 and len(after) == 0: |
| 91 | return 'OK', 'rows: 0', [] |
| 92 | |
| 93 | # Index rows by key for field-level comparison |
| 94 | before_keyed = {} |
| 95 | after_keyed = {} |
| 96 | for r in before: |
| 97 | before_keyed.setdefault(row_key(r, None), []).append(r) |
| 98 | for r in after: |
| 99 | after_keyed.setdefault(row_key(r, None), []).append(r) |
| 100 | |
| 101 | details = [] |
| 102 | field_diffs = 0 |
| 103 | rows_only_before = 0 |
| 104 | rows_only_after = 0 |
| 105 | |
| 106 | all_keys = sorted(set(list(before_keyed.keys()) + list(after_keyed.keys())), |
| 107 | key=lambda k: str(k)) |
| 108 | |
| 109 | for key in all_keys: |
| 110 | b_rows = before_keyed.get(key, []) |
| 111 | a_rows = after_keyed.get(key, []) |
| 112 | pairs = max(len(b_rows), len(a_rows)) |
| 113 | for i in range(pairs): |
| 114 | br = b_rows[i] if i < len(b_rows) else None |
| 115 | ar = a_rows[i] if i < len(a_rows) else None |
| 116 | if br is None: |
| 117 | rows_only_after += 1 |
| 118 | if len(details) < 10: |
| 119 | details.append(f' + after only: {_abbrev(ar)}') |
| 120 | elif ar is None: |
| 121 | rows_only_before += 1 |
| 122 | if len(details) < 10: |
| 123 | details.append(f' - before only: {_abbrev(br)}') |
| 124 | else: |
| 125 | rd = diff_rows(br, ar) |
| 126 | if rd: |
| 127 | field_diffs += 1 |
| 128 | if len(details) < 10: |
| 129 | key_str = _abbrev_key(key) |
| 130 | for field, bv, av in rd[:3]: |