Skip to content

Commit 6163be5

Browse files
Merge pull request #664 from adamtheturtle/inactive-more
Full tests for inactive project
2 parents 888c082 + bd9fa62 commit 6163be5

File tree

12 files changed

+312
-6
lines changed

12 files changed

+312
-6
lines changed

src/mock_vws/_constants.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ class ResultCodes(Enum):
5050
REQUEST_QUOTA_REACHED = 'RequestQuotaReached'
5151
TARGET_STATUS_NOT_SUCCESS = 'TargetStatusNotSuccess'
5252
PROJECT_INACTIVE = 'ProjectInactive'
53+
INACTIVE_PROJECT = 'InactiveProject'
5354

5455

5556
class TargetStatuses(Enum):

src/mock_vws/_mock_web_query_api.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
from requests_mock.request import _RequestObjectProxy
2121
from requests_mock.response import _Context
2222

23-
from mock_vws._constants import ResultCodes, TargetStatuses
23+
from mock_vws._constants import ResultCodes, States, TargetStatuses
2424
from mock_vws._mock_common import Route, json_dump, set_content_length_header
2525
from mock_vws._mock_web_services_api import MockVuforiaWebServicesAPI, Target
2626

@@ -29,6 +29,46 @@
2929
ROUTES = set([])
3030

3131

32+
@wrapt.decorator
33+
def validate_project_state(
34+
wrapped: Callable[..., str],
35+
instance: Any,
36+
args: Tuple[_RequestObjectProxy, _Context],
37+
kwargs: Dict,
38+
) -> str:
39+
"""
40+
Validate the state of the project.
41+
42+
Args:
43+
wrapped: An endpoint function for `requests_mock`.
44+
instance: The class that the endpoint function is in.
45+
args: The arguments given to the endpoint function.
46+
kwargs: The keyword arguments given to the endpoint function.
47+
48+
Returns:
49+
The result of calling the endpoint.
50+
A `FORBIDDEN` response with an InactiveProject result code if the
51+
project is inactive.
52+
"""
53+
_, context = args
54+
55+
if instance.mock_web_services_api.state != States.PROJECT_INACTIVE:
56+
return wrapped(*args, **kwargs)
57+
58+
context.status_code = codes.FORBIDDEN
59+
transaction_id = uuid.uuid4().hex
60+
result_code = ResultCodes.INACTIVE_PROJECT.value
61+
62+
# The response has an unusual format of separators, so we construct it
63+
# manually.
64+
return (
65+
'{"transaction_id": '
66+
f'"{transaction_id}",'
67+
f'"result_code":"{result_code}"'
68+
'}'
69+
)
70+
71+
3272
@wrapt.decorator
3373
def validate_image_format(
3474
wrapped: Callable[..., str],
@@ -593,6 +633,7 @@ def decorator(method: Callable[..., str]) -> Callable[..., str]:
593633
validate_content_type_header,
594634
validate_accept_header,
595635
validate_auth_header_exists,
636+
validate_project_state,
596637
set_content_length_header,
597638
]
598639

src/mock_vws/_mock_web_services_api.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
validate_name_length,
4646
validate_name_type,
4747
validate_not_invalid_json,
48+
validate_project_state,
4849
validate_width,
4950
)
5051

