Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 8 additions & 7 deletions .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ jobs:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9"]
python-version: ["3.8", "3.9", "3.11"]
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v3
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
Expand All @@ -23,15 +23,16 @@ jobs:
python -m pip install python-dotenv
python -m pip install unittest-data-provider
pip install -r requirements.txt
- name: Run Integration Tests
- name: Run Unit Tests
run:
python -m unittest test/Integration/test_*.py
python -m unittest test/Unit/test_*.py
env:
LOB_API_TEST_KEY: ${{ secrets.LOB_API_TEST_KEY }}
LOB_API_LIVE_KEY: ${{ secrets.LOB_API_LIVE_KEY }}
- name: Run Unit Tests
- name: Run Integration Tests
continue-on-error: true
run:
python -m unittest test/Unit/test_*.py
python -m unittest test/Integration/test_*.py
env:
LOB_API_TEST_KEY: ${{ secrets.LOB_API_TEST_KEY }}
LOB_API_LIVE_KEY: ${{ secrets.LOB_API_LIVE_KEY }}
2 changes: 2 additions & 0 deletions lob_python/model/bank_account.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,7 @@ def openapi_types():
'bank_name': (str, type(None)), # noqa: E501
'verified': (bool, type(None)), # noqa: E501
'deleted': (bool, type(None)), # noqa: E501
'microdeposit_type': (str, type(None)), # noqa: E501
}

@cached_property
Expand All @@ -146,6 +147,7 @@ def discriminator():
'bank_name': 'bank_name', # noqa: E501
'verified': 'verified', # noqa: E501
'deleted': 'deleted', # noqa: E501
'microdeposit_type': 'microdeposit_type', # noqa: E501
}

read_only_vars = {
Expand Down
36 changes: 26 additions & 10 deletions lob_python/model/bank_account_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from lob_python.model_utils import ( # noqa: F401
ApiTypeError,
ApiValueError,
ModelComposed,
ModelNormal,
ModelSimple,
Expand Down Expand Up @@ -65,6 +66,11 @@ class BankAccountVerify(ModelNormal):
'max_items': 2,
'min_items': 2,
},
('descriptor_code',): {
'regex': {
'pattern': r'^SM[a-zA-Z0-9]{4}$',
},
},
}

@cached_property
Expand All @@ -89,6 +95,7 @@ def openapi_types():
"""
return {
'amounts': (list,), # noqa: E501
'descriptor_code': (str,), # noqa: E501
}

@cached_property
Expand All @@ -98,6 +105,7 @@ def discriminator():

attribute_map = {
'amounts': 'amounts', # noqa: E501
'descriptor_code': 'descriptor_code', # noqa: E501
}

read_only_vars = {
Expand All @@ -107,13 +115,12 @@ def discriminator():

@classmethod
@convert_js_args_to_python_args
def _from_openapi_data(cls, amounts, *args, **kwargs): # noqa: E501
def _from_openapi_data(cls, *args, **kwargs): # noqa: E501
"""BankAccountVerify - a model defined in OpenAPI

Args:
amounts (list): In live mode, an array containing the two micro deposits (in cents) placed in the bank account. In test mode, no micro deposits will be placed, so any two integers between `1` and `100` will work.

Keyword Args:
amounts (list): In live mode, an array containing the two micro deposits (in cents) placed in the bank account. In test mode, no micro deposits will be placed, so any two integers between `1` and `100` will work. [optional] # noqa: E501
descriptor_code (str): The 6-character code (beginning with SM) from the bank statement descriptor of the single $0.01 microdeposit. Required when microdeposit_type is descriptor_code. [optional] # noqa: E501
_check_type (bool): if True, values for parameters in openapi_types
will be type checked and a TypeError will be
raised if the wrong type is input.
Expand Down Expand Up @@ -171,7 +178,6 @@ def _from_openapi_data(cls, amounts, *args, **kwargs): # noqa: E501
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)

self.amounts = amounts
for var_name, var_value in kwargs.items():
if var_name not in self.attribute_map and \
self._configuration is not None and \
Expand All @@ -192,13 +198,12 @@ def _from_openapi_data(cls, amounts, *args, **kwargs): # noqa: E501
])

