Skip to content

Commit b001f2e

Browse files
author
Lucas McDonald
committed
sync
1 parent 665749d commit b001f2e

File tree

2 files changed

+39
-97
lines changed

2 files changed

+39
-97
lines changed

DynamoDbEncryption/runtimes/python/test/integ/encrypted/test_paginator.py

Lines changed: 24 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,15 @@
2121
basic_put_item_request_ddb,
2222
basic_put_item_request_dict,
2323
basic_query_paginator_request,
24-
basic_scan_request_ddb,
25-
basic_scan_request_dict,
24+
basic_scan_paginator_request,
2625
)
27-
28-
BOTO3_CLIENT = boto3.client("dynamodb")
29-
ENCRYPTED_CLIENT = EncryptedClient(client=BOTO3_CLIENT, encryption_config=INTEG_TEST_DEFAULT_TABLE_CONFIGS)
30-
SCAN_PAGINATOR = ENCRYPTED_CLIENT.get_paginator("scan")
31-
QUERY_PAGINATOR = ENCRYPTED_CLIENT.get_paginator("query")
26+
from . import sort_dynamodb_json_lists
3227

3328

29+
# Creates a matrix of tests for each value in param,
30+
# with a user-friendly string for test output:
31+
# expect_standard_dictionaries = True -> "standard_dicts"
32+
# expect_standard_dictionaries = False -> "ddb_json"
3433
@pytest.fixture(params=[True, False], ids=["standard_dicts", "ddb_json"])
3534
def expect_standard_dictionaries(request):
3635
return request.param
@@ -52,6 +51,10 @@ def plaintext_client(expect_standard_dictionaries):
5251
return client
5352

5453

54+
# Creates a matrix of tests for each value in param,
55+
# with a user-friendly string for test output:
56+
# encrypted = True -> "encrypted"
57+
# encrypted = False -> "plaintext"
5558
@pytest.fixture(params=[True, False], ids=["encrypted", "plaintext"])
5659
def encrypted(request):
5760
return request.param
@@ -75,6 +78,10 @@ def scan_paginator(client):
7578
return client.get_paginator("scan")
7679

7780

81+
# Creates a matrix of tests for each value in param,
82+
# with a user-friendly string for test output:
83+
# use_complex_item = True -> "complex_item"
84+
# use_complex_item = False -> "simple_item"
7885
@pytest.fixture(params=[True, False], ids=["complex_item", "simple_item"])
7986
def use_complex_item(request):
8087
return request.param
@@ -128,22 +135,6 @@ def put_item_request(expect_standard_dictionaries, test_item):
128135
return basic_put_item_request_ddb(test_item)
129136

130137

