Skip to content

Commit dc73116

Browse files
authored
Test/missing image processing tests (#36)
* Add ImageProcessingResponse test * Add ImageProcessing tests * Add service_url fixture where convenient * Add Given/When/Then comments to all tests where missing * Add a new test to greater_or_equal validator * Ignore MyPy protocol classes from coverage * Update Github CI pipeline
1 parent 0598ab8 commit dc73116

30 files changed

+419
-52
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Unit Tests
1+
name: Unit Tests, Test Coverage, and Pre-Commit Hooks
22

33
on:
44
pull_request:
@@ -30,8 +30,7 @@ jobs:
3030
run: coverage run -m pytest -s tests
3131

3232
- name: Test Coverage
33-
# TODO: Change this after finishing tests.
34-
run: coverage report --fail-under=94
33+
run: coverage report --fail-under=96
3534

3635
- name: Run Pre-Commit Hooks
3736
run: pre-commit run --all-files

src/abstract_api/core/mixins/nested_entities_mixin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
class _ResponseFieldProtocol(Protocol):
55
def _init_response_field(self, field: str, value: Any) -> None:
6-
...
6+
... # pragma: no cover
77

88

99
if TYPE_CHECKING:

src/abstract_api/image_processing/image_processing.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
from typing import Any, BinaryIO, ClassVar
33

44
from ..core.bases import BaseService
5-
from ..core.exceptions import ClientRequestError
65
from ..core.validators import numerical
76
from .image_processing_response import ImageProcessingResponse
87
from .strategies import BaseStrategy
@@ -107,7 +106,7 @@ def _process(
107106
) -> ImageProcessingResponse:
108107

109108
if image is None and url is None:
110-
raise ClientRequestError("Image or URL must be passed")
109+
raise ValueError("Image or URL must be passed")
111110

112111
numerical.between("quality", quality, 0, 100)
113112

src/abstract_api/image_processing/strategies/_mixins/_json_representable_protocol.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@
44
class JSONRepresentableProtocol(Protocol):
55
"""MyPy protocol to indicate a class having .json() method."""
66
def json(self) -> dict[str, int | str]: # noqa: D102
7-
...
7+
... # pragma: no cover

tests/avatars/test_avatars.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,33 +19,48 @@ def service(self) -> Avatars:
1919
def test__validate_params(
2020
self, service, image_size, font_size, char_limit, image_format, mocker
2121
):
22+
# Given
2223
mocked__validate_params = mocker.patch.object(
2324
service,
2425
"_validate_params",
2526
wraps=service._validate_params
2627
)
28+
29+
# Then
2730
with pytest.raises(ClientRequestError):
31+
# When
2832
service.create("", image_size=image_size)
2933
assert mocked__validate_params.call_count == 1
34+
35+
# Then
3036
with pytest.raises(ClientRequestError):
37+
# When
3138
service.create("", font_size=font_size)
3239
assert mocked__validate_params.call_count == 2
40+
41+
# Then
3342
with pytest.raises(ClientRequestError):
43+
# When
3444
service.create("", char_limit=char_limit)
3545
assert mocked__validate_params.call_count == 3
46+
47+
# Then
3648
with pytest.raises(ClientRequestError):
49+
# When
3750
service.create("", image_format=image_format)
3851
assert mocked__validate_params.call_count == 4
3952

4053
def test_create(self, service, base_url, requests_mock):
54+
# Given
4155
url = base_url.format(subdomain=Avatars._subdomain)
4256
content = b'some-john-doe-avatar-content'
4357
name = "John Doe"
44-
4558
requests_mock.get(url, content=content)
4659

60+
# When
4761
response = service.create(name=name)
4862

63+
# Then
4964
assert isinstance(response, AvatarsResponse)
5065
assert response.content == content
5166
assert response.meta.body == content

tests/company_enrichment/test_company_enrichment.py

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,28 @@ class TestCompanyEnrichment:
1010
def service(self) -> CompanyEnrichment:
1111
return CompanyEnrichment(api_key="no-api-key")
1212

13+
@pytest.fixture
14+
def service_url(self, base_url, service):
15+
return base_url.format(subdomain=service._subdomain)
16+
1317
def test_check(
1418
self,
1519
service,
20+
service_url,
1621
company_enrichment_sample,
17-
base_url,
1822
requests_mock,
1923
mocker
2024
):
21-
url = base_url.format(subdomain=CompanyEnrichment._subdomain)
22-
requests_mock.get(url, json=company_enrichment_sample)
25+
# Given
26+
requests_mock.get(service_url, json=company_enrichment_sample)
2327
mocked__service_request = mocker.patch.object(
2428
service, "_service_request", wraps=service._service_request
2529
)
2630

31+
# When
2732
response = service.check(domain=company_enrichment_sample["domain"])
2833

34+
# Then
2935
assert isinstance(response, CompanyEnrichmentResponse)
3036
mocked__service_request.assert_called_once_with(
3137
_response_class=CompanyEnrichmentResponse,
@@ -46,27 +52,29 @@ def test_check(
4652
def test_check_with_response_fields(
4753
self,
4854
service,
55+
service_url,
4956
company_enrichment_sample,
50-
base_url,
5157
requests_mock,
5258
mocker
5359
):
54-
url = base_url.format(subdomain=CompanyEnrichment._subdomain)
60+
# Given
5561
sample_for_fields = {
5662
"name": "Google",
5763
"domain": "google.com"
5864
}
5965
selected_fields = service._prepare_selected_fields(sample_for_fields.keys())
60-
requests_mock.get(url, json=sample_for_fields)
66+
requests_mock.get(service_url, json=sample_for_fields)
6167
mocked__service_request = mocker.patch.object(
6268
service, "_service_request", wraps=service._service_request
6369
)
6470

71+
# When
6572
response = service.check(
6673
domain=company_enrichment_sample["domain"],
6774
fields=selected_fields
6875
)
6976

77+
# Then
7078
assert isinstance(response, CompanyEnrichmentResponse)
7179
assert response.name == sample_for_fields["name"]
7280
assert response.domain == sample_for_fields["domain"]

tests/company_enrichment/test_company_enrichment_response.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@
66

77
class TestCompanyEnrichmentResponse:
88
def test_instance(self, company_enrichment_sample, mocker):
9+
# Given
910
response = generate_response(company_enrichment_sample)
1011

12+
# When
1113
instance = CompanyEnrichmentResponse(response, RESPONSE_FIELDS)
1214

15+
# Then
1316
assert_response_fields(
1417
instance, RESPONSE_FIELDS, company_enrichment_sample, mocker
1518
)

tests/core/bases/test__base_response.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,23 @@
66

77
class TestBaseResponseMeta:
88
def test_instance(self, content_response):
9+
# When
910
meta = BaseResponseMeta(content_response)
11+
12+
# Then
1013
assert meta.http_status == content_response.status_code
1114
assert meta.body == content_response.content
1215

1316

1417
class TestBaseResponse:
1518
def test_instance(self, blank_response, mocker):
19+
# Given
1620
mocker.patch.object(
1721
BaseResponse, "_meta_class", BaseResponseMeta, create=True
1822
)
23+
24+
# When
1925
base_response = BaseResponse(blank_response)
26+
27+
# Then
2028
assert isinstance(base_response.meta, BaseResponseMeta)

tests/core/bases/test_base_service.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -35,36 +35,53 @@ def child_service_url(self, child_service):
3535
return child_service._BaseService__service_url()
3636

3737
def test__read_api_key_from_env(self, child_service, mocker):
38+
# Given
3839
env_key = "ABSTRACTAPI_{service_name}_API_KEY".format(
3940
service_name=child_service._service_name_env_var
4041
)
4142
value = "some-api-key"
4243
mocker.patch.dict(os.environ, {env_key: value})
43-
assert child_service._read_api_key_from_env() == value
44+
45+
# When
46+
api_key = child_service._read_api_key_from_env()
47+
48+
# Then
49+
assert api_key == value
4450

4551
def test_init_without_api_key(self, mocker):
52+
# Given
4653
mocked_read = mocker.patch.object(
4754
_ChildService,
4855
"_read_api_key_from_env",
4956
wraps=_ChildService._read_api_key_from_env
5057
)
5158

59+
# Then
5260
with pytest.raises(ValueError):
53-
_ChildService()
61+
_ChildService() # When
5462
mocked_read.assert_called_once()
5563

5664
def test___service_url(self, base_url, mocker):
65+
# Given
5766
service = "test"
5867
action = "testing"
5968
mocker.patch.object(BaseService, "_subdomain", service, create=True)
60-
assert BaseService._BaseService__service_url(self=BaseService) == base_url.format(subdomain=service)
61-
assert BaseService._BaseService__service_url(self=BaseService, action=action) == base_url.format(subdomain=service) + action
69+
70+
# When
71+
service_url = BaseService._BaseService__service_url(self=BaseService)
72+
service_url_with_action = BaseService._BaseService__service_url(self=BaseService, action=action)
73+
74+
# Then
75+
assert service_url == base_url.format(subdomain=service)
76+
assert service_url_with_action == base_url.format(subdomain=service) + action
6277

6378
@pytest.mark.parametrize("method", ["PUT", "PATCH", "DELETE"])
6479
def test__service_request_with_invalid_method(
6580
self, method, child_service,
6681
):
82+
# Then
6783
with pytest.raises(ClientRequestError):
84+
# When
6885
child_service._service_request(
6986
_response_class=_ChildServiceResponse,
7087
_method=method
@@ -77,16 +94,19 @@ def test__service_request_get(
7794
requests_mock,
7895
mocker
7996
):
97+
# Given
8098
service_params = {"param1": "value", "param2": None}
8199
requests_mock.get(child_service_url, status_code=requests.codes.OK)
82100
mocked_request = mocker.patch.object(
83101
requests, "request", wraps=requests.request
84102
)
85103

104+
# When
86105
child_service._service_request(
87106
_ChildServiceResponse, **service_params
88107
)
89108

109+
# Then
90110
mocked_request.assert_called_once_with(
91111
method="GET",
92112
url=child_service_url,
@@ -113,6 +133,7 @@ def test__service_request_post(
113133
requests_mock,
114134
mocker
115135
):
136+
# Given
116137
requests_mock.post(child_service_url, content=b"")
117138
mocked_request = mocker.patch.object(
118139
requests, "request", wraps=requests.request
@@ -126,13 +147,17 @@ def test__service_request_post(
126147
request_kwargs["_files"] = files
127148
if body is not None:
128149
request_kwargs["_body"] = body
129-
child_service._service_request(**request_kwargs)
130150

131151
call_kwargs = {"method": "POST", "url": child_service_url}
132152
if files is not None:
133153
call_kwargs["files"] = files
134154
if body is not None:
135155
call_kwargs["json"] = body
156+
157+
# When
158+
child_service._service_request(**request_kwargs)
159+
160+
# Then
136161
mocked_request.assert_called_once_with(**call_kwargs)
137162

138163
@pytest.mark.parametrize(
@@ -145,10 +170,14 @@ def test__service_request_unaccepted_status_code(
145170
child_service_url,
146171
requests_mock
147172
):
173+
# Given
148174
requests_mock.get(
149175
child_service_url, status_code=status_code
150176
)
177+
178+
# Then
151179
with pytest.raises(APIRequestError):
180+
# When
152181
child_service._service_request(_ChildServiceResponse)
153182

154183
def test__service_request_parsing_error(
@@ -157,10 +186,13 @@ def test__service_request_parsing_error(
157186
blank_response,
158187
mocker
159188
):
189+
# Given
160190
mocked_request = mocker.patch.object(requests, "request")
161191
mocked_request.return_value = blank_response
162192

193+
# Then
163194
with pytest.raises(ResponseParseError):
195+
# When
164196
child_service._service_request(
165197
_ChildServiceResponse,
166198
_response_class_kwargs={"key": "unexpected"}

tests/core/bases/test_file_response.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44

55
class TestFileResponse:
66
def test_initialization(self, content_response):
7+
# When
78
instance = FileResponse(content_response)
89

10+
# Then
911
assert instance.content == content_response.content
1012
assert isinstance(instance.meta, FileResponseMeta)

0 commit comments

Comments
 (0)