Skip to content

Commit

Permalink
ci: enhance coverage + refactoring on unit tests
Browse files Browse the repository at this point in the history
Signed-off-by: ATTY Lionel <[email protected]>
  • Loading branch information
ATTY Lionel committed Aug 20, 2024
1 parent 5a2d858 commit e359e12
Show file tree
Hide file tree
Showing 7 changed files with 141 additions and 9 deletions.
14 changes: 8 additions & 6 deletions cruft_helloworld/services/globe_emoji_with_geoip.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,20 @@ def get_external_ipv4(
try:
# https://requests.readthedocs.io/en/master/user/quickstart/#timeouts
raw = requests.get(URI_TO_DDG_API, timeout=default_timeout)
answer: str = raw.json().get("Answer", "")
json_response = raw.json()
answer: str = json_response.get("Answer", "")
logger.debug("request(%s).Answer -> %s", URI_TO_DDG_API, answer)
# https://regex101.com/r/NMdWXw/1
regex = (
r"Your IP address is (?P<ip_address>[0-9]*\.[0-9]+\.[0-9]+\.[0-9]+) in.*"
)
match = re.match(regex, answer)
if match is None:
logger.error(f"Can't extract ip_address from: {answer}")
else:
# https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_address
external_ip = ip_address(match["ip_address"])
assert match, f"Can't extract ip_address from: {answer}"

# https://docs.python.org/3/library/ipaddress.html#ipaddress.ip_address
external_ip = ip_address(match["ip_address"])
except AssertionError as err:
logger.error(str(err))
except requests.exceptions.Timeout:
logger.error(
"Timeout (=%.5f) occurred on request: requests.get(%s)!",
Expand Down
19 changes: 18 additions & 1 deletion poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ ipython = "*"

[tool.poetry.group.dev.dependencies]
pytest-mock = "^3.14.0"
requests-mock = "^1.12.1"

[tool.black]
line-length = 88
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ branch = True
include = cruft_helloworld/*
omit =
*/tests/*
cruft_helloworld/tools/click_default_group.py

[coverage:report]
show_missing = True
Expand Down
37 changes: 37 additions & 0 deletions tests/test_get_external_ipv4_errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import logging

from cruft_helloworld import __project_name__
from cruft_helloworld.services.globe_emoji_with_geoip import (
URI_TO_DDG_API,
get_external_ipv4,
)


def test_cant_extract_ip_address_error(requests_mock, caplog):
mock_external_ipv4 = "Your IP address is dummy_ip in"
mock_request_response = f'{{"Answer": "{mock_external_ipv4}"}}'
requests_mock.get(URI_TO_DDG_API, text=mock_request_response)

with caplog.at_level(logging.ERROR):
assert get_external_ipv4() is None

records = [record for record in caplog.records if __project_name__ in record.name]
assert len(records) == 1, records
assert records[0].levelname == "ERROR"
assert records[0].message == f"Can't extract ip_address from: {mock_external_ipv4}"


def test_not_valid_json_request_response_error(requests_mock, caplog):
mock_request_response = ""
requests_mock.get(URI_TO_DDG_API, text=mock_request_response)

with caplog.at_level(logging.ERROR):
assert get_external_ipv4() is None

records = [record for record in caplog.records if __project_name__ in record.name]
assert len(records) == 1, records
assert records[0].levelname == "ERROR"
expected_msg_error = (
f"Can't perform internet request: requests.get({URI_TO_DDG_API})!"
)
assert records[0].message == expected_msg_error
4 changes: 3 additions & 1 deletion tests/test_globe_emoji_with_geoip.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,9 @@ def test_get_external_ipv4():
Basic test for validate the external ip address retrieve (from DuckDuckGo JSON API)
=> 4 digits separate by '.'
"""
assert len(list(map(int, format(get_external_ipv4()).split(".")))) == 4
external_ipv4 = get_external_ipv4()
assert external_ipv4
assert len(list(map(int, format(external_ipv4).split(".")))) == 4


@pytest.mark.use_internet
Expand Down
74 changes: 73 additions & 1 deletion tests/test_globe_emoji_with_geoip_errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest
import requests
from geoip import IPInfo

from cruft_helloworld import __project_name__
from cruft_helloworld.services.globe_emoji_with_geoip import (
Expand Down Expand Up @@ -48,7 +49,78 @@ def test_find_globe_emoji_with_errors(
records = [record for record in caplog.records if __project_name__ in record.name]
assert len(records) == 2, records
assert records[0].levelname == "ERROR"
# error_msg = f"Can't perform internet request: requests.get({URI_TO_DDG_API})!"
assert records[0].message == error_log_msg
assert records[1].levelname == "DEBUG"
assert records[1].message == "No external IP found"


def test_geolite2_lookup_return_no_match_error(
requests_mock,
mocker,
caplog,
default_iso_code_continent_emoji,
):
mock_external_ipv4 = "0.0.0.0"
mock_request_answer = f"Your IP address is {mock_external_ipv4} in"
mock_request_response = f'{{"Answer": "{mock_request_answer}"}}'
requests_mock.get(URI_TO_DDG_API, text=mock_request_response)

mocker.patch(
"cruft_helloworld.services.globe_emoji_with_geoip.geolite2.lookup",
return_value=None,
)

with caplog.at_level(logging.DEBUG):
result_emoji = find_globe_emoji_from_external_ip()
expected_emoji = default_iso_code_continent_emoji.value
assert result_emoji == expected_emoji

records = [record for record in caplog.records if __project_name__ in record.name]
assert len(records) == 2, records
assert records[0].levelname == "DEBUG"
expected_debug_msg = f"request({URI_TO_DDG_API}).Answer -> {mock_request_answer}"
assert records[0].message == expected_debug_msg
assert records[1].levelname == "DEBUG"
expected_debug_msg = (
f"Can't provides information about the located IP = {mock_external_ipv4}"
)
assert records[1].message == expected_debug_msg


def test_geolite2_lookup_return_an_unknown_match_continent_error(
requests_mock,
mocker,
caplog,
default_iso_code_continent_emoji,
):
mock_external_ipv4 = "0.0.0.0"
mock_request_answer = f"Your IP address is {mock_external_ipv4} in"
mock_request_response = f'{{"Answer": "{mock_request_answer}"}}'
requests_mock.get(URI_TO_DDG_API, text=mock_request_response)

unknown_code_continent = "UK"
mock_ipinfo = IPInfo(
ip=mock_external_ipv4, data={"continent": {"code": unknown_code_continent}}
)
mocker.patch(
"cruft_helloworld.services.globe_emoji_with_geoip.geolite2.lookup",
return_value=mock_ipinfo,
)

with caplog.at_level(logging.DEBUG):
result_emoji = find_globe_emoji_from_external_ip()
expected_emoji = default_iso_code_continent_emoji.value
assert result_emoji == expected_emoji

records = [record for record in caplog.records if __project_name__ in record.name]
assert len(records) == 3, records
assert records[0].levelname == "DEBUG"
expected_debug_msg = f"request({URI_TO_DDG_API}).Answer -> {mock_request_answer}"
assert records[0].message == expected_debug_msg
assert records[1].levelname == "DEBUG"
assert records[1].message == f"Match result from geolite = {mock_ipinfo}"
assert records[2].levelname == "ERROR"
assert records[2].message == (
f"'{unknown_code_continent}' is not a valid ISO Code continent emoji "
f"- Hint: Need to update `tools.enums.IsoCodeContinentEmoji`"
)

0 comments on commit e359e12

Please sign in to comment.