From cee8d5422422ed393c09f6cfd416896bccb1a4ab Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 16:32:25 +0300 Subject: [PATCH 1/8] feat(validators): add Mir card number validation --- src/validators/card.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/src/validators/card.py b/src/validators/card.py index d95643c..a9a2e7d 100644 --- a/src/validators/card.py +++ b/src/validators/card.py @@ -192,3 +192,25 @@ def discover(value: str, /): """ pattern = re.compile(r"^(60|64|65)") return card_number(value) and len(value) == 16 and pattern.match(value) + + +@validator +def mir(value: str, /): + """Return whether or not given value is a valid Mir card number. + + Examples: + >>> mir('2200123456789012') + # Output: True + >>> mir('4242424242424242') + # Output: ValidationError(func=mir, args={'value': '4242424242424242'}) + + Args: + value: + Mir card number string to validate. + + Returns: + (Literal[True]): If `value` is a valid Mir card number. + (ValidationError): If `value` is an invalid Mir card number. + """ + pattern = re.compile(r"^(220[0-4])") + return card_number(value) and len(value) == 16 and pattern.match(value) From 7c497af9931627d991da38fd2d46f12c600bf5e2 Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 16:33:46 +0300 Subject: [PATCH 2/8] feat: add mir method to __init__ --- src/validators/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/validators/__init__.py b/src/validators/__init__.py index 635a835..e29373d 100644 --- a/src/validators/__init__.py +++ b/src/validators/__init__.py @@ -2,7 +2,7 @@ # local from .between import between -from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa +from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa, mir from .country import calling_code, country_code, currency from .cron import cron from .crypto_addresses import bsc_address, btc_address, eth_address, trx_address @@ -51,6 +51,7 @@ "mastercard", "visa", "unionpay", + "mir", # country "calling_code", "country_code", From af32a68dab614d3e77bbf2021dd5f247d3b247c9 Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 16:41:00 +0300 Subject: [PATCH 3/8] test(card): add Mir card validation tests --- tests/test_card.py | 34 ++++++++++++++++++++++++++-------- 1 file changed, 26 insertions(+), 8 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index 1eafa2f..46061fa 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -14,6 +14,7 @@ mastercard, unionpay, visa, + mir ) visa_cards = ["4242424242424242", "4000002760003184"] @@ -23,6 +24,7 @@ diners_cards = ["3056930009020004", "36227206271667"] jcb_cards = ["3566002020360505"] discover_cards = ["6011111111111117", "6011000990139424"] +mir_cards = ["2200123456789012", "2204987654321098"] @pytest.mark.parametrize( @@ -33,14 +35,15 @@ + unionpay_cards + diners_cards + jcb_cards - + discover_cards, + + discover_cards + + mir_cards, ) def test_returns_true_on_valid_card_number(value: str): """Test returns true on valid card number.""" assert card_number(value) -@pytest.mark.parametrize("value", ["4242424242424240", "4000002760003180", "400000276000318X"]) +@pytest.mark.parametrize("value", ["4242424242424240", "4000002760003180", "400000276000318X", "220012345678901X"]) def test_returns_failed_on_valid_card_number(value: str): """Test returns failed on valid card number.""" assert isinstance(card_number(value), ValidationError) @@ -69,7 +72,7 @@ def test_returns_true_on_valid_mastercard(value: str): @pytest.mark.parametrize( "value", - visa_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards, + visa_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards + mir_cards, ) def test_returns_failed_on_valid_mastercard(value: str): """Test returns failed on valid mastercard.""" @@ -84,7 +87,7 @@ def test_returns_true_on_valid_amex(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards, + visa_cards + mastercard_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards + mir_cards, ) def test_returns_failed_on_valid_amex(value: str): """Test returns failed on valid amex.""" @@ -99,7 +102,7 @@ def test_returns_true_on_valid_unionpay(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + diners_cards + jcb_cards + discover_cards, + visa_cards + mastercard_cards + amex_cards + diners_cards + jcb_cards + discover_cards + mir_cards, ) def test_returns_failed_on_valid_unionpay(value: str): """Test returns failed on valid unionpay.""" @@ -114,7 +117,7 @@ def test_returns_true_on_valid_diners(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + jcb_cards + discover_cards, + visa_cards + mastercard_cards + amex_cards + unionpay_cards + jcb_cards + discover_cards + mir_cards, ) def test_returns_failed_on_valid_diners(value: str): """Test returns failed on valid diners.""" @@ -129,7 +132,7 @@ def test_returns_true_on_valid_jcb(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + discover_cards, + visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + discover_cards + mir_cards, ) def test_returns_failed_on_valid_jcb(value: str): """Test returns failed on valid jcb.""" @@ -144,8 +147,23 @@ def test_returns_true_on_valid_discover(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards, + visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + mir_cards, ) def test_returns_failed_on_valid_discover(value: str): """Test returns failed on valid discover.""" assert isinstance(discover(value), ValidationError) + + +@pytest.mark.parametrize("value", mir_cards) +def test_returns_true_on_valid_mir(value: str): + """Test returns true on valid Mir card.""" + assert mir(value) + + +@pytest.mark.parametrize( + "value", + visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards, +) +def test_returns_failed_on_valid_mir(value: str): + """Test returns failed on invalid Mir card (other payment systems).""" + assert isinstance(mir(value), ValidationError) From b91452f6ef5f345707a90c093d5a904fce0a89e1 Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 16:59:11 +0300 Subject: [PATCH 4/8] docs(mir): update example valid mir card --- src/validators/card.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validators/card.py b/src/validators/card.py index a9a2e7d..3fbe0ff 100644 --- a/src/validators/card.py +++ b/src/validators/card.py @@ -199,7 +199,7 @@ def mir(value: str, /): """Return whether or not given value is a valid Mir card number. Examples: - >>> mir('2200123456789012') + >>> mir('2200123456789019') # Output: True >>> mir('4242424242424242') # Output: ValidationError(func=mir, args={'value': '4242424242424242'}) From 2eabc1378cc17b7255d7493a39c2d20f9d7329fb Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 17:00:19 +0300 Subject: [PATCH 5/8] fix(tests): update examples mir_cards and drop mir_cards from failed_on_valid_mastercard --- tests/test_card.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_card.py b/tests/test_card.py index 46061fa..d60de92 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -24,7 +24,7 @@ diners_cards = ["3056930009020004", "36227206271667"] jcb_cards = ["3566002020360505"] discover_cards = ["6011111111111117", "6011000990139424"] -mir_cards = ["2200123456789012", "2204987654321098"] +mir_cards = ["2200123456789019", "2204987654321098"] @pytest.mark.parametrize( @@ -72,7 +72,7 @@ def test_returns_true_on_valid_mastercard(value: str): @pytest.mark.parametrize( "value", - visa_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards + mir_cards, + visa_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards, ) def test_returns_failed_on_valid_mastercard(value: str): """Test returns failed on valid mastercard.""" From 47ac5bdfef8397529c91a95fb7a1b9f0aa760db7 Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 21:28:50 +0300 Subject: [PATCH 6/8] fix: rearrangement of imports --- src/validators/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/validators/__init__.py b/src/validators/__init__.py index e29373d..34d23ec 100644 --- a/src/validators/__init__.py +++ b/src/validators/__init__.py @@ -49,8 +49,8 @@ "discover", "jcb", "mastercard", - "visa", "unionpay", + "visa", "mir", # country "calling_code", From 49934fbadbc1496e1b941ae5c16f5a67c5d91aeb Mon Sep 17 00:00:00 2001 From: "a.shilov" Date: Wed, 2 Apr 2025 21:34:21 +0300 Subject: [PATCH 7/8] fix(tests): update imports --- src/validators/__init__.py | 2 +- tests/test_card.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/validators/__init__.py b/src/validators/__init__.py index 34d23ec..daa31f2 100644 --- a/src/validators/__init__.py +++ b/src/validators/__init__.py @@ -2,7 +2,7 @@ # local from .between import between -from .card import amex, card_number, diners, discover, jcb, mastercard, unionpay, visa, mir +from .card import amex, card_number, diners, discover, jcb, mastercard, mir, unionpay, visa from .country import calling_code, country_code, currency from .cron import cron from .crypto_addresses import bsc_address, btc_address, eth_address, trx_address diff --git a/tests/test_card.py b/tests/test_card.py index d60de92..8dc3d4b 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -12,9 +12,9 @@ discover, jcb, mastercard, + mir, unionpay, visa, - mir ) visa_cards = ["4242424242424242", "4000002760003184"] From cfdb7b689cb7eea11700dae037d3959900f91f24 Mon Sep 17 00:00:00 2001 From: Yozachar <38415384+yozachar@users.noreply.github.com> Date: Sat, 26 Apr 2025 22:05:37 +0530 Subject: [PATCH 8/8] chore: formatting --- src/validators/card.py | 4 +-- tests/test_card.py | 58 +++++++++++++++++++++++++++++++++++++----- 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/src/validators/card.py b/src/validators/card.py index 3fbe0ff..94b6637 100644 --- a/src/validators/card.py +++ b/src/validators/card.py @@ -200,9 +200,9 @@ def mir(value: str, /): Examples: >>> mir('2200123456789019') - # Output: True + True >>> mir('4242424242424242') - # Output: ValidationError(func=mir, args={'value': '4242424242424242'}) + ValidationError(func=mir, args={'value': '4242424242424242'}) Args: value: diff --git a/tests/test_card.py b/tests/test_card.py index 8dc3d4b..d004392 100644 --- a/tests/test_card.py +++ b/tests/test_card.py @@ -43,7 +43,15 @@ def test_returns_true_on_valid_card_number(value: str): assert card_number(value) -@pytest.mark.parametrize("value", ["4242424242424240", "4000002760003180", "400000276000318X", "220012345678901X"]) +@pytest.mark.parametrize( + "value", + [ + "4242424242424240", + "4000002760003180", + "400000276000318X", + "220012345678901X", + ], +) def test_returns_failed_on_valid_card_number(value: str): """Test returns failed on valid card number.""" assert isinstance(card_number(value), ValidationError) @@ -87,7 +95,13 @@ def test_returns_true_on_valid_amex(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards + mir_cards, + visa_cards + + mastercard_cards + + unionpay_cards + + diners_cards + + jcb_cards + + discover_cards + + mir_cards, ) def test_returns_failed_on_valid_amex(value: str): """Test returns failed on valid amex.""" @@ -102,7 +116,13 @@ def test_returns_true_on_valid_unionpay(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + diners_cards + jcb_cards + discover_cards + mir_cards, + visa_cards + + mastercard_cards + + amex_cards + + diners_cards + + jcb_cards + + discover_cards + + mir_cards, ) def test_returns_failed_on_valid_unionpay(value: str): """Test returns failed on valid unionpay.""" @@ -117,7 +137,13 @@ def test_returns_true_on_valid_diners(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + jcb_cards + discover_cards + mir_cards, + visa_cards + + mastercard_cards + + amex_cards + + unionpay_cards + + jcb_cards + + discover_cards + + mir_cards, ) def test_returns_failed_on_valid_diners(value: str): """Test returns failed on valid diners.""" @@ -132,7 +158,13 @@ def test_returns_true_on_valid_jcb(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + discover_cards + mir_cards, + visa_cards + + mastercard_cards + + amex_cards + + unionpay_cards + + diners_cards + + discover_cards + + mir_cards, ) def test_returns_failed_on_valid_jcb(value: str): """Test returns failed on valid jcb.""" @@ -147,7 +179,13 @@ def test_returns_true_on_valid_discover(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + mir_cards, + visa_cards + + mastercard_cards + + amex_cards + + unionpay_cards + + diners_cards + + jcb_cards + + mir_cards, ) def test_returns_failed_on_valid_discover(value: str): """Test returns failed on valid discover.""" @@ -162,7 +200,13 @@ def test_returns_true_on_valid_mir(value: str): @pytest.mark.parametrize( "value", - visa_cards + mastercard_cards + amex_cards + unionpay_cards + diners_cards + jcb_cards + discover_cards, + visa_cards + + mastercard_cards + + amex_cards + + unionpay_cards + + diners_cards + + jcb_cards + + discover_cards, ) def test_returns_failed_on_valid_mir(value: str): """Test returns failed on invalid Mir card (other payment systems)."""