Skip to content

Commit

Permalink
Add missing type hints and fix minor issues
Browse files Browse the repository at this point in the history
  • Loading branch information
oschwald committed Jan 28, 2025
1 parent ca8a380 commit b8392b8
Show file tree
Hide file tree
Showing 6 changed files with 167 additions and 152 deletions.
2 changes: 1 addition & 1 deletion minfraud/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@


class _Serializable(SimpleEquality):
def to_dict(self):
def to_dict(self) -> dict:
"""Returns a dict of the object suitable for serialization."""
result = {}
for key, value in self.__dict__.items():
Expand Down
10 changes: 5 additions & 5 deletions minfraud/request.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import re
import unicodedata
import warnings
from typing import Any
from typing import Any, Optional

from voluptuous import MultipleInvalid

Expand Down Expand Up @@ -339,7 +339,7 @@ def maybe_hash_email(transaction) -> None:
email["address"] = hashlib.md5(address.encode("UTF-8")).hexdigest()


def _clean_domain(domain):
def _clean_domain(domain: str) -> str:
domain = domain.strip().rstrip(".").encode("idna").decode("ASCII")

domain = re.sub(r"(?:\.com){2,}$", ".com", domain)
Expand All @@ -348,14 +348,14 @@ def _clean_domain(domain):
idx = domain.rfind(".")
if idx != -1:
tld = domain[idx + 1 :]
if tld in _TYPO_TLDS:
domain = domain[:idx] + "." + _TYPO_TLDS.get(tld)
if typo_tld := _TYPO_TLDS.get(tld):
domain = domain[:idx] + "." + typo_tld

domain = _TYPO_DOMAINS.get(domain, domain)
return _EQUIVALENT_DOMAINS.get(domain, domain)


def _clean_email(address):
def _clean_email(address: str) -> tuple[Optional[str], Optional[str]]:
address = address.lower().strip()

at_idx = address.rfind("@")
Expand Down
63 changes: 32 additions & 31 deletions tests/test_models.py
Original file line number Diff line number Diff line change
@@ -1,28 +1,29 @@
import unittest
from typing import Any, Union

from minfraud.models import *


class TestModels(unittest.TestCase):
def setUp(self):
def setUp(self) -> None:
self.maxDiff = 20_000

def test_billing_address(self):
address = BillingAddress(**self.address_dict)
def test_billing_address(self) -> None:
address = BillingAddress(**self.address_dict) # type: ignore[arg-type]
self.check_address(address)

def test_shipping_address(self):
def test_shipping_address(self) -> None:
address_dict = self.address_dict
address_dict["is_high_risk"] = False
address_dict["distance_to_billing_address"] = 200

address = ShippingAddress(**address_dict)
address = ShippingAddress(**address_dict) # type:ignore[arg-type]
self.check_address(address)
self.assertEqual(False, address.is_high_risk)
self.assertEqual(200, address.distance_to_billing_address)

@property
def address_dict(self):
def address_dict(self) -> dict[str, Union[bool, float]]:
return {
"is_in_ip_country": True,
"latitude": 43.1,
Expand All @@ -31,14 +32,14 @@ def address_dict(self):
"is_postal_in_city": True,
}

def check_address(self, address):
def check_address(self, address) -> None:
self.assertEqual(True, address.is_in_ip_country)
self.assertEqual(True, address.is_postal_in_city)
self.assertEqual(100, address.distance_to_ip_location)
self.assertEqual(32.1, address.longitude)
self.assertEqual(43.1, address.latitude)

