spark: add JSON linter with test suite
This commit is contained in:
1 parent
3f7a29c606
commit
0f0d02b4b1
2 files changed
+150
No files matched your search
@@ -0,0 +1,42 @@
|
||||
#!/usr/bin/env python3
|
||||
"""JSON Lint CLI - validates JSON from stdin or --text flag."""
|
||||
|
||||
import json
|
||||
import sys
|
||||
|
||||
|
||||
def validate(text: str) -> tuple[bool, str]:
|
||||
"""Validate JSON text. Returns (is_valid, error_msg)."""
|
||||
try:
|
||||
json.loads(text)
|
||||
return True, ""
|
||||
except json.JSONDecodeError as e:
|
||||
return False, f"Line {e.lineno}, Col {e.colno}: {e.msg}"
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 3 and sys.argv[1] == "--text":
|
||||
# Validate inline JSON text
|
||||
text = sys.argv[2]
|
||||
elif len(sys.argv) == 2 and sys.argv[1] == "-":
|
||||
# Validate from stdin
|
||||
text = sys.stdin.read()
|
||||
elif len(sys.argv) == 2:
|
||||
# Validate from file path
|
||||
with open(sys.argv[1], 'r') as f:
|
||||
text = f.read()
|
||||
else:
|
||||
print("Usage: jsonlint.py [--text 'json'] | [-]", file=sys.stderr)
|
||||
sys.exit(2)
|
||||
|
||||
ok, err = validate(text)
|
||||
if ok:
|
||||
print("VALID")
|
||||
sys.exit(0)
|
||||
else:
|
||||
print(f"INVALID: {err}", file=sys.stderr)
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Test script for jsonlint.py - verifies correct behavior."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import os
|
||||
|
||||
SCRIPT = "/root/.hermes/wizard-outputs/spark/jsonlint.py"
|
||||
|
||||
def run_script(args):
|
||||
"""Run jsonlint.py and return (exit_code, stdout, stderr)."""
|
||||
result = subprocess.run(
|
||||
["python3", SCRIPT] + args,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
return result.returncode, result.stdout.strip(), result.stderr.strip()
|
||||
|
||||
def test_valid_inline_json():
|
||||
"""Valid JSON via --text should return 0."""
|
||||
code, out, err = run_script(["--text", '{"key": "value"}'])
|
||||
assert code == 0, f"Expected exit 0, got {code}"
|
||||
assert out == "VALID", f"Expected 'VALID', got '{out}'"
|
||||
return True
|
||||
|
||||
def test_invalid_inline_json():
|
||||
"""Invalid JSON via --text should return 1."""
|
||||
code, out, err = run_script(["--text", "{not json}"])
|
||||
assert code == 1, f"Expected exit 1, got {code}"
|
||||
assert "INVALID" in err, f"Expected 'INVALID' in stderr, got '{err}'"
|
||||
return True
|
||||
|
||||
def test_valid_file():
|
||||
"""Valid JSON from file should return 0."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
||||
f.write('{"arr": [1, 2, 3]}')
|
||||
path = f.name
|
||||
try:
|
||||
code, out, err = run_script([path])
|
||||
assert code == 0, f"Expected exit 0, got {code}"
|
||||
assert out == "VALID"
|
||||
return True
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
def test_invalid_file():
|
||||
"""Invalid JSON from file should return 1."""
|
||||
with tempfile.NamedTemporaryFile(mode='w', suffix='.json', delete=False) as f:
|
||||
f.write('{"missing": }')
|
||||
path = f.name
|
||||
try:
|
||||
code, out, err = run_script([path])
|
||||
assert code == 1, f"Expected exit 1, got {code}"
|
||||
return True
|
||||
finally:
|
||||
os.unlink(path)
|
||||
|
||||
def test_empty_json():
|
||||
"""Empty object should be valid."""
|
||||
code, out, err = run_script(["--text", "{}"])
|
||||
assert code == 0, f"Expected exit 0, got {code}"
|
||||
return True
|
||||
|
||||
def test_nested_json():
|
||||
"""Complex nested JSON should be valid."""
|
||||
json_text = '{"a": {"b": {"c": [1, 2, {"d": true}]}}}'
|
||||
code, out, err = run_script(["--text", json_text])
|
||||
assert code == 0, f"Expected exit 0, got {code}"
|
||||
return True
|
||||
|
||||
def test_missing_quote():
|
||||
"""Unclosed string should be invalid."""
|
||||
code, out, err = run_script(["--text", '{"a": "unclosed}'])
|
||||
assert code == 1, f"Expected exit 1, got {code}"
|
||||
return True
|
||||
|
||||
def test_trailing_comma():
|
||||
"""Trailing comma should be invalid (standard JSON)."""
|
||||
code, out, err = run_script(["--text", '{"a": 1,}'])
|
||||
assert code == 1, f"Expected exit 1, got {code}"
|
||||
return True
|
||||
|
||||
if __name__ == "__main__":
|
||||
tests = [
|
||||
test_valid_inline_json,
|
||||
test_invalid_inline_json,
|
||||
test_valid_file,
|
||||
test_invalid_file,
|
||||
test_empty_json,
|
||||
test_nested_json,
|
||||
test_missing_quote,
|
||||
test_trailing_comma,
|
||||
]
|
||||
|
||||
passed = 0
|
||||
failed = 0
|
||||
for test in tests:
|
||||
try:
|
||||
test()
|
||||
print(f"PASS: {test.__name__}")
|
||||
passed += 1
|
||||
except AssertionError as e:
|
||||
print(f"FAIL: {test.__name__}: {e}")
|
||||
failed += 1
|
||||
|
||||
print(f"\nResults: {passed} passed, {failed} failed")
|
||||
sys.exit(0 if failed == 0 else 1)
|
||||
Reference in new issue
Block a user