131-
def sort_dynamodb_json_lists(obj):
132-
"""
133-
Utility that recursively sorts all lists in a DynamoDB JSON-like structure.
134-
DynamoDB JSON uses lists to represent sets, so strict equality can fail.
135-
Sort lists to ensure consistent ordering when comparing expected and actual items.
136-
"""
137-
if isinstance(obj, dict):
138-
return {k: sort_dynamodb_json_lists(v) for k, v in obj.items()}
139-
elif isinstance(obj, list):
140-
try:
141-
return sorted(obj) # Sort lists for consistent comparison
142-
except TypeError:
143-
return obj # Not all lists are sortable; ex. complex_item_ddb's "list" attribute
144-
return obj
145-
146-
147138
def test_GIVEN_query_paginator_WHEN_paginate_THEN_returns_expected_items(
148139
client, query_paginator, paginate_query_request, put_item_request, test_item
149140
):
@@ -168,15 +159,11 @@ def test_GIVEN_query_paginator_WHEN_paginate_THEN_returns_expected_items(
168159

169160

170161
@pytest.fixture
171-
def paginate_scan_request(expect_standard_dictionaries, encrypted, test_item):
162+
def paginate_scan_request(expect_standard_dictionaries, test_item):
172163
if expect_standard_dictionaries:
173-
request = {**basic_scan_request_dict(test_item), "TableName": INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME}
164+
request = {**basic_scan_paginator_request(test_item), "TableName": INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME}
174165
else:
175-
request = basic_scan_request_ddb(test_item)
176-
if encrypted:
177-
request["FilterExpression"] = request["FilterExpression"] + " AND attribute_exists (#sig)"
178-
request["ExpressionAttributeNames"] = {}
179-
request["ExpressionAttributeNames"]["#sig"] = "amzn-ddb-map-sig"
166+
request = basic_scan_paginator_request(test_item)
180167
return request
181168

182169

@@ -194,70 +181,10 @@ def test_GIVEN_scan_paginator_WHEN_paginate_THEN_returns_expected_items(
194181
if "Items" in page:
195182
for item in page["Items"]:
196183
items.append(item)
197-
198-
199-
# TODO: set up scan table and tests
200-
201-
# @pytest.fixture
202-
# def default_requests(simple_item_ddb):
203-
# """Fixture to provide default scan and query requests."""
204-
# return {
205-
# "scan": get_scan_request(simple_item_ddb["partition_key"]),
206-
# "query": get_query_request(simple_item_ddb["partition_key"]),
207-
# }
208-
209-
# @pytest.fixture(scope="module", autouse=True)
210-
# def setup_module_fixture(simple_item_ddb):
211-
# # Runs before all tests in this module
212-
# for i in range(10):
213-
# ENCRYPTED_CLIENT.put_item(
214-
# TableName=INTEG_TEST_DEFAULT_DYNAMODB_TABLE_NAME,
215-
# Item={
216-
# "partition_key": simple_item_ddb["partition_key"],
217-
# "sort_key": {"N": str(i)},
218-
# "attribute1": {"S": "encrypt and sign me!"},
219-
# "attribute2": {"S": "sign me!"},
220-
# ":attribute3": {"S": "ignore me!"},
221-
# },
222-
# )
223-
# # Yield to all tests in this module
224-
# yield
225-
# # TODO: Delete items? not needed but nice
226-
227-
# @pytest.mark.parametrize("paginator_name, request_key", [
228-
# ("scan", "scan"),
229-
# ("query", "query")
230-
# ])
231-
# def test_GIVEN_incomplete_search_WHEN_build_full_result_THEN_NextToken_completes_search(
232-
# paginator_name,
233-
# request_key,
234-
# default_requests,
235-
# ):
236-
# # TODO: test
237-
# return
238-
# # Given: PaginationConfig that will result in an incomplete search.
239-
# # There are 10 items, and returning 2 per operation results in multiple uses of NextToken.
240-
# pagination_config = {
241-
# "MaxItems": 2,
242-
# }
243-
244-
# paginator = getattr(ENCRYPTED_CLIENT, f"get_paginator")(paginator_name)
245-
# incomplete_search_request = default_requests[request_key].copy()
246-
# incomplete_search_request["PaginationConfig"] = pagination_config
247-
248-
# # When: Build full result from paginator
249-
# full_result = paginator.paginate(**incomplete_search_request)
250-
251-
# print(f"{full_result=}")
252-
253-
# collected_items = []
254-
255-
# print(f'{full_result=}')
256-
257-
# for item in full_result:
258-
# # assert item["attribute1"]["S"] == "encrypt and sign me!"
259-
# collected_items.append(item)
260-
261-
# # Then: NextToken should be present
262-
# # assert "NextToken" in full_result
263-
# # Then: NextToken is able to complete the search
184+
assert len(items) == 1
185+
# DynamoDB JSON uses lists to represent sets, so strict equality can fail.
186+
# Sort lists to ensure consistent ordering when comparing expected and actual items.
187+
expected_item = sort_dynamodb_json_lists(test_item)
188+
actual_item = sort_dynamodb_json_lists(items[0])
189+
# Then: Items are equal
190+
assert expected_item == actual_item

DynamoDbEncryption/runtimes/python/test/unit/encrypted/test_paginator.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,3 +61,18 @@ def test_GIVEN_kwargs_has_PaginationConfig_WHEN_paginate_THEN_PaginationConfig_i
6161
mock_input_transform_method.assert_called_once_with(
6262
QueryInputTransformInput(sdk_input=kwargs_without_pagination_config)
6363
)
64+
65+
66+
def test_GIVEN_invalid_class_attribute_WHEN_getattr_THEN_raise_error():
67+
# Create a mock with a specific spec that excludes our unknown attribute
68+
mock_boto3_dynamodb_client = MagicMock(spec=["put_item", "get_item", "query", "scan"])
69+
encrypted_paginator = EncryptedPaginator(
70+
paginator=mock_boto3_dynamodb_client,
71+
encryption_config=mock_tables_encryption_config,
72+
)
73+
74+
# Then: AttributeError is raised
75+
with pytest.raises(AttributeError):
76+
# Given: Invalid class attribute: not_a_valid_attribute_on_EncryptedClient_nor_boto3_client
77+
# When: getattr is called
78+
encrypted_paginator.not_a_valid_attribute_on_EncryptedPaginator_nor_boto3_paginator()

0 commit comments

Comments
 (0)