Skip to content

Commit 9486472

Browse files
chore(ci): add the Idempotency feature to nox tests (#4585)
Adding Idempotency test
1 parent 6f291bf commit 9486472

File tree

9 files changed

+259
-180
lines changed

9 files changed

+259
-180
lines changed

noxfile.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ def test_with_boto3_sdk_as_required_package(session: nox.Session):
110110
f"{PREFIX_TESTS_FUNCTIONAL}/feature_flags/_boto3/",
111111
f"{PREFIX_TESTS_UNIT}/data_classes/_boto3/",
112112
f"{PREFIX_TESTS_FUNCTIONAL}/streaming/_boto3/",
113+
f"{PREFIX_TESTS_FUNCTIONAL}/idempotency/_boto3/",
113114
],
114115
extras="aws-sdk",
115116
)
@@ -158,3 +159,37 @@ def test_with_pydantic_required_package(session: nox.Session, pydantic: str):
158159
f"{PREFIX_TESTS_UNIT}/parser/_pydantic/",
159160
],
160161
)
162+
163+
164+
@nox.session()
165+
@nox.parametrize("pydantic", ["1.10", "2.0"])
166+
def test_with_boto3_and_pydantic_required_package(session: nox.Session, pydantic: str):
167+
"""Tests that only depends for Boto3 + Pydantic library v1 and v2"""
168+
# Idempotency with custom serializer
169+
170+
session.install(f"pydantic>={pydantic}")
171+
172+
build_and_run_test(
173+
session,
174+
folders=[
175+
f"{PREFIX_TESTS_FUNCTIONAL}/idempotency/_pydantic/",
176+
],
177+
extras="aws-sdk",
178+
)
179+
180+
181+
@nox.session()
182+
def test_with_redis_and_boto3_sdk_as_required_package(session: nox.Session):
183+
"""Tests that depends on Redis library"""
184+
# Idempotency - Redis backend
185+
186+
# Our Redis tests requires multiprocess library to simulate Race Condition
187+
session.run("pip", "install", "multiprocess")
188+
189+
build_and_run_test(
190+
session,
191+
folders=[
192+
f"{PREFIX_TESTS_FUNCTIONAL}/idempotency/_redis/",
193+
],
194+
extras="redis,aws-sdk",
195+
)

tests/functional/idempotency/_boto3/__init__.py

Whitespace-only changes.

tests/functional/idempotency/test_idempotency.py renamed to tests/functional/idempotency/_boto3/test_idempotency.py

Lines changed: 1 addition & 178 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import pytest
99
from botocore import stub
1010
from botocore.config import Config
11-
from pydantic import BaseModel
1211
from pytest import FixtureRequest
1312
from pytest_mock import MockerFixture
1413

