From 610418e7a54085afd73a67452c38a12f72aef432 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Fri, 4 Jun 2021 15:45:01 -0500 Subject: [PATCH 01/21] Acceptance criteria per DX-1039 - Normalize Address Implementation - https://auctane.atlassian.net/browse/DX-1039 --- shipengine_sdk/services/address_validation.py | 9 +++++ shipengine_sdk/util/sdk_assertions.py | 37 +++++++++++++++++++ 2 files changed, 46 insertions(+) diff --git a/shipengine_sdk/services/address_validation.py b/shipengine_sdk/services/address_validation.py index 0b4a435..a7fc851 100644 --- a/shipengine_sdk/services/address_validation.py +++ b/shipengine_sdk/services/address_validation.py @@ -5,6 +5,7 @@ from ..models.address import Address, AddressValidateResult from ..models.enums import RPCMethods from ..shipengine_config import ShipEngineConfig +from ..util import does_normalized_address_have_errors def validate(address: Address, config: ShipEngineConfig) -> AddressValidateResult: @@ -33,5 +34,13 @@ def validate(address: Address, config: ShipEngineConfig) -> AddressValidateResul def normalize(address: Address, config: ShipEngineConfig) -> Address: + """ + Normalize a given address into a standardized format. + + :param Address address: The address to be validate. + :param ShipEngineConfig config: The global ShipEngine configuration object. + :returns: :class:`Address`: The normalized address returned from ShipEngine API. + """ validation_result: AddressValidateResult = validate(address=address, config=config) + does_normalized_address_have_errors(result=validation_result) return validation_result.normalized_address diff --git a/shipengine_sdk/util/sdk_assertions.py b/shipengine_sdk/util/sdk_assertions.py index ceb5da3..e8bffa6 100644 --- a/shipengine_sdk/util/sdk_assertions.py +++ b/shipengine_sdk/util/sdk_assertions.py @@ -226,3 +226,40 @@ def is_response_500(status_code: int, response_body: Dict[str, any]) -> None: error_type=error_data["type"], error_code=error_data["code"], ) + + +def does_normalized_address_have_errors(result) -> None: + """ + Assertions to check if the returned normalized address has any errors. If errors + are present an exception is thrown. + + :param AddressValidateResult result: The address validation response from ShipEngine API. + """ + if len(result.errors) > 1: + error_list = list() + map(lambda msg: error_list.append(msg), result.errors) + str_errors = "\n".join(error_list) + + raise ShipEngineError( + message=f"Invalid address.\n {str_errors}", + request_id=result.request_id, + source=ErrorSource.SHIPENGINE.value, + error_type=ErrorType.ERROR.value, + error_code=ErrorCode.INVALID_ADDRESS.value, + ) + elif len(result.errors) == 1: + raise ShipEngineError( + message=f"Invalid address.\n {result.errors[0]['message']}", + request_id=result.request_id, + source=ErrorSource.SHIPENGINE.value, + error_type=ErrorType.ERROR.value, + error_code=result.errors[0]["code"], + ) + elif result.is_valid is False: + raise ShipEngineError( + message="Invalid address - The address provided could not be normalized.", + request_id=result.request_id, + source=ErrorSource.SHIPENGINE.value, + error_type=ErrorType.ERROR.value, + error_code=ErrorCode.INVALID_ADDRESS.value, + ) From 95dbe9d0feb6fde1a403bece920c52100b354b9b Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Fri, 4 Jun 2021 15:46:01 -0500 Subject: [PATCH 02/21] fixed imports --- shipengine_sdk/__init__.py | 2 -- tests/jsonrpc/test_process_request.py | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/shipengine_sdk/__init__.py b/shipengine_sdk/__init__.py index ef1b01b..0bbbcc5 100644 --- a/shipengine_sdk/__init__.py +++ b/shipengine_sdk/__init__.py @@ -4,8 +4,6 @@ import logging from logging import NullHandler -from .models import * # noqa - # SDK imports here from .shipengine import ShipEngine from .shipengine_config import ShipEngineConfig diff --git a/tests/jsonrpc/test_process_request.py b/tests/jsonrpc/test_process_request.py index 25ddfe0..5d8dce8 100644 --- a/tests/jsonrpc/test_process_request.py +++ b/tests/jsonrpc/test_process_request.py @@ -1,7 +1,6 @@ """Testing the process request and response functions.""" import pytest -from shipengine_sdk import ErrorCode, ErrorSource, ErrorType, RPCMethods from shipengine_sdk.errors import ( AccountStatusError, BusinessRuleError, @@ -11,6 +10,7 @@ ValidationError, ) from shipengine_sdk.jsonrpc import handle_response, wrap_request +from shipengine_sdk.models import ErrorCode, ErrorSource, ErrorType, RPCMethods def handle_response_errors(error_source: str, error_code: str, error_type: str): From 184f998122d6c373b9477613138793da649d5fbb Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Fri, 4 Jun 2021 15:46:22 -0500 Subject: [PATCH 03/21] Added generic error to ErrorType enumeration. --- shipengine_sdk/models/enums/error_type.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/shipengine_sdk/models/enums/error_type.py b/shipengine_sdk/models/enums/error_type.py index 64b0608..f496ee1 100644 --- a/shipengine_sdk/models/enums/error_type.py +++ b/shipengine_sdk/models/enums/error_type.py @@ -47,3 +47,6 @@ class ErrorType(Enum): AUTHORIZATION = "authorization" """General authorization error type.""" + + ERROR = "error" + """Generic error.""" From da3b47e9a6a64cad7f833a289e2efb3330c448ed Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Fri, 4 Jun 2021 17:32:27 -0500 Subject: [PATCH 04/21] work in progress --- tests/services/__init__.py | 1 + tests/services/test_address_validation.py | 183 ++-------------------- tests/services/test_normalize_address.py | 5 + tests/util/__init__.py | 1 + tests/util/test_data.py | 170 ++++++++++++++++++++ 5 files changed, 192 insertions(+), 168 deletions(-) create mode 100644 tests/services/__init__.py create mode 100644 tests/services/test_normalize_address.py create mode 100644 tests/util/__init__.py create mode 100644 tests/util/test_data.py diff --git a/tests/services/__init__.py b/tests/services/__init__.py new file mode 100644 index 0000000..b5bcf10 --- /dev/null +++ b/tests/services/__init__.py @@ -0,0 +1 @@ +"""Initial Docstring""" diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index e36b9be..6a5d82e 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -1,182 +1,29 @@ -"""Initial Docstring""" +"""Test the validate address method of the ShipEngine SDK.""" import re -from typing import Dict -from shipengine_sdk import ShipEngine from shipengine_sdk.errors import ClientSystemError, ValidationError from shipengine_sdk.models import ( Address, AddressValidateResult, - Endpoints, ErrorCode, ErrorSource, ErrorType, ) - -def stub_config() -> Dict[str, any]: - """ - Return a test configuration dictionary to be used - when instantiating the ShipEngine object. - """ - return dict( - api_key="baz", base_uri=Endpoints.TEST_RPC_URL.value, page_size=50, retries=2, timeout=15 - ) - - -def stub_shipengine_instance() -> ShipEngine: - """Return a test instance of the ShipEngine object.""" - return ShipEngine(stub_config()) - - -def valid_residential_address() -> Address: - """ - Return a test Address object with valid residential - address information. - """ - return Address( - street=["4 Jersey St", "Apt. 2b"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="US", - ) - - -def valid_commercial_address() -> Address: - """ - Return a test Address object with valid commercial - address information. - """ - return Address( - street=["4 Jersey St", "ste 200"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="US", - ) - - -def address_with_warnings() -> Address: - """Return a test Address object that will cause the server to return warning messages.""" - return Address( - street=["170 Warning Blvd", "Apartment 32-B"], - city_locality="Toronto", - state_province="ON", - postal_code="M6K 3C3", - country_code="CA", - ) - - -def address_with_errors() -> Address: - """Return a test Address object that will cause the server to return an error message.""" - return Address( - street=["4 Invalid St"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="US", - ) - - -def valid_canadian_address() -> Address: - """Return an Address object with a valid canadian address.""" - return Address( - street=["170 Princes Blvd", "Ste 200"], - city_locality="Toronto", - state_province="ON", - postal_code="M6K 3C3", - country_code="CA", - ) - - -def multi_line_address() -> Address: - """Returns a valid multiline address.""" - return Address( - street=["4 Jersey St", "ste 200", "1st Floor"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="US", - ) - - -def non_latin_address() -> Address: - """Return an address with non-latin characters.""" - return Address( - street=["上鳥羽角田町68"], - city_locality="南区", - state_province="京都", - postal_code="601-8104", - country_code="JP", - ) - - -def unknown_address() -> Address: - """ - Return an address that will make the server respond with an - address with an unknown residential flag. - """ - return Address( - street=["4 Unknown St"], - city_locality="Toronto", - state_province="ON", - postal_code="M6K 3C3", - country_code="CA", - ) - - -def address_missing_required_fields() -> Address: - """Return an address that is missing a state, city, and postal_code to return a ValidationError..""" - return Address( - street=["4 Jersey St"], - city_locality="", - state_province="", - postal_code="", - country_code="US", - ) - - -def address_missing_country() -> Address: - """Return an address that is only missing the country_code.""" - return Address( - street=["4 Jersey St", "Apt. 2b"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="", - ) - - -def address_with_invalid_country() -> Address: - """Return an address that has an invalid country_code specified.""" - return Address( - street=["4 Jersey St", "Apt. 2b"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="RZ", - ) - - -def get_server_side_error() -> Address: - """Return an address that will cause the server to return a 500 server error.""" - return Address( - street=["500 Server Error"], - city_locality="Boston", - state_province="MA", - postal_code="02215", - country_code="US", - ) - - -def validate_an_address(address: Address) -> AddressValidateResult: - """ - Helper function that passes a config dictionary into the ShipEngine object to instantiate - it and calls the `validate_address` method, providing it the `address` that is passed into - this function. - """ - return stub_shipengine_instance().validate_address(address=address) +from ..util.test_data import ( + address_missing_required_fields, + address_with_errors, + address_with_invalid_country, + address_with_warnings, + get_server_side_error, + multi_line_address, + non_latin_address, + unknown_address, + valid_canadian_address, + valid_commercial_address, + valid_residential_address, + validate_an_address, +) def us_valid_address_assertions( diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py new file mode 100644 index 0000000..31aba74 --- /dev/null +++ b/tests/services/test_normalize_address.py @@ -0,0 +1,5 @@ +"""Test the normalize address method of the ShipEngine SDK.""" + + +# class TestNormalizeAddress: +# pass diff --git a/tests/util/__init__.py b/tests/util/__init__.py new file mode 100644 index 0000000..b5bcf10 --- /dev/null +++ b/tests/util/__init__.py @@ -0,0 +1 @@ +"""Initial Docstring""" diff --git a/tests/util/test_data.py b/tests/util/test_data.py new file mode 100644 index 0000000..ae64958 --- /dev/null +++ b/tests/util/test_data.py @@ -0,0 +1,170 @@ +"""Test data as functions.""" +from typing import Dict + +from shipengine_sdk import ShipEngine +from shipengine_sdk.models import Address, AddressValidateResult, Endpoints + + +def stub_config() -> Dict[str, any]: + """ + Return a test configuration dictionary to be used + when instantiating the ShipEngine object. + """ + return dict( + api_key="baz", base_uri=Endpoints.TEST_RPC_URL.value, page_size=50, retries=2, timeout=15 + ) + + +def stub_shipengine_instance() -> ShipEngine: + """Return a test instance of the ShipEngine object.""" + return ShipEngine(stub_config()) + + +def valid_residential_address() -> Address: + """ + Return a test Address object with valid residential + address information. + """ + return Address( + street=["4 Jersey St", "Apt. 2b"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + +def valid_commercial_address() -> Address: + """ + Return a test Address object with valid commercial + address information. + """ + return Address( + street=["4 Jersey St", "ste 200"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + +def address_with_warnings() -> Address: + """Return a test Address object that will cause the server to return warning messages.""" + return Address( + street=["170 Warning Blvd", "Apartment 32-B"], + city_locality="Toronto", + state_province="ON", + postal_code="M6K 3C3", + country_code="CA", + ) + + +def address_with_errors() -> Address: + """Return a test Address object that will cause the server to return an error message.""" + return Address( + street=["4 Invalid St"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + +def valid_canadian_address() -> Address: + """Return an Address object with a valid canadian address.""" + return Address( + street=["170 Princes Blvd", "Ste 200"], + city_locality="Toronto", + state_province="ON", + postal_code="M6K 3C3", + country_code="CA", + ) + + +def multi_line_address() -> Address: + """Returns a valid multiline address.""" + return Address( + street=["4 Jersey St", "ste 200", "1st Floor"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + +def non_latin_address() -> Address: + """Return an address with non-latin characters.""" + return Address( + street=["上鳥羽角田町68"], + city_locality="南区", + state_province="京都", + postal_code="601-8104", + country_code="JP", + ) + + +def unknown_address() -> Address: + """ + Return an address that will make the server respond with an + address with an unknown residential flag. + """ + return Address( + street=["4 Unknown St"], + city_locality="Toronto", + state_province="ON", + postal_code="M6K 3C3", + country_code="CA", + ) + + +def address_missing_required_fields() -> Address: + """Return an address that is missing a state, city, and postal_code to return a ValidationError..""" + return Address( + street=["4 Jersey St"], + city_locality="", + state_province="", + postal_code="", + country_code="US", + ) + + +def address_missing_country() -> Address: + """Return an address that is only missing the country_code.""" + return Address( + street=["4 Jersey St", "Apt. 2b"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="", + ) + + +def address_with_invalid_country() -> Address: + """Return an address that has an invalid country_code specified.""" + return Address( + street=["4 Jersey St", "Apt. 2b"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="RZ", + ) + + +def get_server_side_error() -> Address: + """Return an address that will cause the server to return a 500 server error.""" + return Address( + street=["500 Server Error"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + +def validate_an_address(address: Address) -> AddressValidateResult: + """ + Helper function that passes a config dictionary into the ShipEngine object to instantiate + it and calls the `validate_address` method, providing it the `address` that is passed into + this function. + """ + return stub_shipengine_instance().validate_address(address=address) From 908419ef8ce1c7f24e011adecee0674f120017ce Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 14:06:32 -0500 Subject: [PATCH 05/21] Acceptance criteria per DX-1041 - Normalize valid residential address. --- tests/services/test_normalize_address.py | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 31aba74..c0da67d 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,5 +1,22 @@ """Test the normalize address method of the ShipEngine SDK.""" +from shipengine_sdk.models import Address +from ..util.test_data import ( + normalize_an_address, + us_valid_normalize_assertions, + valid_residential_address, +) -# class TestNormalizeAddress: -# pass + +class TestNormalizeAddress: + def test_normalize_valid_residential_address(self) -> None: + """DX-1041 - Normalize valid residential address.""" + residential_address = valid_residential_address() + normalized = normalize_an_address(residential_address) + + us_valid_normalize_assertions( + original_address=residential_address, + normalized_address=normalized, + expected_residential_indicator=True, + ) + assert type(normalized) is Address From d112a60abd143e122502f364eb1159aeda00ef07 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 14:09:11 -0500 Subject: [PATCH 06/21] Moved assertion functions for re-use throughout the test suite. --- tests/services/test_address_validation.py | 59 +++-------------- tests/util/test_data.py | 77 +++++++++++++++++++++++ 2 files changed, 87 insertions(+), 49 deletions(-) diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index 6a5d82e..6e7e091 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -15,10 +15,12 @@ address_with_errors, address_with_invalid_country, address_with_warnings, + canada_valid_avs_assertions, get_server_side_error, multi_line_address, non_latin_address, unknown_address, + us_valid_avs_assertions, valid_canadian_address, valid_commercial_address, valid_residential_address, @@ -26,48 +28,6 @@ ) -def us_valid_address_assertions( - original_address: Address, - validated_address: AddressValidateResult, - expected_residential_indicator, -) -> None: - """A set of common assertions that are regularly made on the commercial US address used for testing.""" - address = validated_address.normalized_address - assert type(validated_address) is AddressValidateResult - assert validated_address.is_valid is True - assert type(address) is Address - assert len(validated_address.info) == 0 - assert len(validated_address.warnings) == 0 - assert len(validated_address.errors) == 0 - assert address is not None - assert address.city_locality == original_address.city_locality.upper() - assert address.state_province == original_address.state_province.upper() - assert address.postal_code == original_address.postal_code - assert address.country_code == original_address.country_code.upper() - assert address.is_residential is expected_residential_indicator - - -def canada_valid_address_assertions( - original_address: Address, - validated_address: AddressValidateResult, - expected_residential_indicator, -) -> None: - """A set of common assertions that are regularly made on the canadian_address used for testing.""" - address = validated_address.normalized_address - assert type(validated_address) is AddressValidateResult - assert validated_address.is_valid is True - assert type(address) is Address - assert len(validated_address.info) == 0 - assert len(validated_address.warnings) == 0 - assert len(validated_address.errors) == 0 - assert address is not None - assert address.city_locality == original_address.city_locality - assert address.state_province == original_address.state_province.title() - assert address.postal_code == "M6 K 3 C3" - assert address.country_code == original_address.country_code.upper() - assert address.is_residential is expected_residential_indicator - - class TestValidateAddress: def test_valid_residential_address(self) -> None: """DX-1024 - Valid residential address.""" @@ -75,7 +35,7 @@ def test_valid_residential_address(self) -> None: validated_address = validate_an_address(residential_address) address = validated_address.normalized_address - us_valid_address_assertions( + us_valid_avs_assertions( original_address=residential_address, validated_address=validated_address, expected_residential_indicator=True, @@ -93,7 +53,7 @@ def test_valid_commercial_address(self) -> None: validated_address = validate_an_address(commercial_address) address = validated_address.normalized_address - us_valid_address_assertions( + us_valid_avs_assertions( original_address=commercial_address, validated_address=validated_address, expected_residential_indicator=False, @@ -111,7 +71,7 @@ def test_multi_line_address(self) -> None: validated_address = validate_an_address(valid_multi_line_address) address = validated_address.normalized_address - us_valid_address_assertions( + us_valid_avs_assertions( original_address=valid_multi_line_address, validated_address=validated_address, expected_residential_indicator=False, @@ -128,7 +88,7 @@ def test_numeric_postal_code(self) -> None: """DX-1028 - Validate numeric postal code.""" residential_address = valid_residential_address() validated_address = validate_an_address(residential_address) - us_valid_address_assertions( + us_valid_avs_assertions( original_address=residential_address, validated_address=validated_address, expected_residential_indicator=True, @@ -139,7 +99,7 @@ def test_alpha_postal_code(self): """DX-1029 - Alpha postal code.""" canadian_address = valid_canadian_address() validated_address = validate_an_address(canadian_address) - canada_valid_address_assertions( + canada_valid_avs_assertions( original_address=canadian_address, validated_address=validated_address, expected_residential_indicator=False, @@ -149,7 +109,7 @@ def test_unknown_address(self): """DX-1026 - Validate address of unknown address.""" address = unknown_address() validated_address = validate_an_address(address) - canada_valid_address_assertions( + canada_valid_avs_assertions( original_address=address, validated_address=validated_address, expected_residential_indicator=None, @@ -228,7 +188,8 @@ def test_missing_city_state_and_postal_code(self): assert err.error_code is ErrorCode.FIELD_VALUE_REQUIRED.value assert ( err.message - == "Invalid address. Either the postal code or the city/locality and state/province must be specified." # noqa + == "Invalid address. Either the postal code or the city/locality and state/province must be specified." + # noqa ) def test_invalid_country_code(self): diff --git a/tests/util/test_data.py b/tests/util/test_data.py index ae64958..1e32e86 100644 --- a/tests/util/test_data.py +++ b/tests/util/test_data.py @@ -168,3 +168,80 @@ def validate_an_address(address: Address) -> AddressValidateResult: this function. """ return stub_shipengine_instance().validate_address(address=address) + + +def normalize_an_address(address: Address) -> Address: + """ + Helper function that passes a config dictionary into the ShipEngine object to instantiate + it and calls the `normalize_address` method, providing it the `address` that is passed into + this function. + """ + return stub_shipengine_instance().normalize_address(address) + + +# Assertion helper functions + + +def us_valid_avs_assertions( + original_address: Address, + validated_address: AddressValidateResult, + expected_residential_indicator, +) -> None: + """ + A set of common assertions that are regularly made on the commercial US address + used for testing `validate_address`. + """ + address = validated_address.normalized_address + assert type(validated_address) is AddressValidateResult + assert validated_address.is_valid is True + assert type(address) is Address + assert len(validated_address.info) == 0 + assert len(validated_address.warnings) == 0 + assert len(validated_address.errors) == 0 + assert address is not None + assert address.city_locality == original_address.city_locality.upper() + assert address.state_province == original_address.state_province.upper() + assert address.postal_code == original_address.postal_code + assert address.country_code == original_address.country_code.upper() + assert address.is_residential is expected_residential_indicator + + +def canada_valid_avs_assertions( + original_address: Address, + validated_address: AddressValidateResult, + expected_residential_indicator, +) -> None: + """ + A set of common assertions that are regularly made on the canadian_address + used for testing `validate_address`. + """ + address = validated_address.normalized_address + assert type(validated_address) is AddressValidateResult + assert validated_address.is_valid is True + assert type(address) is Address + assert len(validated_address.info) == 0 + assert len(validated_address.warnings) == 0 + assert len(validated_address.errors) == 0 + assert address is not None + assert address.city_locality == original_address.city_locality + assert address.state_province == original_address.state_province.title() + assert address.postal_code == "M6 K 3 C3" + assert address.country_code == original_address.country_code.upper() + assert address.is_residential is expected_residential_indicator + + +def us_valid_normalize_assertions( + original_address: Address, + normalized_address: Address, + expected_residential_indicator, +) -> None: + """ + A set of common assertions that are regularly made on the commercial US address + used for `normalized_address` testing. + """ + assert type(normalized_address) is Address + assert normalized_address.city_locality == original_address.city_locality.upper() + assert normalized_address.state_province == original_address.state_province.upper() + assert normalized_address.postal_code == original_address.postal_code + assert normalized_address.country_code == original_address.country_code.upper() + assert normalized_address.is_residential is expected_residential_indicator From 69c50cc25b8ed4097cd9f5ef0f8ec10627d71897 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 14:09:52 -0500 Subject: [PATCH 07/21] Imported normalize method an created normalize_address() --- shipengine_sdk/shipengine.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/shipengine_sdk/shipengine.py b/shipengine_sdk/shipengine.py index 4e701c2..75b906f 100644 --- a/shipengine_sdk/shipengine.py +++ b/shipengine_sdk/shipengine.py @@ -2,7 +2,7 @@ from typing import Dict, Union from .models.address import Address, AddressValidateResult -from .services.address_validation import validate +from .services.address_validation import normalize, validate from .shipengine_config import ShipEngineConfig @@ -40,3 +40,10 @@ def validate_address( """ config: ShipEngineConfig = self.config.merge(new_config=config) return validate(address, config) + + def normalize_address( + self, address: Address, config: Union[Dict[str, any], ShipEngineConfig] = None + ) -> Address: + """Normalize a given address into a standardized format used by carriers.""" + config: ShipEngineConfig = self.config.merge(new_config=config) + return normalize(address=address, config=config) From 36481451698dac43ee9abfbf4eebf91c0dcc2bf2 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 14:13:18 -0500 Subject: [PATCH 08/21] formatting fix --- tests/services/test_address_validation.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index 6e7e091..eb5eba9 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -188,8 +188,7 @@ def test_missing_city_state_and_postal_code(self): assert err.error_code is ErrorCode.FIELD_VALUE_REQUIRED.value assert ( err.message - == "Invalid address. Either the postal code or the city/locality and state/province must be specified." - # noqa + == "Invalid address. Either the postal code or the city/locality and state/province must be specified." # noqa ) def test_invalid_country_code(self): From 902308e2a3bb857dddbdf0619fb9772c042dd2fb Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 15:29:11 -0500 Subject: [PATCH 09/21] Acceptance criteria per DX-1042 - Normalize valid commercial address. https://auctane.atlassian.net/browse/DX-1042 --- tests/services/test_normalize_address.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index c0da67d..bf35a37 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -4,6 +4,7 @@ from ..util.test_data import ( normalize_an_address, us_valid_normalize_assertions, + valid_commercial_address, valid_residential_address, ) @@ -20,3 +21,14 @@ def test_normalize_valid_residential_address(self) -> None: expected_residential_indicator=True, ) assert type(normalized) is Address + + def test_normalize_valid_commercial_address(self) -> None: + """DX-1042 - Normalize valid commercial address.""" + commercial_address = valid_commercial_address() + normalized = normalize_an_address(commercial_address) + + us_valid_normalize_assertions( + original_address=commercial_address, + normalized_address=normalized, + expected_residential_indicator=False, + ) From 2ce372095ed3aa6fcfda40462df3f5cc22c624b3 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 16:08:31 -0500 Subject: [PATCH 10/21] Work in progress - optimizing tests. --- tests/services/test_address_validation.py | 28 ++++--- tests/services/test_normalize_address.py | 16 ++-- tests/util/test_data.py | 89 ++++++++++++++++++----- 3 files changed, 102 insertions(+), 31 deletions(-) diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index eb5eba9..2c389e4 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -20,7 +20,7 @@ multi_line_address, non_latin_address, unknown_address, - us_valid_avs_assertions, + valid_address_assertions, valid_canadian_address, valid_commercial_address, valid_residential_address, @@ -29,15 +29,19 @@ class TestValidateAddress: + TEST_METHOD: str = "validate" + def test_valid_residential_address(self) -> None: """DX-1024 - Valid residential address.""" residential_address = valid_residential_address() validated_address = validate_an_address(residential_address) address = validated_address.normalized_address - us_valid_avs_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=residential_address, - validated_address=validated_address, + returned_address=validated_address, expected_residential_indicator=True, ) assert ( @@ -53,9 +57,11 @@ def test_valid_commercial_address(self) -> None: validated_address = validate_an_address(commercial_address) address = validated_address.normalized_address - us_valid_avs_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=commercial_address, - validated_address=validated_address, + returned_address=validated_address, expected_residential_indicator=False, ) assert ( @@ -71,9 +77,11 @@ def test_multi_line_address(self) -> None: validated_address = validate_an_address(valid_multi_line_address) address = validated_address.normalized_address - us_valid_avs_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=valid_multi_line_address, - validated_address=validated_address, + returned_address=validated_address, expected_residential_indicator=False, ) assert ( @@ -88,9 +96,11 @@ def test_numeric_postal_code(self) -> None: """DX-1028 - Validate numeric postal code.""" residential_address = valid_residential_address() validated_address = validate_an_address(residential_address) - us_valid_avs_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=residential_address, - validated_address=validated_address, + returned_address=validated_address, expected_residential_indicator=True, ) assert re.match(r"\d", validated_address.normalized_address.postal_code) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index bf35a37..3599b3c 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -3,21 +3,25 @@ from ..util.test_data import ( normalize_an_address, - us_valid_normalize_assertions, + valid_address_assertions, valid_commercial_address, valid_residential_address, ) class TestNormalizeAddress: + TEST_METHOD: str = "normalize" + def test_normalize_valid_residential_address(self) -> None: """DX-1041 - Normalize valid residential address.""" residential_address = valid_residential_address() normalized = normalize_an_address(residential_address) - us_valid_normalize_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=residential_address, - normalized_address=normalized, + returned_address=normalized, expected_residential_indicator=True, ) assert type(normalized) is Address @@ -27,8 +31,10 @@ def test_normalize_valid_commercial_address(self) -> None: commercial_address = valid_commercial_address() normalized = normalize_an_address(commercial_address) - us_valid_normalize_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", original_address=commercial_address, - normalized_address=normalized, + returned_address=normalized, expected_residential_indicator=False, ) diff --git a/tests/util/test_data.py b/tests/util/test_data.py index 1e32e86..b459585 100644 --- a/tests/util/test_data.py +++ b/tests/util/test_data.py @@ -1,5 +1,5 @@ """Test data as functions.""" -from typing import Dict +from typing import Dict, Union from shipengine_sdk import ShipEngine from shipengine_sdk.models import Address, AddressValidateResult, Endpoints @@ -182,28 +182,66 @@ def normalize_an_address(address: Address) -> Address: # Assertion helper functions -def us_valid_avs_assertions( +def valid_address_assertions( + test_method: str, + locale: str, original_address: Address, - validated_address: AddressValidateResult, + returned_address: Union[Address, AddressValidateResult], expected_residential_indicator, ) -> None: """ A set of common assertions that are regularly made on the commercial US address - used for testing `validate_address`. + used for testing the `validate_address` or `normalize_address` methods, based on + the `test_method` that is passed in. It also makes different sets of assertions + depending on what `locale` is passed in. """ - address = validated_address.normalized_address - assert type(validated_address) is AddressValidateResult - assert validated_address.is_valid is True - assert type(address) is Address - assert len(validated_address.info) == 0 - assert len(validated_address.warnings) == 0 - assert len(validated_address.errors) == 0 - assert address is not None - assert address.city_locality == original_address.city_locality.upper() - assert address.state_province == original_address.state_province.upper() - assert address.postal_code == original_address.postal_code - assert address.country_code == original_address.country_code.upper() - assert address.is_residential is expected_residential_indicator + address = ( + returned_address.normalized_address + if type(returned_address) is AddressValidateResult + else returned_address + ) + if locale == "domestic": + if test_method == "validate": + assert type(returned_address) is AddressValidateResult + assert returned_address.is_valid is True + assert type(address) is Address + assert len(returned_address.info) == 0 + assert len(returned_address.warnings) == 0 + assert len(returned_address.errors) == 0 + assert address is not None + assert address.city_locality == original_address.city_locality.upper() + assert address.state_province == original_address.state_province.upper() + assert address.postal_code == original_address.postal_code + assert address.country_code == original_address.country_code.upper() + assert address.is_residential is expected_residential_indicator + elif test_method == "normalize": + assert type(returned_address) is Address + assert returned_address.city_locality == original_address.city_locality.upper() + assert returned_address.state_province == original_address.state_province.upper() + assert returned_address.postal_code == original_address.postal_code + assert returned_address.country_code == original_address.country_code.upper() + assert returned_address.is_residential is expected_residential_indicator + elif locale == "international": + if test_method == "validate": + assert type(returned_address) is AddressValidateResult + assert returned_address.is_valid is True + assert type(address) is Address + assert len(returned_address.info) == 0 + assert len(returned_address.warnings) == 0 + assert len(returned_address.errors) == 0 + assert address is not None + assert address.city_locality == original_address.city_locality + assert address.state_province == original_address.state_province.title() + assert address.postal_code == "M6 K 3 C3" + assert address.country_code == original_address.country_code.upper() + assert address.is_residential is expected_residential_indicator + if test_method == "normalize": + assert type(returned_address) is Address + assert returned_address.city_locality == original_address.city_locality + assert returned_address.state_province == original_address.state_province.title() + assert returned_address.postal_code == "M6 K 3 C3" + assert returned_address.country_code == original_address.country_code.upper() + assert returned_address.is_residential is expected_residential_indicator def canada_valid_avs_assertions( @@ -245,3 +283,20 @@ def us_valid_normalize_assertions( assert normalized_address.postal_code == original_address.postal_code assert normalized_address.country_code == original_address.country_code.upper() assert normalized_address.is_residential is expected_residential_indicator + + +def canada_valid_normalize_assertions( + original_address: Address, + normalized_address: Address, + expected_residential_indicator, +) -> None: + """ + A set of common assertions that are regularly made on the canadian_address + used for testing `validate_address`. + """ + assert type(normalized_address) is Address + assert normalized_address.city_locality == original_address.city_locality + assert normalized_address.state_province == original_address.state_province.title() + assert normalized_address.postal_code == "M6 K 3 C3" + assert normalized_address.country_code == original_address.country_code.upper() + assert normalized_address.is_residential is expected_residential_indicator From 8b5cb776964a88f1068e0132c222e8bdfbe17340 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 16:33:22 -0500 Subject: [PATCH 11/21] work in progress - test optimizations. --- tests/services/test_address_validation.py | 6 ++++-- tests/util/test_data.py | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index 2c389e4..879f17a 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -109,9 +109,11 @@ def test_alpha_postal_code(self): """DX-1029 - Alpha postal code.""" canadian_address = valid_canadian_address() validated_address = validate_an_address(canadian_address) - canada_valid_avs_assertions( + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="international", original_address=canadian_address, - validated_address=validated_address, + returned_address=validated_address, expected_residential_indicator=False, ) diff --git a/tests/util/test_data.py b/tests/util/test_data.py index b459585..4d1942e 100644 --- a/tests/util/test_data.py +++ b/tests/util/test_data.py @@ -199,7 +199,7 @@ def valid_address_assertions( returned_address.normalized_address if type(returned_address) is AddressValidateResult else returned_address - ) + ) # noqa if locale == "domestic": if test_method == "validate": assert type(returned_address) is AddressValidateResult From b930f29a9ba652e1daa1ae3e549990e0a92f4da4 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Mon, 7 Jun 2021 16:54:49 -0500 Subject: [PATCH 12/21] Acceptance criteria per DX-1043 - Normalize unknown address. https://auctane.atlassian.net/browse/DX-1043 --- tests/services/test_normalize_address.py | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 3599b3c..242df1c 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,8 +1,7 @@ """Test the normalize address method of the ShipEngine SDK.""" -from shipengine_sdk.models import Address - from ..util.test_data import ( normalize_an_address, + unknown_address, valid_address_assertions, valid_commercial_address, valid_residential_address, @@ -24,7 +23,6 @@ def test_normalize_valid_residential_address(self) -> None: returned_address=normalized, expected_residential_indicator=True, ) - assert type(normalized) is Address def test_normalize_valid_commercial_address(self) -> None: """DX-1042 - Normalize valid commercial address.""" @@ -38,3 +36,16 @@ def test_normalize_valid_commercial_address(self) -> None: returned_address=normalized, expected_residential_indicator=False, ) + + def test_normalize_unknown_address(self) -> None: + """DX-1043 - Normalize unknown address.""" + address = unknown_address() + normalized = normalize_an_address(address) + + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="international", + original_address=address, + returned_address=normalized, + expected_residential_indicator=None, + ) From 5a0acde2ab5e1764c17b6d724789aa1cf47a153f Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 10:16:34 -0500 Subject: [PATCH 13/21] DX-1044 - Normalize multi-line address. https://auctane.atlassian.net/browse/DX-1044 --- tests/services/test_normalize_address.py | 19 ++++++++++++++++ tests/util/test_data.py | 28 +++++++++--------------- 2 files changed, 29 insertions(+), 18 deletions(-) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 242df1c..74eadcb 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,5 +1,6 @@ """Test the normalize address method of the ShipEngine SDK.""" from ..util.test_data import ( + multi_line_address, normalize_an_address, unknown_address, valid_address_assertions, @@ -49,3 +50,21 @@ def test_normalize_unknown_address(self) -> None: returned_address=normalized, expected_residential_indicator=None, ) + + def test_normalize_multi_line_address(self): + """DX-1044 - Normalize multi-line address.""" + multi_line = multi_line_address() + normalized = normalize_an_address(multi_line) + + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", + original_address=multi_line, + returned_address=normalized, + expected_residential_indicator=False, + ) + assert ( + normalized.street[0] + == (multi_line.street[0] + " " + multi_line.street[1]).replace(".", "").upper() + ) + assert normalized.street[1] == multi_line.street[2].upper() diff --git a/tests/util/test_data.py b/tests/util/test_data.py index 4d1942e..6c0b5b1 100644 --- a/tests/util/test_data.py +++ b/tests/util/test_data.py @@ -223,25 +223,17 @@ def valid_address_assertions( assert returned_address.is_residential is expected_residential_indicator elif locale == "international": if test_method == "validate": - assert type(returned_address) is AddressValidateResult - assert returned_address.is_valid is True - assert type(address) is Address - assert len(returned_address.info) == 0 - assert len(returned_address.warnings) == 0 - assert len(returned_address.errors) == 0 - assert address is not None - assert address.city_locality == original_address.city_locality - assert address.state_province == original_address.state_province.title() - assert address.postal_code == "M6 K 3 C3" - assert address.country_code == original_address.country_code.upper() - assert address.is_residential is expected_residential_indicator + canada_valid_avs_assertions( + original_address=original_address, + validated_address=returned_address, + expected_residential_indicator=expected_residential_indicator, + ) if test_method == "normalize": - assert type(returned_address) is Address - assert returned_address.city_locality == original_address.city_locality - assert returned_address.state_province == original_address.state_province.title() - assert returned_address.postal_code == "M6 K 3 C3" - assert returned_address.country_code == original_address.country_code.upper() - assert returned_address.is_residential is expected_residential_indicator + canada_valid_normalize_assertions( + original_address=original_address, + normalized_address=returned_address, + expected_residential_indicator=expected_residential_indicator, + ) def canada_valid_avs_assertions( From 995a22f16b3406c56a30186929c06030e2e159af Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 10:26:00 -0500 Subject: [PATCH 14/21] DX-1045 - Normalize address with numeric postal code. https://auctane.atlassian.net/browse/DX-1045 --- tests/services/test_normalize_address.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 74eadcb..c88cee4 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,4 +1,6 @@ """Test the normalize address method of the ShipEngine SDK.""" +import re + from ..util.test_data import ( multi_line_address, normalize_an_address, @@ -68,3 +70,17 @@ def test_normalize_multi_line_address(self): == (multi_line.street[0] + " " + multi_line.street[1]).replace(".", "").upper() ) assert normalized.street[1] == multi_line.street[2].upper() + + def test_normalize_numeric_postal_code(self) -> None: + """DX-1045 - Normalize address with numeric postal code.""" + address = valid_residential_address() + normalized = normalize_an_address(address) + + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="domestic", + original_address=address, + returned_address=normalized, + expected_residential_indicator=True, + ) + assert re.match(r"\d", normalized.postal_code) From 3e05821e9000887d452ba6eea08a3a3a9c27a73e Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 10:30:45 -0500 Subject: [PATCH 15/21] DX-1046 - Normalize address with alpha-numeric postal code. https://auctane.atlassian.net/browse/DX-1046 --- tests/services/test_normalize_address.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index c88cee4..eb0bb6c 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -6,6 +6,7 @@ normalize_an_address, unknown_address, valid_address_assertions, + valid_canadian_address, valid_commercial_address, valid_residential_address, ) @@ -84,3 +85,16 @@ def test_normalize_numeric_postal_code(self) -> None: expected_residential_indicator=True, ) assert re.match(r"\d", normalized.postal_code) + + def test_normalize_alpha_postal_code(self) -> None: + """DX-1046 - Normalize address with alpha-numeric postal code.""" + address = valid_canadian_address() + normalized = normalize_an_address(address) + + valid_address_assertions( + test_method=self.TEST_METHOD, + locale="international", + original_address=address, + returned_address=normalized, + expected_residential_indicator=False, + ) From d6fafbfabff4bb6279b4a861178d60b78ab770c0 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 10:41:45 -0500 Subject: [PATCH 16/21] DX-1047 - Normalize address with non-latin characters. https://auctane.atlassian.net/browse/DX-1047 --- tests/services/test_normalize_address.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index eb0bb6c..c688b0c 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,8 +1,11 @@ """Test the normalize address method of the ShipEngine SDK.""" import re +from shipengine_sdk.models import Address + from ..util.test_data import ( multi_line_address, + non_latin_address, normalize_an_address, unknown_address, valid_address_assertions, @@ -98,3 +101,17 @@ def test_normalize_alpha_postal_code(self) -> None: returned_address=normalized, expected_residential_indicator=False, ) + + def test_normalize_non_latin_chars(self) -> None: + """DX-1047 - Normalize address with non-latin characters.""" + non_latin = non_latin_address() + normalized = normalize_an_address(non_latin) + + assert type(normalized) is Address + assert normalized.street[0] == "68 Kamitobatsunodacho" + assert normalized.city_locality == "Kyoto-Shi Minami-Ku" + assert normalized.state_province == "Kyoto" + assert normalized.postal_code == non_latin.postal_code + assert normalized.country_code == non_latin.country_code + assert normalized.is_residential is False + assert len(normalized.street) == 1 From 9a89cffafdd7c349b6d30fbde035f3fb804db8bf Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 11:01:31 -0500 Subject: [PATCH 17/21] DX-1048 - Normalize address with warnings. https://auctane.atlassian.net/browse/DX-1048 --- tests/services/test_normalize_address.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index c688b0c..4966e3c 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -4,6 +4,7 @@ from shipengine_sdk.models import Address from ..util.test_data import ( + address_with_warnings, multi_line_address, non_latin_address, normalize_an_address, @@ -115,3 +116,16 @@ def test_normalize_non_latin_chars(self) -> None: assert normalized.country_code == non_latin.country_code assert normalized.is_residential is False assert len(normalized.street) == 1 + + def test_normalize_with_warnings(self) -> None: + """DX-1048 - Normalize address with warnings.""" + warning_address = address_with_warnings() + normalized = normalize_an_address(warning_address) + + assert type(normalized) is Address + assert normalized is not None + assert normalized.city_locality == warning_address.city_locality + assert normalized.state_province == warning_address.state_province.title() + assert normalized.postal_code == "M6K 3C3" + assert normalized.country_code == warning_address.country_code.upper() + assert normalized.is_residential is True From e1a8d7f3fdc01f8d0eb07d1f1eef4783fcb4b3b6 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 11:04:34 -0500 Subject: [PATCH 18/21] work in progress - test optimizations --- tests/services/test_address_validation.py | 25 ++++++++++---------- tests/services/test_normalize_address.py | 4 ++-- tests/util/{test_data.py => test_helpers.py} | 2 +- 3 files changed, 15 insertions(+), 16 deletions(-) rename tests/util/{test_data.py => test_helpers.py} (99%) diff --git a/tests/services/test_address_validation.py b/tests/services/test_address_validation.py index 879f17a..2c4e07f 100644 --- a/tests/services/test_address_validation.py +++ b/tests/services/test_address_validation.py @@ -10,7 +10,7 @@ ErrorType, ) -from ..util.test_data import ( +from ..util.test_helpers import ( address_missing_required_fields, address_with_errors, address_with_invalid_country, @@ -105,7 +105,7 @@ def test_numeric_postal_code(self) -> None: ) assert re.match(r"\d", validated_address.normalized_address.postal_code) - def test_alpha_postal_code(self): + def test_alpha_postal_code(self) -> None: """DX-1029 - Alpha postal code.""" canadian_address = valid_canadian_address() validated_address = validate_an_address(canadian_address) @@ -117,7 +117,7 @@ def test_alpha_postal_code(self): expected_residential_indicator=False, ) - def test_unknown_address(self): + def test_unknown_address(self) -> None: """DX-1026 - Validate address of unknown address.""" address = unknown_address() validated_address = validate_an_address(address) @@ -127,7 +127,7 @@ def test_unknown_address(self): expected_residential_indicator=None, ) - def test_address_with_non_latin_chars(self): + def test_address_with_non_latin_chars(self) -> None: """DX-1030 - non-latin characters.""" non_latin = non_latin_address() validated_address = validate_an_address(non_latin) @@ -144,7 +144,7 @@ def test_address_with_non_latin_chars(self): assert address.is_residential is False assert len(address.street) == 1 - def test_address_with_warnings(self): + def test_address_with_warnings(self) -> None: """DX-1031 - validate with warnings.""" warnings_address = address_with_warnings() validated_address = validate_an_address(warnings_address) @@ -164,14 +164,13 @@ def test_address_with_warnings(self): == "This address has been verified down to the house/building level (highest possible accuracy with the provided data)" # noqa ) assert len(validated_address.errors) == 0 - assert address is not None - assert address.city_locality == validated_address.normalized_address.city_locality - assert address.state_province == validated_address.normalized_address.state_province.title() + assert address.city_locality == warnings_address.city_locality + assert address.state_province == warnings_address.state_province.title() assert address.postal_code == "M6K 3C3" - assert address.country_code == validated_address.normalized_address.country_code.upper() + assert address.country_code == warnings_address.country_code.upper() assert address.is_residential is True - def test_address_with_errors(self): + def test_address_with_errors(self) -> None: """DX-1032 - Validate with error messages.""" error_address = address_with_errors() validated_address = validate_an_address(error_address) @@ -189,7 +188,7 @@ def test_address_with_errors(self): assert validated_address.errors[1]["code"] == ErrorCode.ADDRESS_NOT_FOUND.value assert validated_address.errors[1]["message"] == "Insufficient or Incorrect Address Data" - def test_missing_city_state_and_postal_code(self): + def test_missing_city_state_and_postal_code(self) -> None: """DX-1035 & DX-1036 - Missing city, state, and postal code.""" try: address_missing_required_fields() @@ -203,7 +202,7 @@ def test_missing_city_state_and_postal_code(self): == "Invalid address. Either the postal code or the city/locality and state/province must be specified." # noqa ) - def test_invalid_country_code(self): + def test_invalid_country_code(self) -> None: """DX-1037 - Invalid country code.""" try: address_with_invalid_country() @@ -214,7 +213,7 @@ def test_invalid_country_code(self): assert err.error_code is ErrorCode.FIELD_VALUE_REQUIRED.value assert err.message == "Invalid address: [RZ] is not a valid country code." - def test_server_side_error(self): + def test_server_side_error(self) -> None: """DX-1038 - Server-side error.""" try: get_server_side_error() diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 4966e3c..396055a 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -3,7 +3,7 @@ from shipengine_sdk.models import Address -from ..util.test_data import ( +from ..util.test_helpers import ( address_with_warnings, multi_line_address, non_latin_address, @@ -58,7 +58,7 @@ def test_normalize_unknown_address(self) -> None: expected_residential_indicator=None, ) - def test_normalize_multi_line_address(self): + def test_normalize_multi_line_address(self) -> None: """DX-1044 - Normalize multi-line address.""" multi_line = multi_line_address() normalized = normalize_an_address(multi_line) diff --git a/tests/util/test_data.py b/tests/util/test_helpers.py similarity index 99% rename from tests/util/test_data.py rename to tests/util/test_helpers.py index 6c0b5b1..d12b791 100644 --- a/tests/util/test_data.py +++ b/tests/util/test_helpers.py @@ -1,4 +1,4 @@ -"""Test data as functions.""" +"""Test data as functions and common assertion helper functions.""" from typing import Dict, Union from shipengine_sdk import ShipEngine From 2a163f7f4d6f3f8576fedd0fc04b97ac143b9bda Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 11:27:45 -0500 Subject: [PATCH 19/21] DX-1049 - Normalize address with single error message. https://auctane.atlassian.net/browse/DX-1049 --- shipengine_sdk/util/sdk_assertions.py | 2 +- tests/services/test_normalize_address.py | 17 ++++++++++++++++- tests/util/test_helpers.py | 11 +++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/shipengine_sdk/util/sdk_assertions.py b/shipengine_sdk/util/sdk_assertions.py index e8bffa6..b5495c4 100644 --- a/shipengine_sdk/util/sdk_assertions.py +++ b/shipengine_sdk/util/sdk_assertions.py @@ -249,7 +249,7 @@ def does_normalized_address_have_errors(result) -> None: ) elif len(result.errors) == 1: raise ShipEngineError( - message=f"Invalid address.\n {result.errors[0]['message']}", + message=f"Invalid address. {result.errors[0]['message']}", request_id=result.request_id, source=ErrorSource.SHIPENGINE.value, error_type=ErrorType.ERROR.value, diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 396055a..2157102 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -1,9 +1,11 @@ """Test the normalize address method of the ShipEngine SDK.""" import re -from shipengine_sdk.models import Address +from shipengine_sdk.errors import ShipEngineError +from shipengine_sdk.models import Address, ErrorCode, ErrorSource, ErrorType from ..util.test_helpers import ( + address_with_single_error, address_with_warnings, multi_line_address, non_latin_address, @@ -129,3 +131,16 @@ def test_normalize_with_warnings(self) -> None: assert normalized.postal_code == "M6K 3C3" assert normalized.country_code == warning_address.country_code.upper() assert normalized.is_residential is True + + def test_normalize_with_single_error_message(self) -> None: + """DX-1049 - Normalize address with single error message.""" + single_error = address_with_single_error() + try: + normalize_an_address(single_error) + except ShipEngineError as err: + assert err.request_id is not None + assert err.request_id.startswith("req_") is True + assert err.source is ErrorSource.SHIPENGINE.value + assert err.error_type is ErrorType.ERROR.value + assert err.error_code == ErrorCode.MINIMUM_POSTAL_CODE_VERIFICATION_FAILED.value + assert err.message == "Invalid address. Insufficient or inaccurate postal code" diff --git a/tests/util/test_helpers.py b/tests/util/test_helpers.py index d12b791..f74d07e 100644 --- a/tests/util/test_helpers.py +++ b/tests/util/test_helpers.py @@ -59,6 +59,17 @@ def address_with_warnings() -> Address: ) +def address_with_single_error() -> Address: + """Return a test Address object that will cause the server to return a single error message.""" + return Address( + street=["170 Error Blvd"], + city_locality="Boston", + state_province="MA", + postal_code="02215", + country_code="US", + ) + + def address_with_errors() -> Address: """Return a test Address object that will cause the server to return an error message.""" return Address( From c7d404163a248d38ce400207b56967e98109a4ba Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 12:05:53 -0500 Subject: [PATCH 20/21] DX-1050 - Normalize address with multiple error messages. https://auctane.atlassian.net/browse/DX-1050 --- shipengine_sdk/util/sdk_assertions.py | 6 ++++-- tests/services/test_normalize_address.py | 17 +++++++++++++++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/shipengine_sdk/util/sdk_assertions.py b/shipengine_sdk/util/sdk_assertions.py index b5495c4..919f7f4 100644 --- a/shipengine_sdk/util/sdk_assertions.py +++ b/shipengine_sdk/util/sdk_assertions.py @@ -237,11 +237,13 @@ def does_normalized_address_have_errors(result) -> None: """ if len(result.errors) > 1: error_list = list() - map(lambda msg: error_list.append(msg), result.errors) + for err in result.errors: + error_list.append(err["message"]) + str_errors = "\n".join(error_list) raise ShipEngineError( - message=f"Invalid address.\n {str_errors}", + message=f"Invalid address.\n{str_errors}", request_id=result.request_id, source=ErrorSource.SHIPENGINE.value, error_type=ErrorType.ERROR.value, diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 2157102..81ec625 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -5,6 +5,7 @@ from shipengine_sdk.models import Address, ErrorCode, ErrorSource, ErrorType from ..util.test_helpers import ( + address_with_errors, address_with_single_error, address_with_warnings, multi_line_address, @@ -144,3 +145,19 @@ def test_normalize_with_single_error_message(self) -> None: assert err.error_type is ErrorType.ERROR.value assert err.error_code == ErrorCode.MINIMUM_POSTAL_CODE_VERIFICATION_FAILED.value assert err.message == "Invalid address. Insufficient or inaccurate postal code" + + def test_normalize_with_multiple_errors(self) -> None: + """DX-1050 - Normalize address with multiple error messages.""" + errors_address = address_with_errors() + try: + normalize_an_address(errors_address) + except ShipEngineError as err: + assert err.request_id is not None + assert err.request_id.startswith("req_") is True + assert err.source is ErrorSource.SHIPENGINE.value + assert err.error_type is ErrorType.ERROR.value + assert err.error_code is ErrorCode.INVALID_ADDRESS.value + assert ( + err.message + == "Invalid address.\nInvalid City, State, or Zip\nInsufficient or Incorrect Address Data" + ) # noqa From cb4056ea9929977a2263829544bd861366c72d15 Mon Sep 17 00:00:00 2001 From: KaseyCantu Date: Tue, 8 Jun 2021 12:07:25 -0500 Subject: [PATCH 21/21] Housekeeping --- tests/services/test_normalize_address.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/services/test_normalize_address.py b/tests/services/test_normalize_address.py index 81ec625..1ed3f16 100644 --- a/tests/services/test_normalize_address.py +++ b/tests/services/test_normalize_address.py @@ -160,4 +160,4 @@ def test_normalize_with_multiple_errors(self) -> None: assert ( err.message == "Invalid address.\nInvalid City, State, or Zip\nInsufficient or Incorrect Address Data" - ) # noqa + )