def test_credit_card(self):
def test_credit_card(self) -> None:
cc = CreditCard(
issuer={"name": "Bank"},
brand="Visa",
Expand All @@ -59,7 +60,7 @@ def test_credit_card(self):
self.assertEqual(True, cc.is_issued_in_billing_address_country)
self.assertEqual("credit", cc.type)

def test_device(self):
def test_device(self) -> None:
id = "b643d445-18b2-4b9d-bad4-c9c4366e402a"
last_seen = "2016-06-08T14:16:38Z"
local_time = "2016-06-10T14:19:10-08:00"
Expand All @@ -75,7 +76,7 @@ def test_device(self):
self.assertEqual(last_seen, device.last_seen)
self.assertEqual(local_time, device.local_time)

def test_disposition(self):
def test_disposition(self) -> None:
disposition = Disposition(
action="accept",
reason="default",
Expand All @@ -86,7 +87,7 @@ def test_disposition(self):
self.assertEqual("default", disposition.reason)
self.assertEqual("custom rule label", disposition.rule_label)

def test_email(self):
def test_email(self) -> None:
first_seen = "2016-01-01"
email = Email(
first_seen=first_seen,
Expand All @@ -100,21 +101,21 @@ def test_email(self):
self.assertEqual(True, email.is_free)
self.assertEqual(False, email.is_high_risk)

def test_email_domain(self):
def test_email_domain(self) -> None:
first_seen = "2016-01-01"
domain = EmailDomain(
first_seen=first_seen,
)

self.assertEqual(first_seen, domain.first_seen)

def test_geoip2_location(self):
def test_geoip2_location(self) -> None:
time = "2015-04-19T12:59:23-01:00"
location = GeoIP2Location(local_time=time, latitude=5)
self.assertEqual(time, location.local_time)
self.assertEqual(5, location.latitude)

def test_ip_address(self):
def test_ip_address(self) -> None:
time = "2015-04-19T12:59:23-01:00"
address = IPAddress(
["en"],
Expand Down Expand Up @@ -175,15 +176,15 @@ def test_ip_address(self):
address.risk_reasons[1].reason,
)

def test_empty_address(self):
def test_empty_address(self) -> None:
address = IPAddress([])
self.assertEqual([], address.risk_reasons)

def test_score_ip_address(self):
def test_score_ip_address(self) -> None:
address = ScoreIPAddress(risk=99)
self.assertEqual(99, address.risk)

def test_ip_address_locales(self):
def test_ip_address_locales(self) -> None:
loc = IPAddress(
["fr"],
country={"names": {"fr": "Country"}},
Expand All @@ -193,7 +194,7 @@ def test_ip_address_locales(self):
self.assertEqual("City", loc.city.name)
self.assertEqual("Country", loc.country.name)

def test_issuer(self):
def test_issuer(self) -> None:
phone = "132-342-2131"

issuer = Issuer(
Expand All @@ -208,7 +209,7 @@ def test_issuer(self):
self.assertEqual(phone, issuer.phone_number)
self.assertEqual(True, issuer.matches_provided_phone_number)

def test_phone(self):
def test_phone(self) -> None:
phone = Phone(
country="US",
is_voip=True,
Expand All @@ -221,7 +222,7 @@ def test_phone(self):
self.assertEqual("Verizon/1", phone.network_operator)
self.assertEqual("fixed", phone.number_type)

def test_warning(self):
def test_warning(self) -> None:
code = "INVALID_INPUT"
msg = "Input invalid"

Expand All @@ -231,7 +232,7 @@ def test_warning(self):
self.assertEqual(msg, warning.warning)
self.assertEqual("/first/second", warning.input_pointer)

def test_reason(self):
def test_reason(self) -> None:
code = "EMAIL_ADDRESS_NEW"
msg = "Riskiness of newly-sighted email address"

Expand All @@ -240,7 +241,7 @@ def test_reason(self):
self.assertEqual(code, reason.code)
self.assertEqual(msg, reason.reason)

def test_risk_score_reason(self):
def test_risk_score_reason(self) -> None:
multiplier = 0.34
code = "EMAIL_ADDRESS_NEW"
msg = "Riskiness of newly-sighted email address"
Expand All @@ -254,7 +255,7 @@ def test_risk_score_reason(self):
self.assertEqual(code, reason.reasons[0].code)
self.assertEqual(msg, reason.reasons[0].reason)

def test_score(self):
def test_score(self) -> None:
id = "b643d445-18b2-4b9d-bad4-c9c4366e402a"
response = {
"id": id,
Expand All @@ -264,7 +265,7 @@ def test_score(self):
"ip_address": {"risk": 99},
"warnings": [{"code": "INVALID_INPUT"}],
}
score = Score(**response)
score = Score(**response) # type: ignore[arg-type]

self.assertEqual(id, score.id)
self.assertEqual(10.01, score.funds_remaining)
Expand All @@ -275,17 +276,17 @@ def test_score(self):

self.assertEqual(response, score.to_dict())

def test_insights(self):
def test_insights(self) -> None:
response = self.factors_response()
del response["risk_score_reasons"]
del response["subscores"]
insights = Insights(None, **response)
insights = Insights(None, **response) # type: ignore[arg-type]
self.check_insights_data(insights, response["id"])
self.assertEqual(response, insights.to_dict())

def test_factors(self):
def test_factors(self) -> None:
response = self.factors_response()
factors = Factors(None, **response)
factors = Factors(None, **response) # type: ignore[arg-type]
self.check_insights_data(factors, response["id"])
self.check_risk_score_reasons_data(factors.risk_score_reasons)
self.assertEqual(0.01, factors.subscores.avs_result)
Expand Down Expand Up @@ -317,7 +318,7 @@ def test_factors(self):

self.assertEqual(response, factors.to_dict())

def factors_response(self):
def factors_response(self) -> dict[str, Any]:
return {
"id": "b643d445-18b2-4b9d-bad4-c9c4366e402a",
"disposition": {"action": "reject"},
Expand Down Expand Up @@ -373,7 +374,7 @@ def factors_response(self):
],
}

def check_insights_data(self, insights, uuid):
def check_insights_data(self, insights, uuid) -> None:
self.assertEqual("US", insights.ip_address.country.iso_code)
self.assertEqual(False, insights.ip_address.country.is_in_european_union)
self.assertEqual(True, insights.credit_card.is_business)
Expand All @@ -395,7 +396,7 @@ def check_insights_data(self, insights, uuid):
self.assertEqual("INVALID_INPUT", insights.warnings[0].code)
self.assertIsInstance(insights.warnings, list, "warnings is a list")

def check_risk_score_reasons_data(self, reasons):
def check_risk_score_reasons_data(self, reasons) -> None:
self.assertEqual(1, len(reasons))
self.assertEqual(45, reasons[0].multiplier)
self.assertEqual(1, len(reasons[0].reasons))
Expand Down
10 changes: 5 additions & 5 deletions tests/test_request.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@


class TestRequest(unittest.TestCase):
def test_maybe_hash_email(self):
def test_maybe_hash_email(self) -> None:
tests = [
{
"name": "no email",
Expand Down Expand Up @@ -171,7 +171,7 @@ def test_maybe_hash_email(self):

self.assertEqual(test["expected"], transaction)

def test_clean_credit_card(self):
def test_clean_credit_card(self) -> None:
tests = [
{
"name": "deprecated last_4_digits is cleaned to last_digits",
Expand Down Expand Up @@ -217,7 +217,7 @@ def test_clean_credit_card(self):
self.assertEqual(test["expected"], transaction)


def test_clean_email():
def test_clean_email() -> None:
tests = [
{"input": "", "output": None},
{"input": "fasfs", "output": None},
Expand Down Expand Up @@ -256,5 +256,5 @@ def test_clean_email():
]

for test in tests:
got, _ = _clean_email(test["input"])
assert test["output"] == got
got, _ = _clean_email(test["input"]) # type: ignore
assert test["output"] == got # type: ignore
Loading

0 comments on commit b8392b8

Please sign in to comment.