|
1 | 1 | """Tests for the usage of the mock for ``requests``.""" |
2 | 2 |
|
| 3 | +import dataclasses |
3 | 4 | import datetime |
4 | 5 | import email.utils |
5 | 6 | import io |
|
8 | 9 | import zipfile |
9 | 10 | from http import HTTPStatus |
10 | 11 | from urllib.parse import urlparse |
| 12 | +from zoneinfo import ZoneInfo |
11 | 13 |
|
12 | 14 | import httpx |
13 | 15 | import pytest |
|
33 | 35 | RequestRateLimiter, |
34 | 36 | ) |
35 | 37 | from mock_vws.database import CloudDatabase, VuMarkDatabase |
| 38 | +from mock_vws.database_type import DatabaseType |
36 | 39 | from mock_vws.image_matchers import ExactMatcher, StructuralSimilarityMatcher |
37 | 40 | from mock_vws.request_rate_limits import ( |
38 | 41 | DOCUMENTED_REQUEST_RATE_LIMITS, |
|
42 | 45 | ) |
43 | 46 | from mock_vws.states import States |
44 | 47 | from mock_vws.target import ImageTarget, VuMarkTarget |
| 48 | +from mock_vws.target_raters import HardcodedTargetTrackingRater |
45 | 49 | from tests.mock_vws.utils import Endpoint |
46 | 50 | from tests.mock_vws.utils.assertions import assert_vws_failure |
47 | 51 | from tests.mock_vws.utils.usage_test_helpers import ( |
@@ -974,6 +978,74 @@ def test_to_dict_deleted(high_quality_image: io.BytesIO) -> None: |
974 | 978 | new_target = ImageTarget.from_dict(target_dict=target_dict) |
975 | 979 | assert new_target.delete_date == target.delete_date |
976 | 980 |
|
| 981 | + @staticmethod |
| 982 | + def test_round_trip_non_default_fields( |
| 983 | + high_quality_image: io.BytesIO, |
| 984 | + ) -> None: |
| 985 | + """Every field of a target survives a dictionary round trip. |
| 986 | +
|
| 987 | + The target tracking rater is deliberately not preserved: |
| 988 | + ``to_dict`` writes the computed tracking rating and ``from_dict`` |
| 989 | + rebuilds the target with a hardcoded rater which gives that |
| 990 | + rating. |
| 991 | + """ |
| 992 | + gmt = ZoneInfo(key="GMT") |
| 993 | + target = ImageTarget( |
| 994 | + active_flag=False, |
| 995 | + application_metadata="example-metadata", |
| 996 | + current_month_recos=1, |
| 997 | + delete_date=datetime.datetime( |
| 998 | + year=2020, month=1, day=4, tzinfo=gmt |
| 999 | + ), |
| 1000 | + image_value=high_quality_image.getvalue(), |
| 1001 | + last_modified_date=datetime.datetime( |
| 1002 | + year=2020, month=1, day=3, tzinfo=gmt |
| 1003 | + ), |
| 1004 | + name="example", |
| 1005 | + previous_month_recos=2, |
| 1006 | + processing_time_seconds=0.5, |
| 1007 | + reco_rating="example-reco-rating", |
| 1008 | + target_id="example-target-id", |
| 1009 | + target_tracking_rater=HardcodedTargetTrackingRater(rating=4), |
| 1010 | + total_recos=3, |
| 1011 | + upload_date=datetime.datetime( |
| 1012 | + year=2020, month=1, day=2, tzinfo=gmt |
| 1013 | + ), |
| 1014 | + width=1.5, |
| 1015 | + ) |
| 1016 | + # Adding a field to ``ImageTarget`` must mean adding it to this |
| 1017 | + # test, and therefore to the round trip. |
| 1018 | + expected_field_names = { |
| 1019 | + "active_flag", |
| 1020 | + "application_metadata", |
| 1021 | + "current_month_recos", |
| 1022 | + "delete_date", |
| 1023 | + "image_value", |
| 1024 | + "last_modified_date", |
| 1025 | + "name", |
| 1026 | + "previous_month_recos", |
| 1027 | + "processing_time_seconds", |
| 1028 | + "reco_rating", |
| 1029 | + "target_id", |
| 1030 | + "target_tracking_rater", |
| 1031 | + "total_recos", |
| 1032 | + "upload_date", |
| 1033 | + "width", |
| 1034 | + } |
| 1035 | + field_names = { |
| 1036 | + field.name |
| 1037 | + for field in dataclasses.fields(class_or_instance=ImageTarget) |
| 1038 | + } |
| 1039 | + assert field_names == expected_field_names |
| 1040 | + |
| 1041 | + target_dict = target.to_dict() |
| 1042 | + # The dictionary is JSON dump-able |
| 1043 | + assert json.dumps(obj=target_dict) |
| 1044 | + |
| 1045 | + new_target = ImageTarget.from_dict(target_dict=target_dict) |
| 1046 | + assert new_target == target |
| 1047 | + assert new_target.tracking_rating == target.tracking_rating |
| 1048 | + |
977 | 1049 | @staticmethod |
978 | 1050 | def test_vumark_target_to_dict() -> None: |
979 | 1051 | """It is possible to dump a VuMark target to a dictionary and |
@@ -1078,6 +1150,82 @@ def test_custom_request_rate_limits() -> None: |
1078 | 1150 | new_database.request_rate_limits == DOCUMENTED_REQUEST_RATE_LIMITS |
1079 | 1151 | ) |
1080 | 1152 |
|
| 1153 | + @staticmethod |
| 1154 | + def test_round_trip_non_default_fields( |
| 1155 | + high_quality_image: io.BytesIO, |
| 1156 | + ) -> None: |
| 1157 | + """Every field of a database survives a dictionary round trip.""" |
| 1158 | + gmt = ZoneInfo(key="GMT") |
| 1159 | + target = ImageTarget( |
| 1160 | + active_flag=True, |
| 1161 | + application_metadata=None, |
| 1162 | + image_value=high_quality_image.getvalue(), |
| 1163 | + last_modified_date=datetime.datetime( |
| 1164 | + year=2020, month=1, day=3, tzinfo=gmt |
| 1165 | + ), |
| 1166 | + name="example", |
| 1167 | + processing_time_seconds=0.5, |
| 1168 | + target_tracking_rater=HardcodedTargetTrackingRater(rating=4), |
| 1169 | + upload_date=datetime.datetime( |
| 1170 | + year=2020, month=1, day=2, tzinfo=gmt |
| 1171 | + ), |
| 1172 | + width=1.5, |
| 1173 | + ) |
| 1174 | + database = CloudDatabase( |
| 1175 | + client_access_key="example-client-access-key", |
| 1176 | + client_secret_key="example-client-secret-key", |
| 1177 | + current_month_recos=1, |
| 1178 | + database_id="example-database-id", |
| 1179 | + database_name="example-database-name", |
| 1180 | + # ``CLOUD_RECO`` is the only database type, so it is not |
| 1181 | + # possible to use a non-default value here. |
| 1182 | + database_type=DatabaseType.CLOUD_RECO, |
| 1183 | + previous_month_recos=2, |
| 1184 | + reco_threshold=3, |
| 1185 | + request_quota=4, |
| 1186 | + request_rate_limits=DOCUMENTED_REQUEST_RATE_LIMITS, |
| 1187 | + requests_per_second_limit=5, |
| 1188 | + server_access_key="example-server-access-key", |
| 1189 | + server_secret_key="example-server-secret-key", |
| 1190 | + state=States.PROJECT_SUSPENDED, |
| 1191 | + target_quota=6, |
| 1192 | + targets={target}, |
| 1193 | + total_recos=7, |
| 1194 | + ) |
| 1195 | + # Adding a field to ``CloudDatabase`` must mean adding it to this |
| 1196 | + # test, and therefore to the round trip. |
| 1197 | + expected_field_names = { |
| 1198 | + "client_access_key", |
| 1199 | + "client_secret_key", |
| 1200 | + "current_month_recos", |
| 1201 | + "database_id", |
| 1202 | + "database_name", |
| 1203 | + "database_type", |
| 1204 | + "previous_month_recos", |
| 1205 | + "reco_threshold", |
| 1206 | + "request_quota", |
| 1207 | + "request_rate_limits", |
| 1208 | + "requests_per_second_limit", |
| 1209 | + "server_access_key", |
| 1210 | + "server_secret_key", |
| 1211 | + "state", |
| 1212 | + "target_quota", |
| 1213 | + "targets", |
| 1214 | + "total_recos", |
| 1215 | + } |
| 1216 | + field_names = { |
| 1217 | + field.name |
| 1218 | + for field in dataclasses.fields(class_or_instance=CloudDatabase) |
| 1219 | + } |
| 1220 | + assert field_names == expected_field_names |
| 1221 | + |
| 1222 | + database_dict = database.to_dict() |
| 1223 | + # The dictionary is JSON dump-able |
| 1224 | + assert json.dumps(obj=database_dict) |
| 1225 | + |
| 1226 | + new_database = CloudDatabase.from_dict(database_dict=database_dict) |
| 1227 | + assert new_database == database |
| 1228 | + |
1081 | 1229 | @staticmethod |
1082 | 1230 | def test_vumark_database_to_dict() -> None: |
1083 | 1231 | """It is possible to dump a VuMark database to a dictionary and |
|
0 commit comments