diff --git a/test_luhn.py b/test_luhn.py index add2f7b..4715b5a 100644 --- a/test_luhn.py +++ b/test_luhn.py @@ -29,6 +29,47 @@ def test_calculate_check_digit(self): def test_append_check_digit(self): self.assertEqual(append_check_digit("7992739871"), "79927398713") + def test_generated_numbers_have_valid_checksums(self): + for payload in ("0", "7", "12", "000", "7992739871", 7992739871): + with self.subTest(payload=payload): + number = append_check_digit(payload) + self.assertTrue(is_valid(number)) + self.assertEqual(checksum(number), 0) + + def test_other_check_digits_are_invalid(self): + for payload in ("0", "12", "7992739871"): + number = append_check_digit(payload) + + for digit in "0123456789": + if digit == number[-1]: + continue + + with self.subTest(payload=payload, check_digit=digit): + self.assertFalse(is_valid(number[:-1] + digit)) + + def test_supported_separators_are_removed(self): + for separator in (" ", "-", ".", "/", "\\", ","): + with self.subTest(separator=separator): + self.assertTrue(is_valid("799" + separator + "27398713")) + self.assertEqual( + append_check_digit("799" + separator + "2739871"), + "79927398713", + ) + + self.assertTrue(is_valid(" 799-273.987/1\\3, ")) + self.assertEqual(append_check_digit(" 799-273.987/1\\, "), "79927398713") + self.assertEqual(append_check_digit("000"), "0000") + + def test_separator_only_input_is_rejected(self): + value = " -./\\, " + self.assertFalse(is_valid(value)) + + for function in (checksum, calculate_check_digit, append_check_digit): + with self.subTest(function=function.__name__), self.assertRaises( + ValueError + ): + function(value) + def test_rejects_non_digit_payloads(self): expected_message = ( "value must contain digits only, with optional spaces, hyphens, " @@ -55,6 +96,7 @@ def test_cli_exit_status_reflects_validity(self): for number, expected_output, expected_status in ( ("79927398713", "valid", 0), ("79927398714", "invalid", 1), + ("abc", "invalid", 1), ): with self.subTest(number=number): result = subprocess.run( @@ -67,6 +109,19 @@ def test_cli_exit_status_reflects_validity(self): self.assertEqual(result.stdout.strip(), expected_output) self.assertEqual(result.returncode, expected_status) + def test_cli_requires_a_number(self): + script = Path(__file__).with_name("luhn.py") + result = subprocess.run( + [sys.executable, str(script)], + capture_output=True, + text=True, + check=False, + ) + + self.assertEqual(result.returncode, 2) + self.assertEqual(result.stdout, "") + self.assertIn("required", result.stderr) + if __name__ == "__main__": unittest.main()