You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			905 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
			
		
		
	
	
			905 lines
		
	
	
		
			28 KiB
		
	
	
	
		
			Python
		
	
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
 | 
						|
 | 
						|
        @classmethod
 | 
						|
        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)  # noqa: SIM115
 | 
						|
        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)  # noqa: SIM115
 | 
						|
        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):
 | 
						|
        our_metadata = metadata.metadata("jsonschema")
 | 
						|
        self.assertEqual(our_metadata.get("License-Expression"), "MIT")
 | 
						|
 | 
						|
    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)
 |