diff --git a/fixit/cli.py b/fixit/cli.py index 632cf17..b5ecd6d 100644 --- a/fixit/cli.py +++ b/fixit/cli.py @@ -44,25 +44,29 @@ def build_parser() -> argparse.ArgumentParser: def main(argv=None) -> int: - args = build_parser().parse_args(argv) - if args.command == "wc": - print(f"words={wordcount.count_words(args.text)} " - f"lines={wordcount.count_lines(args.text)} " - f"chars={wordcount.count_chars(args.text)}") - elif args.command == "convert": - print(CONVERSIONS[args.kind](args.value)) - elif args.command == "password": - print(password.generate(args.length, use_symbols=args.symbols)) - elif args.command == "uuid": - for _ in range(args.count): - print(uuid.uuid4()) - elif args.command == "json": - print(jsonfmt.format_json(args.raw)) - elif args.command == "palindrome": - print(textutils.is_palindrome(args.text)) - elif args.command == "reverse": - print(textutils.reverse_words(args.text)) - return 0 + try: + args = build_parser().parse_args(argv) + if args.command == "wc": + print(f"words={wordcount.count_words(args.text)} " + f"lines={wordcount.count_lines(args.text)} " + f"chars={wordcount.count_chars(args.text)}") + elif args.command == "convert": + print(CONVERSIONS[args.kind](args.value)) + elif args.command == "password": + print(password.generate(args.length, use_symbols=args.symbols)) + elif args.command == "uuid": + for _ in range(args.count): + print(uuid.uuid4()) + elif args.command == "json": + print(jsonfmt.format_json(args.raw)) + elif args.command == "palindrome": + print(textutils.is_palindrome(args.text)) + elif args.command == "reverse": + print(textutils.reverse_words(args.text)) + return 0 + except ValueError as e: + print(str(e), file=sys.stderr) + return 1 if __name__ == "__main__": diff --git a/fixit/jsonfmt.py b/fixit/jsonfmt.py index 544dd66..965935c 100644 --- a/fixit/jsonfmt.py +++ b/fixit/jsonfmt.py @@ -3,6 +3,9 @@ def format_json(raw: str, indent: int = 2) -> str: - """Return pretty-printed JSON.""" - data = json.loads(raw) - return json.dumps(data, indent=indent) + try: + """Return pretty-printed JSON.""" + data = json.loads(raw) + return json.dumps(data, indent=indent) + except json.JSONDecodeError: + raise ValueError("Invalid JSON") diff --git a/fixit/password.py b/fixit/password.py index 8105c71..7ff6e59 100644 --- a/fixit/password.py +++ b/fixit/password.py @@ -5,6 +5,8 @@ def generate(length: int = 12, use_digits: bool = True, use_symbols: bool = False) -> str: """Return a random password of the given length.""" + if length <= 0: + raise ValueError("Password length must be greater then 0!") chars = string.ascii_letters if use_digits: chars += string.digits diff --git a/tests/test_errors.py b/tests/test_errors.py new file mode 100644 index 0000000..0c62904 --- /dev/null +++ b/tests/test_errors.py @@ -0,0 +1,7 @@ +from fixit import cli + +def test_invalid_json_returns_1(): + assert cli.main(["json", "{bad"]) == 1 + +def test_invalid_password_returns_1(): + assert cli.main(["password", "--length", "0"]) == 1 \ No newline at end of file diff --git a/tests/test_jsonfmt.py b/tests/test_jsonfmt.py index 6a08346..b850f8e 100644 --- a/tests/test_jsonfmt.py +++ b/tests/test_jsonfmt.py @@ -6,7 +6,6 @@ def test_format_json(): assert format_json('{"a":1}') == '{\n "a": 1\n}' -@pytest.mark.xfail(reason="Known bug: invalid JSON raises a raw traceback instead of a friendly error") def test_invalid_json_friendly_error(): with pytest.raises(ValueError, match="Invalid JSON"): format_json("{not json") diff --git a/tests/test_password.py b/tests/test_password.py index 04316ec..ceae252 100644 --- a/tests/test_password.py +++ b/tests/test_password.py @@ -10,7 +10,6 @@ def test_no_digits(): assert not any(c.isdigit() for c in generate(50, use_digits=False)) -@pytest.mark.xfail(reason="Known bug: length < 1 is not validated") def test_invalid_length(): with pytest.raises(ValueError): generate(0)