@convert_js_args_to_python_args
def __init__(self, amounts, *args, **kwargs): # noqa: E501
def __init__(self, *args, **kwargs): # noqa: E501
"""BankAccountVerify - a model defined in OpenAPI

Args:
amounts ([Cents]): In live mode, an array containing the two micro deposits (in cents) placed in the bank account. In test mode, no micro deposits will be placed, so any two integers between `1` and `100` will work.

Keyword Args:
amounts ([Cents]): In live mode, an array containing the two micro deposits (in cents) placed in the bank account. In test mode, no micro deposits will be placed, so any two integers between `1` and `100` will work. [optional] # noqa: E501
descriptor_code (str): The 6-character code (beginning with SM) from the bank statement descriptor of the single $0.01 microdeposit. Required when microdeposit_type is descriptor_code. [optional] # noqa: E501
_check_type (bool): if True, values for parameters in openapi_types
will be type checked and a TypeError will be
raised if the wrong type is input.
Expand Down Expand Up @@ -254,7 +259,18 @@ def __init__(self, amounts, *args, **kwargs): # noqa: E501
self._configuration = _configuration
self._visited_composed_classes = _visited_composed_classes + (self.__class__,)

self.amounts = amounts
has_amounts = 'amounts' in kwargs
has_descriptor_code = 'descriptor_code' in kwargs

if not has_amounts and not has_descriptor_code:
raise ApiValueError(
"one of `amounts` or `descriptor_code` must be provided"
)
if has_amounts and has_descriptor_code:
raise ApiValueError(
"only one of `amounts` or `descriptor_code` may be provided"
)

for var_name, var_value in kwargs.items():
if var_name not in self.attribute_map and \
self._configuration is not None and \
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from setuptools import setup, find_packages # noqa: H301

NAME = "lob-python"
VERSION = "5.1.3"
VERSION = "5.2.0"
# To install the library, run the following
#
# python setup.py install
Expand Down
2 changes: 1 addition & 1 deletion test/Integration/test_addresses_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ def test_create422_addr_too_long(self):
)
with self.assertRaises(Exception) as context:
self.api.create(faulty_address)
self.assertTrue("address_line1 length must be less than or equal to 64 characters long" in context.exception.__str__())
self.assertTrue("address_line1" in context.exception.__str__())

def test_get200(self):
"""Test case for get
Expand Down
15 changes: 15 additions & 0 deletions test/Integration/test_bank_accounts_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -271,6 +271,21 @@ def test_delete404(self):
self.api.delete("bank_fake")
self.assertTrue("bank account not found" in context.exception.__str__())

def test_verify_with_descriptor_code200(self):
"""Test case for verify using descriptor_code path"""
bank_verify = BankAccountVerify(descriptor_code="SM11AA")
created_bank = self.api.create(self.bank_writable)
verified_bank_acc = self.api.verify(created_bank.id, bank_verify)
self.bank_ids.append(verified_bank_acc.id)
self.assertIsNotNone(verified_bank_acc.id)

def test_bank_account_has_microdeposit_type(self):
"""Test that a freshly created bank account exposes microdeposit_type"""
created_bank = self.api.create(self.bank_writable)
retrieved_bank = self.api.get(created_bank.id)
self.bank_ids.append(created_bank.id)
self.assertIn(retrieved_bank.microdeposit_type, ["amounts", "descriptor_code", None])


