spark: add JSON validator CLI with test suite (4/4 tests pass)

This commit is contained in:
spark committed 2026-10-24 07:55:43 +00:00
1 parent 838588136c
commit c72e1de9ff
2 files changed
+157

No files matched your search

@@ -0,0 +1,71 @@
#!/usr/bin/env python3
"""JSON Validator CLI - Validates JSON files and reports syntax errors."""
import json
import sys
from pathlib import Path
def validate_json_file(filepath: str) -> dict:
"""Validate a JSON file and return result dict."""
result = {
"file": filepath,
"valid": False,
"error": None,
"line": None,
"column": None,
"decoded": None
}
try:
content = Path(filepath).read_text()
except FileNotFoundError:
result["error"] = f"File not found: {filepath}"
return result
except Exception as e:
result["error"] = f"Read error: {e}"
return result
try:
data = json.loads(content)
result["valid"] = True
result["decoded"] = data
return result
except json.JSONDecodeError as e:
result["error"] = str(e.msg)
result["line"] = e.lineno
result["column"] = e.colno
# Show context around error
lines = content.split('\n')
if e.lineno and e.lineno <= len(lines):
result["context"] = lines[e.lineno - 1]
return result
def main():
if len(sys.argv) < 2:
print("Usage: json_validate_standalone.py <file.json> [file2.json...]")
sys.exit(1)
exit_code = 0
results = []
for filepath in sys.argv[1:]:
result = validate_json_file(filepath)
results.append(result)
if result["valid"]:
print(f"VALID: {filepath}")
else:
print(f"INVALID: {filepath}")
print(f" Error: {result['error']}")
if result.get('line'):
print(f" Line: {result['line']}, Column: {result['column']}")
if result.get('context'):
print(f" Context: {result['context'][:60]}")
exit_code = 1
sys.exit(exit_code)
if __name__ == "__main__":
main()
+86
View File
@@ -0,0 +1,86 @@
#!/usr/bin/env python3
"""Test suite for json_validate_standalone.py"""
import json
import sys
import subprocess
import tempfile
from pathlib import Path
def run_validator(files):
"""Run validator and return (exit_code, output)."""
cmd = ['python3', '/root/.hermes/wizard-outputs/spark/json_validate_standalone.py'] + files
result = subprocess.run(cmd, capture_output=True, text=True)
return result.returncode, result.stdout
def test_valid_json():
"""Test that valid JSON returns exit code 0."""
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
json.dump({"test": "data"}, f)
f.flush()
exit_code, output = run_validator([f.name])
Path(f.name).unlink()
assert exit_code == 0, f"Expected 0, got {exit_code}"
assert "VALID:" in output, f"Expected 'VALID:' in output"
return True
def test_invalid_json():
"""Test that invalid JSON returns exit code 1."""
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
f.write('{invalid json')
f.flush()
exit_code, output = run_validator([f.name])
Path(f.name).unlink()
assert exit_code == 1, f"Expected 1, got {exit_code}"
assert "INVALID:" in output, f"Expected 'INVALID:' in output"
return True
def test_nonexistent_file():
"""Test handling of nonexistent file."""
exit_code, output = run_validator(['/nonexistent/file.json'])
assert exit_code == 1, f"Expected 1 for nonexistent file"
assert "File not found" in output, f"Expected 'File not found' in output"
return True
def test_multiple_files_mixed():
"""Test with multiple files, some valid some invalid."""
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f1:
json.dump({"a": 1}, f1)
f1.flush()
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f2:
f2.write('{bad}')
f2.flush()
exit_code, output = run_validator([f1.name, f2.name])
Path(f1.name).unlink()
Path(f2.name).unlink()
assert exit_code == 1, "Should fail due to invalid file"
assert "VALID:" in output and "INVALID:" in output
return True
def main():
tests = [
("Valid JSON", test_valid_json),
("Invalid JSON", test_invalid_json),
("Nonexistent file", test_nonexistent_file),
("Multiple files mixed", test_multiple_files_mixed),
]
passed = 0
failed = 0
for name, test_fn in tests:
try:
test_fn()
print(f"PASS: {name}")
passed += 1
except AssertionError as e:
print(f"FAIL: {name} - {e}")
failed += 1
except Exception as e:
print(f"ERROR: {name} - {e}")
failed += 1
print(f"\nResults: {passed} passed, {failed} failed")
return 0 if failed == 0 else 1
if __name__ == "__main__":
sys.exit(main())