@@ -179,6 +180,7 @@ def decorator(method: Callable[..., str]) -> Callable[..., str]:
179180
decorators = [
180181
parse_target_id,
181182
validate_authorization,
183+
validate_project_state,
182184
validate_metadata_size,
183185
validate_metadata_encoding,
184186
validate_metadata_type,

src/mock_vws/_validators.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@
2424
from mock_vws._constants import ResultCodes
2525
from mock_vws._mock_common import json_dump
2626

27+
from ._constants import States
28+
2729

2830
def compute_hmac_base64(key: bytes, data: bytes) -> bytes:
2931
"""
@@ -126,6 +128,44 @@ def validate_active_flag(
126128
return json_dump(body)
127129

128130

131+
@wrapt.decorator
132+
def validate_project_state(
133+
wrapped: Callable[..., str],
134+
instance: Any,
135+
args: Tuple[_RequestObjectProxy, _Context],
136+
kwargs: Dict,
137+
) -> str:
138+
"""
139+
Validate the state of the project.
140+
141+
Args:
142+
wrapped: An endpoint function for `requests_mock`.
143+
instance: The class that the endpoint function is in.
144+
args: The arguments given to the endpoint function.
145+
kwargs: The keyword arguments given to the endpoint function.
146+
147+
Returns:
148+
The result of calling the endpoint.
149+
A `FORBIDDEN` response with a PROJECT_INACTIVE result code if the
150+
project is inactive.
151+
"""
152+
request, context = args
153+
154+
if instance.state != States.PROJECT_INACTIVE:
155+
return wrapped(*args, **kwargs)
156+
157+
if request.method == 'GET' and 'duplicates' not in request.path:
158+
return wrapped(*args, **kwargs)
159+
160+
context.status_code = codes.FORBIDDEN
161+
162+
body: Dict[str, str] = {
163+
'transaction_id': uuid.uuid4().hex,
164+
'result_code': ResultCodes.PROJECT_INACTIVE.value,
165+
}
166+
return json_dump(body)
167+
168+
129169
@wrapt.decorator
130170
def validate_not_invalid_json(
131171
wrapped: Callable[..., str],

tests/mock_vws/fixtures/credentials.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def inactive_database_keys() -> VuforiaDatabaseKeys:
3434
environ['INACTIVE_VUFORIA_TARGET_MANAGER_DATABASE_NAME'],
3535
server_access_key=os.environ['INACTIVE_VUFORIA_SERVER_ACCESS_KEY'],
3636
server_secret_key=os.environ['INACTIVE_VUFORIA_SERVER_SECRET_KEY'],
37-
client_access_key=os.environ['INACTIVE_VUFORIA_SERVER_ACCESS_KEY'],
38-
client_secret_key=os.environ['INACTIVE_VUFORIA_SERVER_SECRET_KEY'],
37+
client_access_key=os.environ['INACTIVE_VUFORIA_CLIENT_ACCESS_KEY'],
38+
client_secret_key=os.environ['INACTIVE_VUFORIA_CLIENT_SECRET_KEY'],
3939
)
4040
return credentials

tests/mock_vws/test_add_target.py

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -834,3 +834,39 @@ def test_metadata_too_large(
834834
status_code=codes.UNPROCESSABLE_ENTITY,
835835
result_code=ResultCodes.METADATA_TOO_LARGE,
836836
)
837+
838+
839+
@pytest.mark.usefixtures('verify_mock_vuforia_inactive')
840+
class TestInactiveProject:
841+
"""
842+
Tests for inactive projects.
843+
"""
844+
845+
def test_inactive_project(
846+
self,
847+
inactive_database_keys: VuforiaDatabaseKeys,
848+
png_rgb: io.BytesIO,
849+
) -> None:
850+
"""
851+
If the project is inactive, a FORBIDDEN response is returned.
852+
"""
853+
image_data = png_rgb.read()
854+
image_data_encoded = base64.b64encode(image_data).decode('ascii')
855+
856+
data = {
857+
'name': 'example',
858+
'width': 1,
859+
'image': image_data_encoded,
860+
}
861+
862+
response = add_target_to_vws(
863+
vuforia_database_keys=inactive_database_keys,
864+
data=data,
865+
content_type='application/json',
866+
)
867+
868+
assert_vws_failure(
869+
response=response,
870+
status_code=codes.FORBIDDEN,
871+
result_code=ResultCodes.PROJECT_INACTIVE,
872+
)

tests/mock_vws/test_delete_target.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,3 +83,29 @@ def test_processed(
8383
status_code=codes.NOT_FOUND,
8484
result_code=ResultCodes.UNKNOWN_TARGET,
8585
)
86+
87+
88+
@pytest.mark.usefixtures('verify_mock_vuforia_inactive')
89+
class TestInactiveProject:
90+
"""
91+
Tests for inactive projects.
92+
"""
93+
94+
def test_inactive_project(
95+
self,
96+
inactive_database_keys: VuforiaDatabaseKeys,
97+
) -> None:
98+
"""
99+
If the project is inactive, a FORBIDDEN response is returned.
100+
"""
101+
target_id = 'does_not_exist'
102+
response = delete_target(
103+
vuforia_database_keys=inactive_database_keys,
104+
target_id=target_id,
105+
)
106+
107+
assert_vws_failure(
108+
response=response,
109+
status_code=codes.FORBIDDEN,
110+
result_code=ResultCodes.PROJECT_INACTIVE,
111+
)

tests/mock_vws/test_get_duplicates.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@
1717
target_api_request,
1818
wait_for_target_processed,
1919
)
20-
from tests.mock_vws.utils.assertions import assert_vws_response
20+
from tests.mock_vws.utils.assertions import (
21+
assert_vws_failure,
22+
assert_vws_response,
23+
)
2124
from tests.mock_vws.utils.authorization import VuforiaDatabaseKeys
2225

2326

@@ -379,3 +382,28 @@ def test_processing(
379382

380383
assert status_response.json()['status'] == 'processing'
381384
assert response.json()['similar_targets'] == [processed_target_id]
385+
386+
387+
@pytest.mark.usefixtures('verify_mock_vuforia_inactive')
388+
class TestInactiveProject:
389+
"""
390+
Tests for inactive projects.
391+
"""
392+
393+
def test_inactive_project(
394+
self,
395+
inactive_database_keys: VuforiaDatabaseKeys,
396+
) -> None:
397+
"""
398+
If the project is inactive, a FORBIDDEN response is returned.
399+
"""
400+
response = target_duplicates(
401+
target_id=uuid.uuid4().hex,
402+
vuforia_database_keys=inactive_database_keys,
403+
)
404+
405+
assert_vws_failure(
406+
response=response,
407+
status_code=codes.FORBIDDEN,
408+
result_code=ResultCodes.PROJECT_INACTIVE,
409+
)

tests/mock_vws/test_get_target.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import base64
88
import io
9+
import uuid
910

1011
import pytest
1112
from requests import codes
@@ -16,7 +17,10 @@
1617
get_vws_target,
1718
wait_for_target_processed,
1819
)
19-
from tests.mock_vws.utils.assertions import assert_vws_response
20+
from tests.mock_vws.utils.assertions import (
21+
assert_vws_failure,
22+
assert_vws_response,
23+
)
2024
from tests.mock_vws.utils.authorization import VuforiaDatabaseKeys
2125

2226

@@ -249,3 +253,28 @@ def test_success_status(
249253
new_target_record = response.json()['target_record']
250254
new_tracking_rating = new_target_record['tracking_rating']
251255
assert new_tracking_rating == tracking_rating
256+
257+
258+
@pytest.mark.usefixtures('verify_mock_vuforia_inactive')
259+
class TestInactiveProject:
260+
"""
261+
Tests for inactive projects.
262+
"""
263+
264+
def test_inactive_project(
265+
self,
266+
inactive_database_keys: VuforiaDatabaseKeys,
267+
) -> None:
268+
"""
269+
The project's active state does not affect getting a target.
270+
"""
271+
response = get_vws_target(
272+
target_id=uuid.uuid4().hex,
273+
vuforia_database_keys=inactive_database_keys,
274+
)
275+
276+
assert_vws_failure(
277+
response=response,
278+
status_code=codes.NOT_FOUND,
279+
result_code=ResultCodes.UNKNOWN_TARGET,
280+
)

tests/mock_vws/test_query.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1641,3 +1641,46 @@ def test_date_formats(
16411641

16421642
assert_query_success(response=response)
16431643
assert response.json()['results'] == []
1644+
1645+
1646+
@pytest.mark.usefixtures('verify_mock_vuforia_inactive')
1647+
class TestInactiveProject:
1648+
"""
1649+
Tests for inactive projects.
1650+
"""
1651+
1652+
def test_inactive_project(
1653+
self,
1654+
inactive_database_keys: VuforiaDatabaseKeys,
1655+
high_quality_image: io.BytesIO,
1656+
) -> None:
1657+
"""
1658+
If the project is inactive, a FORBIDDEN response is returned.
1659+
"""
1660+
image_content = high_quality_image.getvalue()
1661+
body = {'image': ('image.jpeg', image_content, 'image/jpeg')}
1662+
1663+
response = query(
1664+
vuforia_database_keys=inactive_database_keys,
1665+
body=body,
1666+
)
1667+
1668+
assert_vwq_failure(
1669+
response=response,
1670+
status_code=codes.FORBIDDEN,
1671+
content_type='application/json',
1672+
)
1673+
assert response.json().keys() == {'transaction_id', 'result_code'}
1674+
assert_valid_transaction_id(response=response)
1675+
assert_valid_date_header(response=response)
1676+
result_code = response.json()['result_code']
1677+
transaction_id = response.json()['transaction_id']
1678+
assert result_code == ResultCodes.INACTIVE_PROJECT.value
1679+
# The separators are inconsistent and we test this.
1680+
expected_text = (
1681+
'{"transaction_id": '
1682+
f'"{transaction_id}",'
1683+
f'"result_code":"{result_code}"'
1684+
'}'
1685+
)
1686+
assert response.text == expected_text

0 commit comments

Comments
 (0)