@@ -32,7 +31,6 @@
3231
IdempotencyInconsistentStateError,
3332
IdempotencyInvalidStatusError,
3433
IdempotencyKeyError,
35-
IdempotencyModelTypeError,
3634
IdempotencyNoSerializationModelError,
3735
IdempotencyPersistenceLayerError,
3836
IdempotencyValidationError,
@@ -47,9 +45,6 @@
4745
from aws_lambda_powertools.utilities.idempotency.serialization.dataclass import (
4846
DataclassSerializer,
4947
)
50-
from aws_lambda_powertools.utilities.idempotency.serialization.pydantic import (
51-
PydanticSerializer,
52-
)
5348
from aws_lambda_powertools.utilities.validation import envelopes, validator
5449
from aws_lambda_powertools.warnings import PowertoolsUserWarning
5550
from tests.functional.idempotency.utils import (
@@ -61,7 +56,7 @@
6156
from tests.functional.utils import json_serialize, load_event
6257

6358
TABLE_NAME = "TEST_TABLE"
64-
TESTS_MODULE_PREFIX = "test-func.tests.functional.idempotency.test_idempotency"
59+
TESTS_MODULE_PREFIX = "test-func.tests.functional.idempotency._boto3.test_idempotency"
6560

6661

6762
def get_dataclasses_lib():
@@ -1315,106 +1310,6 @@ def record_handler(record):
13151310
assert from_dict_called is False, "in case response is None, from_dict should not be called"
13161311

13171312

1318-
@pytest.mark.parametrize("output_serializer_type", ["explicit", "deduced"])
1319-
def test_idempotent_function_serialization_pydantic(output_serializer_type: str):
1320-
# GIVEN
1321-
config = IdempotencyConfig(use_local_cache=True)
1322-
mock_event = {"customer_id": "fake", "transaction_id": "fake-id"}
1323-
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_serialization_pydantic.<locals>.collect_payment#{hash_idempotency_key(mock_event)}" # noqa E501
1324-
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
1325-
1326-
class PaymentInput(BaseModel):
1327-
customer_id: str
1328-
transaction_id: str
1329-
1330-
class PaymentOutput(BaseModel):
1331-
customer_id: str
1332-
transaction_id: str
1333-
1334-
if output_serializer_type == "explicit":
1335-
output_serializer = PydanticSerializer(
1336-
model=PaymentOutput,
1337-
)
1338-
else:
1339-
output_serializer = PydanticSerializer
1340-
1341-
@idempotent_function(
1342-
data_keyword_argument="payment",
1343-
persistence_store=persistence_layer,
1344-
config=config,
1345-
output_serializer=output_serializer,
1346-
)
1347-
def collect_payment(payment: PaymentInput) -> PaymentOutput:
1348-
return PaymentOutput(**payment.dict())
1349-
1350-
# WHEN
1351-
payment = PaymentInput(**mock_event)
1352-
first_call: PaymentOutput = collect_payment(payment=payment)
1353-
assert first_call.customer_id == payment.customer_id
1354-
assert first_call.transaction_id == payment.transaction_id
1355-
assert isinstance(first_call, PaymentOutput)
1356-
second_call: PaymentOutput = collect_payment(payment=payment)
1357-
assert isinstance(second_call, PaymentOutput)
1358-
assert second_call.customer_id == payment.customer_id
1359-
assert second_call.transaction_id == payment.transaction_id
1360-
1361-
1362-
def test_idempotent_function_serialization_pydantic_failure_no_return_type():
1363-
# GIVEN
1364-
config = IdempotencyConfig(use_local_cache=True)
1365-
mock_event = {"customer_id": "fake", "transaction_id": "fake-id"}
1366-
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_serialization_pydantic_failure_no_return_type.<locals>.collect_payment#{hash_idempotency_key(mock_event)}" # noqa E501
1367-
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
1368-
1369-
class PaymentInput(BaseModel):
1370-
customer_id: str
1371-
transaction_id: str
1372-
1373-
class PaymentOutput(BaseModel):
1374-
customer_id: str
1375-
transaction_id: str
1376-
1377-
idempotent_function_decorator = idempotent_function(
1378-
data_keyword_argument="payment",
1379-
persistence_store=persistence_layer,
1380-
config=config,
1381-
output_serializer=PydanticSerializer,
1382-
)
1383-
with pytest.raises(IdempotencyNoSerializationModelError, match="No serialization model was supplied"):
1384-
1385-
@idempotent_function_decorator
1386-
def collect_payment(payment: PaymentInput):
1387-
return PaymentOutput(**payment.dict())
1388-
1389-
1390-
def test_idempotent_function_serialization_pydantic_failure_bad_type():
1391-
# GIVEN
1392-
config = IdempotencyConfig(use_local_cache=True)
1393-
mock_event = {"customer_id": "fake", "transaction_id": "fake-id"}
1394-
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_serialization_pydantic_failure_no_return_type.<locals>.collect_payment#{hash_idempotency_key(mock_event)}" # noqa E501
1395-
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
1396-
1397-
class PaymentInput(BaseModel):
1398-
customer_id: str
1399-
transaction_id: str
1400-
1401-
class PaymentOutput(BaseModel):
1402-
customer_id: str
1403-
transaction_id: str
1404-
1405-
idempotent_function_decorator = idempotent_function(
1406-
data_keyword_argument="payment",
1407-
persistence_store=persistence_layer,
1408-
config=config,
1409-
output_serializer=PydanticSerializer,
1410-
)
1411-
with pytest.raises(IdempotencyModelTypeError, match="Model type is not inherited from pydantic BaseModel"):
1412-
1413-
@idempotent_function_decorator
1414-
def collect_payment(payment: PaymentInput) -> dict:
1415-
return PaymentOutput(**payment.dict())
1416-
1417-
14181313
@pytest.mark.parametrize("output_serializer_type", ["explicit", "deduced"])
14191314
def test_idempotent_function_serialization_dataclass(output_serializer_type: str):
14201315
# GIVEN
@@ -1493,37 +1388,6 @@ def collect_payment(payment: PaymentInput):
14931388
return PaymentOutput(**payment.dict())
14941389

14951390

1496-
def test_idempotent_function_serialization_dataclass_failure_bad_type():
1497-
# GIVEN
1498-
dataclasses = get_dataclasses_lib()
1499-
config = IdempotencyConfig(use_local_cache=True)
1500-
mock_event = {"customer_id": "fake", "transaction_id": "fake-id"}
1501-
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_serialization_pydantic_failure_no_return_type.<locals>.collect_payment#{hash_idempotency_key(mock_event)}" # noqa E501
1502-
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
1503-
1504-
@dataclasses.dataclass
1505-
class PaymentInput:
1506-
customer_id: str
1507-
transaction_id: str
1508-
1509-
@dataclasses.dataclass
1510-
class PaymentOutput:
1511-
customer_id: str
1512-
transaction_id: str
1513-
1514-
idempotent_function_decorator = idempotent_function(
1515-
data_keyword_argument="payment",
1516-
persistence_store=persistence_layer,
1517-
config=config,
1518-
output_serializer=PydanticSerializer,
1519-
)
1520-
with pytest.raises(IdempotencyModelTypeError, match="Model type is not inherited from pydantic BaseModel"):
1521-
1522-
@idempotent_function_decorator
1523-
def collect_payment(payment: PaymentInput) -> dict:
1524-
return PaymentOutput(**payment.dict())
1525-
1526-
15271391
def test_idempotent_function_arbitrary_args_kwargs():
15281392
# Scenario to validate we can use idempotent_function with a function
15291393
# with an arbitrary number of args and kwargs
@@ -1804,24 +1668,6 @@ class Foo:
18041668
assert as_dict == expected_result
18051669

18061670

1807-
def test_idempotent_function_pydantic():
1808-
# Scenario _prepare_data should convert a pydantic to a dict
1809-
class Foo(BaseModel):
1810-
name: str
1811-
1812-
expected_result = {"name": "Bar"}
1813-
data = Foo(name="Bar")
1814-
as_dict = _prepare_data(data)
1815-
assert as_dict == data.dict()
1816-
assert as_dict == expected_result
1817-
1818-
1819-
@pytest.mark.parametrize("data", [None, "foo", ["foo"], 1, True, {}])
1820-
def test_idempotent_function_other(data):
1821-
# All other data types should be left as is
1822-
assert _prepare_data(data) == data
1823-
1824-
18251671
def test_idempotent_function_dataclass_with_jmespath():
18261672
# GIVEN
18271673
dataclasses = get_dataclasses_lib()
@@ -1847,29 +1693,6 @@ def collect_payment(payment: Payment):
18471693
assert result == payment.transaction_id
18481694

18491695

1850-
def test_idempotent_function_pydantic_with_jmespath():
1851-
# GIVEN
1852-
config = IdempotencyConfig(event_key_jmespath="transaction_id", use_local_cache=True)
1853-
mock_event = {"customer_id": "fake", "transaction_id": "fake-id"}
1854-
idempotency_key = f"{TESTS_MODULE_PREFIX}.test_idempotent_function_pydantic_with_jmespath.<locals>.collect_payment#{hash_idempotency_key(mock_event['transaction_id'])}" # noqa E501
1855-
persistence_layer = MockPersistenceLayer(expected_idempotency_key=idempotency_key)
1856-
1857-
class Payment(BaseModel):
1858-
customer_id: str
1859-
transaction_id: str
1860-
1861-
@idempotent_function(data_keyword_argument="payment", persistence_store=persistence_layer, config=config)
1862-
def collect_payment(payment: Payment):
1863-
return payment.transaction_id
1864-
1865-
# WHEN
1866-
payment = Payment(**mock_event)
1867-
result = collect_payment(payment=payment)
1868-
1869-
# THEN idempotency key assertion happens at MockPersistenceLayer
1870-
assert result == payment.transaction_id
1871-
1872-
18731696
@pytest.mark.parametrize("idempotency_config", [{"use_local_cache": False}], indirect=True)
18741697
def test_idempotent_lambda_compound_already_completed(
18751698
idempotency_config: IdempotencyConfig,

tests/functional/idempotency/_pydantic/__init__.py

Whitespace-only changes.

0 commit comments

Comments
 (0)