if __name__ == '__main__':
unittest.main()
3 changes: 1 addition & 2 deletions test/Integration/test_checks_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,6 @@ def setUpClass(self):
),
mail_type="usps_first_class",
merge_variables=MergeVariables(),
send_date=now + dt.timedelta(days=30),
memo = "Test Check Memo",
check_number = 2,
logo = "https://s3.us-west-2.amazonaws.com/public.lob.com/assets/check_logo.png",
Expand Down Expand Up @@ -297,7 +296,7 @@ def test_list200(self):
# perform test with after query param
if next:
listed_checks_after = self.api.list(limit=2, after=next)
self.assertEqual(len(listed_checks_after.data), 2)
self.assertGreaterEqual(len(listed_checks_after.data), 1)
self.assertIsNotNone(listed_checks_after.data[0]['id'])
prev = listed_checks_after.getPreviousPageToken()
if prev:
Expand Down
6 changes: 3 additions & 3 deletions test/Integration/test_intl_verifications_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,9 +57,9 @@ def setUpClass(self):
country = CountryExtended("GB")
)
self.mc2 = MultipleComponentsIntl(
primary_line = "10 DOWNING ST",
primary_line = "1 FAKE POTATO LANE",
city = "LONDON",
postal_code = "SW1A 2AB",
postal_code = "ZC4Z 46Z",
country = CountryExtended("GB")
)
self.address_list = IntlVerificationsPayload(
Expand Down Expand Up @@ -95,7 +95,7 @@ def test_verifyBulk_valid_addresses(self):
verified_list = self.api.verifyBulk(self.address_list)
self.assertEqual(len(verified_list.addresses), 2)
self.assertEqual(verified_list.addresses[0]['deliverability'], "deliverable")
self.assertEqual(verified_list.addresses[1]['deliverability'], "deliverable_missing_info")
self.assertEqual(verified_list.addresses[1]['deliverability'], "undeliverable")

def test_verifyBulk422(self):
"""Test case for verifyBulk
Expand Down
3 changes: 1 addition & 2 deletions test/Integration/test_letters_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def setUpClass(self):
),
mail_type=MailType("usps_first_class"),
merge_variables=MergeVariables(),
send_date=now + dt.timedelta(days=30),
double_sided = True,
return_envelope = True,
perforated_page = 1,
Expand Down Expand Up @@ -328,7 +327,7 @@ def test_list200(self):
# perform test with after query param
if next:
listed_letters_after = self.api.list(limit=2, after=next)
self.assertEqual(len(listed_letters_after.data), 2)
self.assertGreaterEqual(len(listed_letters_after.data), 1)
self.assertIsNotNone(listed_letters_after.data[0]['id'])
prev = listed_letters_after.getPreviousPageToken()
if prev:
Expand Down
3 changes: 1 addition & 2 deletions test/Integration/test_postcards_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,6 @@ def setUpClass(self):
),
mail_type=MailType("usps_first_class"),
merge_variables=MergeVariables(),
send_date=now + dt.timedelta(days=30),
front = "https://s3-us-west-2.amazonaws.com/public.lob.com/assets/templates/4x6_pc_template.pdf",
back = "https://s3-us-west-2.amazonaws.com/public.lob.com/assets/templates/4x6_pc_template.pdf",
use_type= PscUseType("marketing")
Expand Down Expand Up @@ -258,7 +257,7 @@ def test_list200(self):
# perform test with after query param
if next:
listed_postcards_after = self.api.list(limit=2, after=next)
self.assertEqual(len(listed_postcards_after.data), 2)
self.assertGreaterEqual(len(listed_postcards_after.data), 1)
self.assertIsNotNone(listed_postcards_after.data[0]['id'])
prev = listed_postcards_after.getPreviousPageToken()
if prev:
Expand Down
4 changes: 1 addition & 3 deletions test/Integration/test_self_mailers_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,8 @@ def setUpClass(self):
),
mail_type=MailType("usps_first_class"),
merge_variables=MergeVariables(),
send_date=now + dt.timedelta(days=30),
inside = "https://s3.us-west-2.amazonaws.com/public.lob.com/assets/templates/self_mailers/6x18_sfm_inside.pdf",
outside = "https://s3.us-west-2.amazonaws.com/public.lob.com/assets/templates/self_mailers/6x18_sfm_inside.pdf",
billing_group_id = "bg_5c79d158d8f69e3e0",
use_type= SfmUseType("marketing")
)

Expand Down Expand Up @@ -279,7 +277,7 @@ def test_list200(self):
# perform test with after query param
if next:
listed_self_mailers_after = self.api.list(limit=2, after=next)
self.assertEqual(len(listed_self_mailers_after.data), 2)
self.assertGreaterEqual(len(listed_self_mailers_after.data), 1)
self.assertIsNotNone(listed_self_mailers_after.data[0]['id'])
prev = listed_self_mailers_after.getPreviousPageToken()
if prev:
Expand Down
67 changes: 64 additions & 3 deletions test/Unit/test_bank_accounts_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@

import lob_python
import os
from lob_python.model.bank_account import BankAccount
from lob_python.model.bank_account_verify import BankAccountVerify
from lob_python.model.bank_type_enum import BankTypeEnum
from lob_python.api.bank_accounts_api import BankAccountsApi # noqa: E501
from lob_python.model.bank_account_writable import BankAccountWritable # noqa: E501
from lob_python.model.metadata_model import MetadataModel
from lob_python.model.include_model import IncludeModel
from lob_python.exceptions import UnauthorizedException, NotFoundException, ApiException
from lob_python.model_utils import ApiValueError
from unittest.mock import Mock, MagicMock

class TestBankAccountsApi(unittest.TestCase):
Expand All @@ -42,9 +44,7 @@ def setUp(self):
signatory = "fake signatory",
)

self.bank_account_verify = BankAccountVerify(
amounts = [1, 2]
)
self.bank_account_verify = BankAccountVerify(amounts=[1, 2])

self.mock_list_of_bank_accounts = MagicMock(return_value={
"data": [{ "id": "fake 1" }, { "id": "fake 2" }]
Expand Down Expand Up @@ -217,5 +217,66 @@ def test_bank_account_delete_error_handle(self):
self.mock_api.bank_account_delete("bank_fakeId")
self.assertTrue("Not Found" in context.exception.__str__())

def test_bank_account_verify_with_descriptor_code(self):
"""Test case for creating BankAccountVerify with descriptor_code"""
verify = BankAccountVerify(descriptor_code="SM11AA")
self.assertIsNotNone(verify)

def test_bank_account_verify_fails_with_both_amounts_and_descriptor_code(self):
"""Test that providing both amounts and descriptor_code raises ApiValueError"""
with self.assertRaises(ApiValueError) as context:
BankAccountVerify(amounts=[1, 2], descriptor_code="SM11AA")
self.assertIn("only one of", str(context.exception))

def test_bank_account_verify_fails_with_neither(self):
"""Test that providing neither amounts nor descriptor_code raises ApiValueError"""
with self.assertRaises(ApiValueError) as context:
BankAccountVerify()
self.assertIn("one of `amounts` or `descriptor_code` must be provided", str(context.exception))

def test_bank_account_verify_fails_with_invalid_descriptor_code_pattern(self):
"""Test that an invalid descriptor_code pattern raises a validation error"""
with self.assertRaises(Exception):
BankAccountVerify(descriptor_code="INVALID")

def test_bank_account_has_microdeposit_type(self):
"""Test that BankAccount accepts and returns microdeposit_type"""
import datetime
account = BankAccount(
routing_number="322271627",
account_number="123456789",
account_type="individual",
signatory="Test User",
id="bank_fakeId",
date_created=datetime.datetime.now(),
date_modified=datetime.datetime.now(),
microdeposit_type="amounts",
)
self.assertEqual(account.microdeposit_type, "amounts")

account2 = BankAccount(
routing_number="322271627",
account_number="123456789",
account_type="individual",
signatory="Test User",
id="bank_fakeId2",
date_created=datetime.datetime.now(),
date_modified=datetime.datetime.now(),
microdeposit_type="descriptor_code",
)
self.assertEqual(account2.microdeposit_type, "descriptor_code")

account3 = BankAccount(
routing_number="322271627",
account_number="123456789",
account_type="individual",
signatory="Test User",
id="bank_fakeId3",
date_created=datetime.datetime.now(),
date_modified=datetime.datetime.now(),
)
self.assertFalse(hasattr(account3, 'microdeposit_type') and account3.microdeposit_type is not None)


if __name__ == '__main__':
unittest.main()
Loading