#!/usr/bin/env python3 """ Test script to verify metric sanitization fixes corrupted JSON values. This demonstrates the fix for the issue where PCP metrics containing corrupted values like '0.9XXXXXX999999999' would cause the HTML report to fail loading in browsers. """ import json import sys # Minimal implementation of the sanitization functions from create-report.py def _sanitize_metric_array(arr, metric_name="value"): """ Sanitize a metric array, replacing corrupted values with None. Corrupted values can appear when CI infrastructure redacts sensitive data or when there are data processing errors. Common patterns include values with 'X' characters (e.g., '0.9XXXXXX999999999'). Returns a sanitized copy of the array. """ if not isinstance(arr, list): return arr sanitized = [] corrupted_count = 0 for i, val in enumerate(arr): # Check if value is already a valid number if isinstance(val, (int, float)): sanitized.append(val) continue # Check for string values that might be corrupted if isinstance(val, str): # Check for X-pattern corruption (e.g., "0.9XXXXXX999999999") if 'X' in val or 'x' in val: sanitized.append(None) corrupted_count += 1 if corrupted_count <= 3: # Log first few occurrences print(f" Corrupted {metric_name} value at index {i}: {val!r} - replaced with null") continue # Try to parse as float try: sanitized.append(float(val)) continue except (ValueError, TypeError): pass # Any other unexpected type - replace with None sanitized.append(None) corrupted_count += 1 if corrupted_count <= 3: print(f" Invalid {metric_name} value at index {i}: {val!r} (type {type(val).__name__}) - replaced with null") if corrupted_count > 3: print(f" Total {corrupted_count} corrupted {metric_name} values found") return sanitized def _sanitize_metrics(metrics_dict, metric_type): """ Sanitize all arrays in a metrics dictionary. Metrics typically contain arrays for timestamps and various metric values. This ensures all numeric arrays contain only valid numbers or None. """ if not isinstance(metrics_dict, dict): return metrics_dict # Common array keys in PCP metrics array_keys = ['timestamps', 'user', 'sys', 'iowait', 'idle', 'values', 'used', 'free', 'available', 'read', 'write'] for key in array_keys: if key in metrics_dict and isinstance(metrics_dict[key], list): metrics_dict[key] = _sanitize_metric_array( metrics_dict[key], metric_name=f"{metric_type}.{key}" ) return metrics_dict if __name__ == "__main__": # Note: The corrupted value in the test needs to be a string to be valid Python # In the real scenario, it appears in the JSON file as a malformed numeric literal # For this test, we'll use a string representation print("=== Testing Metric Sanitization ===\n") # Simulate corrupted CPU metrics with string representation corrupted_cpu_data = { "timestamps": ["2026-10-15 02:47:24", "2026-10-15 02:47:34", "2026-10-15 02:47:44"], "user": [0.626643, 0.520164, 1.096774], "sys": [0.858436, 0.491806, 0.504891], "iowait": ["0.9XXXXXX999999999", 0.790714, 0.666646], # Corrupted value as string "idle": [0.707218, 0.918723, 1.129626] } print("1. Original corrupted data:") print(f" iowait array: {corrupted_cpu_data['iowait']}") print(f" iowait[0] type: {type(corrupted_cpu_data['iowait'][0]).__name__}") print("\n2. Sanitizing the data:") sanitized = _sanitize_metrics(corrupted_cpu_data.copy(), "cpu") print(f"\n3. Sanitized data:") print(f" iowait array: {sanitized['iowait']}") print(f" iowait[0]: {sanitized['iowait'][0]} (should be None)") print("\n4. Testing JSON serialization:") try: json_str = json.dumps(sanitized) print(" ✓ SUCCESS: Sanitized data serializes to valid JSON") # Verify it can be parsed back parsed = json.loads(json_str) print(" ✓ SUCCESS: JSON can be parsed back") # Check the corrupted value was replaced with null if parsed['iowait'][0] is None: print(" ✓ SUCCESS: Corrupted value replaced with null") print(f"\n JSON preview: {json_str[:200]}...") sys.exit(0) else: print(f" ✗ FAIL: Expected null but got {parsed['iowait'][0]}") sys.exit(1) except Exception as e: print(f" ✗ FAIL: {e}") sys.exit(1)