Spaces:
Paused
Paused
| from contextlib import redirect_stderr, redirect_stdout | |
| from importlib import metadata | |
| from io import StringIO | |
| from json import JSONDecodeError | |
| from pathlib import Path | |
| from textwrap import dedent | |
| from unittest import TestCase | |
| import json | |
| import os | |
| import subprocess | |
| import sys | |
| import tempfile | |
| import warnings | |
| from jsonschema import Draft4Validator, Draft202012Validator | |
| from jsonschema.exceptions import ( | |
| SchemaError, | |
| ValidationError, | |
| _RefResolutionError, | |
| ) | |
| from jsonschema.validators import _LATEST_VERSION, validate | |
| with warnings.catch_warnings(): | |
| warnings.simplefilter("ignore") | |
| from jsonschema import cli | |
| def fake_validator(*errors): | |
| errors = list(reversed(errors)) | |
| class FakeValidator: | |
| def __init__(self, *args, **kwargs): | |
| pass | |
| def iter_errors(self, instance): | |
| if errors: | |
| return errors.pop() | |
| return [] # pragma: no cover | |
| def check_schema(self, schema): | |
| pass | |
| return FakeValidator | |
| def fake_open(all_contents): | |
| def open(path): | |
| contents = all_contents.get(path) | |
| if contents is None: | |
| raise FileNotFoundError(path) | |
| return StringIO(contents) | |
| return open | |
| def _message_for(non_json): | |
| try: | |
| json.loads(non_json) | |
| except JSONDecodeError as error: | |
| return str(error) | |
| else: # pragma: no cover | |
| raise RuntimeError("Tried and failed to capture a JSON dump error.") | |
| class TestCLI(TestCase): | |
| def run_cli( | |
| self, argv, files=None, stdin=StringIO(), exit_code=0, **override, | |
| ): | |
| arguments = cli.parse_args(argv) | |
| arguments.update(override) | |
| self.assertFalse(hasattr(cli, "open")) | |
| cli.open = fake_open(files or {}) | |
| try: | |
| stdout, stderr = StringIO(), StringIO() | |
| actual_exit_code = cli.run( | |
| arguments, | |
| stdin=stdin, | |
| stdout=stdout, | |
| stderr=stderr, | |
| ) | |
| finally: | |
| del cli.open | |
| self.assertEqual( | |
| actual_exit_code, exit_code, msg=dedent( | |
| f""" | |
| Expected an exit code of {exit_code} != {actual_exit_code}. | |
| stdout: {stdout.getvalue()} | |
| stderr: {stderr.getvalue()} | |
| """, | |
| ), | |
| ) | |
| return stdout.getvalue(), stderr.getvalue() | |
| def assertOutputs(self, stdout="", stderr="", **kwargs): | |
| self.assertEqual( | |
| self.run_cli(**kwargs), | |
| (dedent(stdout), dedent(stderr)), | |
| ) | |
| def test_invalid_instance(self): | |
| error = ValidationError("I am an error!", instance=12) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_instance=json.dumps(error.instance), | |
| ), | |
| validator=fake_validator([error]), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr="12: I am an error!\n", | |
| ) | |
| def test_invalid_instance_pretty_output(self): | |
| error = ValidationError("I am an error!", instance=12) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_instance=json.dumps(error.instance), | |
| ), | |
| validator=fake_validator([error]), | |
| argv=["-i", "some_instance", "--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[ValidationError]===(some_instance)=== | |
| I am an error! | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_invalid_instance_explicit_plain_output(self): | |
| error = ValidationError("I am an error!", instance=12) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_instance=json.dumps(error.instance), | |
| ), | |
| validator=fake_validator([error]), | |
| argv=["--output", "plain", "-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr="12: I am an error!\n", | |
| ) | |
| def test_invalid_instance_multiple_errors(self): | |
| instance = 12 | |
| first = ValidationError("First error", instance=instance) | |
| second = ValidationError("Second error", instance=instance) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_instance=json.dumps(instance), | |
| ), | |
| validator=fake_validator([first, second]), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| 12: First error | |
| 12: Second error | |
| """, | |
| ) | |
| def test_invalid_instance_multiple_errors_pretty_output(self): | |
| instance = 12 | |
| first = ValidationError("First error", instance=instance) | |
| second = ValidationError("Second error", instance=instance) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_instance=json.dumps(instance), | |
| ), | |
| validator=fake_validator([first, second]), | |
| argv=["-i", "some_instance", "--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[ValidationError]===(some_instance)=== | |
| First error | |
| ----------------------------- | |
| ===[ValidationError]===(some_instance)=== | |
| Second error | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_multiple_invalid_instances(self): | |
| first_instance = 12 | |
| first_errors = [ | |
| ValidationError("An error", instance=first_instance), | |
| ValidationError("Another error", instance=first_instance), | |
| ] | |
| second_instance = "foo" | |
| second_errors = [ValidationError("BOOM", instance=second_instance)] | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_first_instance=json.dumps(first_instance), | |
| some_second_instance=json.dumps(second_instance), | |
| ), | |
| validator=fake_validator(first_errors, second_errors), | |
| argv=[ | |
| "-i", "some_first_instance", | |
| "-i", "some_second_instance", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr="""\ | |
| 12: An error | |
| 12: Another error | |
| foo: BOOM | |
| """, | |
| ) | |
| def test_multiple_invalid_instances_pretty_output(self): | |
| first_instance = 12 | |
| first_errors = [ | |
| ValidationError("An error", instance=first_instance), | |
| ValidationError("Another error", instance=first_instance), | |
| ] | |
| second_instance = "foo" | |
| second_errors = [ValidationError("BOOM", instance=second_instance)] | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_first_instance=json.dumps(first_instance), | |
| some_second_instance=json.dumps(second_instance), | |
| ), | |
| validator=fake_validator(first_errors, second_errors), | |
| argv=[ | |
| "--output", "pretty", | |
| "-i", "some_first_instance", | |
| "-i", "some_second_instance", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[ValidationError]===(some_first_instance)=== | |
| An error | |
| ----------------------------- | |
| ===[ValidationError]===(some_first_instance)=== | |
| Another error | |
| ----------------------------- | |
| ===[ValidationError]===(some_second_instance)=== | |
| BOOM | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_custom_error_format(self): | |
| first_instance = 12 | |
| first_errors = [ | |
| ValidationError("An error", instance=first_instance), | |
| ValidationError("Another error", instance=first_instance), | |
| ] | |
| second_instance = "foo" | |
| second_errors = [ValidationError("BOOM", instance=second_instance)] | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"does not": "matter since it is stubbed"}', | |
| some_first_instance=json.dumps(first_instance), | |
| some_second_instance=json.dumps(second_instance), | |
| ), | |
| validator=fake_validator(first_errors, second_errors), | |
| argv=[ | |
| "--error-format", ":{error.message}._-_.{error.instance}:", | |
| "-i", "some_first_instance", | |
| "-i", "some_second_instance", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr=":An error._-_.12::Another error._-_.12::BOOM._-_.foo:", | |
| ) | |
| def test_invalid_schema(self): | |
| self.assertOutputs( | |
| files=dict(some_schema='{"type": 12}'), | |
| argv=["some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| 12: 12 is not valid under any of the given schemas | |
| """, | |
| ) | |
| def test_invalid_schema_pretty_output(self): | |
| schema = {"type": 12} | |
| with self.assertRaises(SchemaError) as e: | |
| validate(schema=schema, instance="") | |
| error = str(e.exception) | |
| self.assertOutputs( | |
| files=dict(some_schema=json.dumps(schema)), | |
| argv=["--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| stderr=( | |
| "===[SchemaError]===(some_schema)===\n\n" | |
| + str(error) | |
| + "\n-----------------------------\n" | |
| ), | |
| ) | |
| def test_invalid_schema_multiple_errors(self): | |
| self.assertOutputs( | |
| files=dict(some_schema='{"type": 12, "items": 57}'), | |
| argv=["some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| 57: 57 is not of type 'object', 'boolean' | |
| """, | |
| ) | |
| def test_invalid_schema_multiple_errors_pretty_output(self): | |
| schema = {"type": 12, "items": 57} | |
| with self.assertRaises(SchemaError) as e: | |
| validate(schema=schema, instance="") | |
| error = str(e.exception) | |
| self.assertOutputs( | |
| files=dict(some_schema=json.dumps(schema)), | |
| argv=["--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| stderr=( | |
| "===[SchemaError]===(some_schema)===\n\n" | |
| + str(error) | |
| + "\n-----------------------------\n" | |
| ), | |
| ) | |
| def test_invalid_schema_with_invalid_instance(self): | |
| """ | |
| "Validating" an instance that's invalid under an invalid schema | |
| just shows the schema error. | |
| """ | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"type": 12, "minimum": 30}', | |
| some_instance="13", | |
| ), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| 12: 12 is not valid under any of the given schemas | |
| """, | |
| ) | |
| def test_invalid_schema_with_invalid_instance_pretty_output(self): | |
| instance, schema = 13, {"type": 12, "minimum": 30} | |
| with self.assertRaises(SchemaError) as e: | |
| validate(schema=schema, instance=instance) | |
| error = str(e.exception) | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema=json.dumps(schema), | |
| some_instance=json.dumps(instance), | |
| ), | |
| argv=["--output", "pretty", "-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr=( | |
| "===[SchemaError]===(some_schema)===\n\n" | |
| + str(error) | |
| + "\n-----------------------------\n" | |
| ), | |
| ) | |
| def test_invalid_instance_continues_with_the_rest(self): | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema='{"minimum": 30}', | |
| first_instance="not valid JSON!", | |
| second_instance="12", | |
| ), | |
| argv=[ | |
| "-i", "first_instance", | |
| "-i", "second_instance", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr="""\ | |
| Failed to parse 'first_instance': {} | |
| 12: 12 is less than the minimum of 30 | |
| """.format(_message_for("not valid JSON!")), | |
| ) | |
| def test_custom_error_format_applies_to_schema_errors(self): | |
| instance, schema = 13, {"type": 12, "minimum": 30} | |
| with self.assertRaises(SchemaError): | |
| validate(schema=schema, instance=instance) | |
| self.assertOutputs( | |
| files=dict(some_schema=json.dumps(schema)), | |
| argv=[ | |
| "--error-format", ":{error.message}._-_.{error.instance}:", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr=":12 is not valid under any of the given schemas._-_.12:", | |
| ) | |
| def test_instance_is_invalid_JSON(self): | |
| instance = "not valid JSON!" | |
| self.assertOutputs( | |
| files=dict(some_schema="{}", some_instance=instance), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stderr=f"""\ | |
| Failed to parse 'some_instance': {_message_for(instance)} | |
| """, | |
| ) | |
| def test_instance_is_invalid_JSON_pretty_output(self): | |
| stdout, stderr = self.run_cli( | |
| files=dict( | |
| some_schema="{}", | |
| some_instance="not valid JSON!", | |
| ), | |
| argv=["--output", "pretty", "-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| ) | |
| self.assertFalse(stdout) | |
| self.assertIn( | |
| "(some_instance)===\n\nTraceback (most recent call last):\n", | |
| stderr, | |
| ) | |
| self.assertNotIn("some_schema", stderr) | |
| def test_instance_is_invalid_JSON_on_stdin(self): | |
| instance = "not valid JSON!" | |
| self.assertOutputs( | |
| files=dict(some_schema="{}"), | |
| stdin=StringIO(instance), | |
| argv=["some_schema"], | |
| exit_code=1, | |
| stderr=f"""\ | |
| Failed to parse <stdin>: {_message_for(instance)} | |
| """, | |
| ) | |
| def test_instance_is_invalid_JSON_on_stdin_pretty_output(self): | |
| stdout, stderr = self.run_cli( | |
| files=dict(some_schema="{}"), | |
| stdin=StringIO("not valid JSON!"), | |
| argv=["--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| ) | |
| self.assertFalse(stdout) | |
| self.assertIn( | |
| "(<stdin>)===\n\nTraceback (most recent call last):\n", | |
| stderr, | |
| ) | |
| self.assertNotIn("some_schema", stderr) | |
| def test_schema_is_invalid_JSON(self): | |
| schema = "not valid JSON!" | |
| self.assertOutputs( | |
| files=dict(some_schema=schema), | |
| argv=["some_schema"], | |
| exit_code=1, | |
| stderr=f"""\ | |
| Failed to parse 'some_schema': {_message_for(schema)} | |
| """, | |
| ) | |
| def test_schema_is_invalid_JSON_pretty_output(self): | |
| stdout, stderr = self.run_cli( | |
| files=dict(some_schema="not valid JSON!"), | |
| argv=["--output", "pretty", "some_schema"], | |
| exit_code=1, | |
| ) | |
| self.assertFalse(stdout) | |
| self.assertIn( | |
| "(some_schema)===\n\nTraceback (most recent call last):\n", | |
| stderr, | |
| ) | |
| def test_schema_and_instance_are_both_invalid_JSON(self): | |
| """ | |
| Only the schema error is reported, as we abort immediately. | |
| """ | |
| schema, instance = "not valid JSON!", "also not valid JSON!" | |
| self.assertOutputs( | |
| files=dict(some_schema=schema, some_instance=instance), | |
| argv=["some_schema"], | |
| exit_code=1, | |
| stderr=f"""\ | |
| Failed to parse 'some_schema': {_message_for(schema)} | |
| """, | |
| ) | |
| def test_schema_and_instance_are_both_invalid_JSON_pretty_output(self): | |
| """ | |
| Only the schema error is reported, as we abort immediately. | |
| """ | |
| stdout, stderr = self.run_cli( | |
| files=dict( | |
| some_schema="not valid JSON!", | |
| some_instance="also not valid JSON!", | |
| ), | |
| argv=["--output", "pretty", "-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| ) | |
| self.assertFalse(stdout) | |
| self.assertIn( | |
| "(some_schema)===\n\nTraceback (most recent call last):\n", | |
| stderr, | |
| ) | |
| self.assertNotIn("some_instance", stderr) | |
| def test_instance_does_not_exist(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}"), | |
| argv=["-i", "nonexisting_instance", "some_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| 'nonexisting_instance' does not exist. | |
| """, | |
| ) | |
| def test_instance_does_not_exist_pretty_output(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}"), | |
| argv=[ | |
| "--output", "pretty", | |
| "-i", "nonexisting_instance", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[FileNotFoundError]===(nonexisting_instance)=== | |
| 'nonexisting_instance' does not exist. | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_schema_does_not_exist(self): | |
| self.assertOutputs( | |
| argv=["nonexisting_schema"], | |
| exit_code=1, | |
| stderr="'nonexisting_schema' does not exist.\n", | |
| ) | |
| def test_schema_does_not_exist_pretty_output(self): | |
| self.assertOutputs( | |
| argv=["--output", "pretty", "nonexisting_schema"], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[FileNotFoundError]===(nonexisting_schema)=== | |
| 'nonexisting_schema' does not exist. | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_neither_instance_nor_schema_exist(self): | |
| self.assertOutputs( | |
| argv=["-i", "nonexisting_instance", "nonexisting_schema"], | |
| exit_code=1, | |
| stderr="'nonexisting_schema' does not exist.\n", | |
| ) | |
| def test_neither_instance_nor_schema_exist_pretty_output(self): | |
| self.assertOutputs( | |
| argv=[ | |
| "--output", "pretty", | |
| "-i", "nonexisting_instance", | |
| "nonexisting_schema", | |
| ], | |
| exit_code=1, | |
| stderr="""\ | |
| ===[FileNotFoundError]===(nonexisting_schema)=== | |
| 'nonexisting_schema' does not exist. | |
| ----------------------------- | |
| """, | |
| ) | |
| def test_successful_validation(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}", some_instance="{}"), | |
| argv=["-i", "some_instance", "some_schema"], | |
| stdout="", | |
| stderr="", | |
| ) | |
| def test_successful_validation_pretty_output(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}", some_instance="{}"), | |
| argv=["--output", "pretty", "-i", "some_instance", "some_schema"], | |
| stdout="===[SUCCESS]===(some_instance)===\n", | |
| stderr="", | |
| ) | |
| def test_successful_validation_of_stdin(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}"), | |
| stdin=StringIO("{}"), | |
| argv=["some_schema"], | |
| stdout="", | |
| stderr="", | |
| ) | |
| def test_successful_validation_of_stdin_pretty_output(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}"), | |
| stdin=StringIO("{}"), | |
| argv=["--output", "pretty", "some_schema"], | |
| stdout="===[SUCCESS]===(<stdin>)===\n", | |
| stderr="", | |
| ) | |
| def test_successful_validation_of_just_the_schema(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}", some_instance="{}"), | |
| argv=["-i", "some_instance", "some_schema"], | |
| stdout="", | |
| stderr="", | |
| ) | |
| def test_successful_validation_of_just_the_schema_pretty_output(self): | |
| self.assertOutputs( | |
| files=dict(some_schema="{}", some_instance="{}"), | |
| argv=["--output", "pretty", "-i", "some_instance", "some_schema"], | |
| stdout="===[SUCCESS]===(some_instance)===\n", | |
| stderr="", | |
| ) | |
| def test_successful_validation_via_explicit_base_uri(self): | |
| ref_schema_file = tempfile.NamedTemporaryFile(delete=False) | |
| ref_schema_file.close() | |
| self.addCleanup(os.remove, ref_schema_file.name) | |
| ref_path = Path(ref_schema_file.name) | |
| ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') | |
| schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' | |
| self.assertOutputs( | |
| files=dict(some_schema=schema, some_instance="1"), | |
| argv=[ | |
| "-i", "some_instance", | |
| "--base-uri", ref_path.parent.as_uri() + "/", | |
| "some_schema", | |
| ], | |
| stdout="", | |
| stderr="", | |
| ) | |
| def test_unsuccessful_validation_via_explicit_base_uri(self): | |
| ref_schema_file = tempfile.NamedTemporaryFile(delete=False) | |
| ref_schema_file.close() | |
| self.addCleanup(os.remove, ref_schema_file.name) | |
| ref_path = Path(ref_schema_file.name) | |
| ref_path.write_text('{"definitions": {"num": {"type": "integer"}}}') | |
| schema = f'{{"$ref": "{ref_path.name}#/definitions/num"}}' | |
| self.assertOutputs( | |
| files=dict(some_schema=schema, some_instance='"1"'), | |
| argv=[ | |
| "-i", "some_instance", | |
| "--base-uri", ref_path.parent.as_uri() + "/", | |
| "some_schema", | |
| ], | |
| exit_code=1, | |
| stdout="", | |
| stderr="1: '1' is not of type 'integer'\n", | |
| ) | |
| def test_nonexistent_file_with_explicit_base_uri(self): | |
| schema = '{"$ref": "someNonexistentFile.json#definitions/num"}' | |
| instance = "1" | |
| with self.assertRaises(_RefResolutionError) as e: | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema=schema, | |
| some_instance=instance, | |
| ), | |
| argv=[ | |
| "-i", "some_instance", | |
| "--base-uri", Path.cwd().as_uri(), | |
| "some_schema", | |
| ], | |
| ) | |
| error = str(e.exception) | |
| self.assertIn(f"{os.sep}someNonexistentFile.json'", error) | |
| def test_invalid_explicit_base_uri(self): | |
| schema = '{"$ref": "foo.json#definitions/num"}' | |
| instance = "1" | |
| with self.assertRaises(_RefResolutionError) as e: | |
| self.assertOutputs( | |
| files=dict( | |
| some_schema=schema, | |
| some_instance=instance, | |
| ), | |
| argv=[ | |
| "-i", "some_instance", | |
| "--base-uri", "not@UR1", | |
| "some_schema", | |
| ], | |
| ) | |
| error = str(e.exception) | |
| self.assertEqual( | |
| error, "unknown url type: 'foo.json'", | |
| ) | |
| def test_it_validates_using_the_latest_validator_when_unspecified(self): | |
| # There isn't a better way now I can think of to ensure that the | |
| # latest version was used, given that the call to validator_for | |
| # is hidden inside the CLI, so guard that that's the case, and | |
| # this test will have to be updated when versions change until | |
| # we can think of a better way to ensure this behavior. | |
| self.assertIs(Draft202012Validator, _LATEST_VERSION) | |
| self.assertOutputs( | |
| files=dict(some_schema='{"const": "check"}', some_instance='"a"'), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stdout="", | |
| stderr="a: 'check' was expected\n", | |
| ) | |
| def test_it_validates_using_draft7_when_specified(self): | |
| """ | |
| Specifically, `const` validation applies for Draft 7. | |
| """ | |
| schema = """ | |
| { | |
| "$schema": "http://json-schema.org/draft-07/schema#", | |
| "const": "check" | |
| } | |
| """ | |
| instance = '"foo"' | |
| self.assertOutputs( | |
| files=dict(some_schema=schema, some_instance=instance), | |
| argv=["-i", "some_instance", "some_schema"], | |
| exit_code=1, | |
| stdout="", | |
| stderr="foo: 'check' was expected\n", | |
| ) | |
| def test_it_validates_using_draft4_when_specified(self): | |
| """ | |
| Specifically, `const` validation *does not* apply for Draft 4. | |
| """ | |
| schema = """ | |
| { | |
| "$schema": "http://json-schema.org/draft-04/schema#", | |
| "const": "check" | |
| } | |
| """ | |
| instance = '"foo"' | |
| self.assertOutputs( | |
| files=dict(some_schema=schema, some_instance=instance), | |
| argv=["-i", "some_instance", "some_schema"], | |
| stdout="", | |
| stderr="", | |
| ) | |
| class TestParser(TestCase): | |
| FakeValidator = fake_validator() | |
| def test_find_validator_by_fully_qualified_object_name(self): | |
| arguments = cli.parse_args( | |
| [ | |
| "--validator", | |
| "jsonschema.tests.test_cli.TestParser.FakeValidator", | |
| "--instance", "mem://some/instance", | |
| "mem://some/schema", | |
| ], | |
| ) | |
| self.assertIs(arguments["validator"], self.FakeValidator) | |
| def test_find_validator_in_jsonschema(self): | |
| arguments = cli.parse_args( | |
| [ | |
| "--validator", "Draft4Validator", | |
| "--instance", "mem://some/instance", | |
| "mem://some/schema", | |
| ], | |
| ) | |
| self.assertIs(arguments["validator"], Draft4Validator) | |
| def cli_output_for(self, *argv): | |
| stdout, stderr = StringIO(), StringIO() | |
| with redirect_stdout(stdout), redirect_stderr(stderr): # noqa: SIM117 | |
| with self.assertRaises(SystemExit): | |
| cli.parse_args(argv) | |
| return stdout.getvalue(), stderr.getvalue() | |
| def test_unknown_output(self): | |
| stdout, stderr = self.cli_output_for( | |
| "--output", "foo", | |
| "mem://some/schema", | |
| ) | |
| self.assertIn("invalid choice: 'foo'", stderr) | |
| self.assertFalse(stdout) | |
| def test_useless_error_format(self): | |
| stdout, stderr = self.cli_output_for( | |
| "--output", "pretty", | |
| "--error-format", "foo", | |
| "mem://some/schema", | |
| ) | |
| self.assertIn( | |
| "--error-format can only be used with --output plain", | |
| stderr, | |
| ) | |
| self.assertFalse(stdout) | |
| class TestCLIIntegration(TestCase): | |
| def test_license(self): | |
| output = subprocess.check_output( | |
| [sys.executable, "-m", "pip", "show", "jsonschema"], | |
| stderr=subprocess.STDOUT, | |
| ) | |
| self.assertIn(b"License: MIT", output) | |
| def test_version(self): | |
| version = subprocess.check_output( | |
| [sys.executable, "-W", "ignore", "-m", "jsonschema", "--version"], | |
| stderr=subprocess.STDOUT, | |
| ) | |
| version = version.decode("utf-8").strip() | |
| self.assertEqual(version, metadata.version("jsonschema")) | |
| def test_no_arguments_shows_usage_notes(self): | |
| output = subprocess.check_output( | |
| [sys.executable, "-m", "jsonschema"], | |
| stderr=subprocess.STDOUT, | |
| ) | |
| output_for_help = subprocess.check_output( | |
| [sys.executable, "-m", "jsonschema", "--help"], | |
| stderr=subprocess.STDOUT, | |
| ) | |
| self.assertEqual(output, output_for_help) | |