Skip to content

Commit d1e160a

Browse files
author
Lucas McDonald
committed
sync
1 parent 0f5647e commit d1e160a

File tree

5 files changed

+259
-39
lines changed

5 files changed

+259
-39
lines changed

DynamoDbEncryption/runtimes/python/test/requests.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,17 @@ def base_exhaustive_scan_request(item):
260260
}
261261

262262

263+
# No exhaustive requests for:
264+
# - transact_write_items
265+
# - transact_get_items
266+
# - batch_write_item
267+
# - batch_get_item
268+
# - batch_execute_statement
269+
# - execute_statement
270+
# - execute_transaction
271+
# The base requests sufficiently test the conversion of the request between client and resource formats
272+
# for items.
273+
263274
# DDB format request functions
264275

265276

DynamoDbEncryption/runtimes/python/test/responses.py

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,16 +154,109 @@ def basic_transact_write_items_response(items):
154154
}
155155

156156

157+
# No exhaustive response for transact_write_items;
158+
# The basic_transact_write_items_response is sufficient
159+
160+
157161
def basic_transact_get_items_response(items):
158162
"""Get a transact_get_items response in resource (ddb) format for any items."""
159163
return {"Responses": [{"Item": item} for item in items]}
160164

161165

166+
# No exhaustive response for transact_get_items;
167+
# The basic_transact_get_items_response is sufficient
168+
169+
162170
def basic_update_item_response(item):
163171
"""Get an update_item response in resource (ddb) format for any item."""
164172
return {"Attributes": item}
165173

166174

175+
def exhaustive_update_item_response(item):
176+
"""
177+
Get an update_item response in resource (ddb) format for any item.
178+
This is not intended to be a real response that DynamoDB would return,
179+
but the response should contain additional attributes that DynamoDB could return.
180+
This is only intended to exhaustively test the conversion of the request between client and resource formats.
181+
"""
182+
base = basic_update_item_response(item)
183+
additional_keys = {
184+
"ItemCollectionMetrics": {
185+
"ItemCollectionKey": {"partition_key": item["partition_key"]},
186+
},
187+
}
188+
return {**base, **additional_keys}
189+
190+
167191
def basic_delete_item_response(item):
168192
"""Get a delete_item response in resource (ddb) format for any item."""
169193
return {"Attributes": item}
194+
195+
196+
def exhaustive_delete_item_response(item):
197+
"""
198+
Get a delete_item response in resource (ddb) format for any item.
199+
This is not intended to be a real response that DynamoDB would return,
200+
but the response should contain additional attributes that DynamoDB could return.
201+
This is only intended to exhaustively test the conversion of the request between client and resource formats.
202+
"""
203+
base = basic_delete_item_response(item)
204+
additional_keys = {
205+
"ItemCollectionMetrics": {
206+
"ItemCollectionKey": {"partition_key": item["partition_key"]},
207+
},
208+
}
209+
return {**base, **additional_keys}
210+
211+
212+
def basic_execute_statement_response(items):
213+
"""Get an execute_statement response in resource (ddb) format for any items."""
214+
return {"Items": items}
215+
216+
217+
def exhaustive_execute_statement_response(items):
218+
"""
219+
Get an execute_statement response in resource (ddb) format for any items.
220+
This is not intended to be a real response that DynamoDB would return,
221+
but the response should contain additional attributes that DynamoDB could return.
222+
This is only intended to exhaustively test the conversion of the request between client and resource formats.
223+
"""
224+
base = basic_execute_statement_response(items)
225+
additional_keys = {
226+
"LastEvaluatedKey": {
227+
"partition_key": items[-1]["partition_key"],
228+
"sort_key": items[-1]["sort_key"],
229+
},
230+
}
231+
return {**base, **additional_keys}
232+
233+
234+
def basic_execute_transaction_response(items):
235+
"""Get an execute_transaction response in resource (ddb) format for any items."""
236+
return {"Responses": [{"Item": item} for item in items]}
237+
238+
239+
# No exhaustive response for execute_transaction;
240+
# The basic_execute_transaction_response is sufficient
241+
242+
243+
def basic_batch_execute_statement_response(items):
244+
"""Get a batch_execute_statement response in resource (ddb) format for any items."""
245+
return {"Responses": [{"Item": item} for item in items]}
246+
247+
248+
def exhaustive_batch_execute_statement_response(items):
249+
"""
250+
Get a batch_execute_statement response in resource (ddb) format for any items.
251+
This is not intended to be a real response that DynamoDB would return,
252+
but the response should contain additional attributes that DynamoDB could return.
253+
This is only intended to exhaustively test the conversion of the request between client and resource formats.
254+
"""
255+
base = basic_batch_execute_statement_response(items)
256+
base["Responses"][0]["Error"] = {
257+
"Item": {
258+
"partition_key": items[0]["partition_key"],
259+
"sort_key": items[0]["sort_key"],
260+
}
261+
}
262+
return base
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
The `test_client_to_resource.py` and `test_resource_to_client.py` files
2+
in this directory verify that DBESDK's boto3 resource/client conversion methods
3+
correctly convert between resource/client shapes for all operations
4+
supported by DBESDK.
5+
6+
The only shapes that require conversion are
7+
* `AttributeValue`s (DDB items or keys)
8+
* Client format example: `{"S": "some string"}`
9+
* Resource format example: `"some string"`
10+
* ConditionExpressions (`KeyConditionExpression` or `FilterExpression`; only resource-to-client)
11+
* Client shape ex.:
12+
* KeyConditionExpression: `"attr : :value"`
13+
* ExpressionAttributeValues: `{":value" : {"S" : "some value}}`
14+
* Resource shape ex.:
15+
* KeyConditionExpression: `Attr("attr").eq("some value")`
16+
* (Resources also support the client-style string expression)
17+
18+
The conversion logic will recursively traverse inpuyt/output shapes to find shapes that require conversion, then convert them.
19+
(ex. for boto3 Table [put_item](https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/dynamodb/table/put_item.html)),
20+
the following kwargs MUST be converted from resource to client format:
21+
* `["Item"]`
22+
* `["Expected"][<table_name>]["Value"]`
23+
* `["Expected"][<table_name>]["AttributeValueList"]`
24+
* `["ConditionExpression"]`
25+
* `["ExpressionAttributeValues"]`
26+
27+
The requests, responses, and items in the parent directory are shared between the integ tests and these unit tests.
28+
The integ tests send the exact request that whose client/resource conversion is tested in the unit tests,
29+
and the integ tests receive the exact response whose conversion is tested in the unit tests.
30+
31+
The integration tests verify that the basic forms of these requests and responses are authoritative. The unit tests verify that DBESDK’s conversion logic exactly transforms one shape format into the other.
32+
33+
Note: The conversion logic is generated by Smithy-Dafny Python
34+
and the shape traversals are derived from the MPL's DynamoDB Smithy model.
35+
As a result, the correctness of this conversion logic is primarily depends on the correctness of the Smithy codegen logic and the correctness of the DynamoDB Smithy model.
36+
37+
Originally, the conversion logic was hand-written,
38+
so these tests go beyond smoke testing to provide extra guarantees,
39+
even though basic smoke testing should suffice now that the logic is machine-generated.

DynamoDbEncryption/runtimes/python/test/unit/internal/test_client_to_resource.py

Lines changed: 48 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,29 @@
5555
exhaustive_scan_request_dict,
5656
)
5757
from ...responses import (
58+
basic_batch_execute_statement_response,
5859
basic_batch_get_item_response,
5960
basic_batch_write_item_put_response,
6061
basic_delete_item_response,
62+
basic_execute_statement_response,
63+
basic_execute_transaction_response,
6164
basic_get_item_response,
6265
basic_put_item_response,
6366
basic_query_response,
6467
basic_scan_response,
6568
basic_transact_get_items_response,
6669
basic_transact_write_items_response,
6770
basic_update_item_response,
71+
exhaustive_batch_execute_statement_response,
6872
exhaustive_batch_get_item_response,
6973
exhaustive_batch_write_item_put_response,
74+
exhaustive_delete_item_response,
75+
exhaustive_execute_statement_response,
7076
exhaustive_get_item_response,
7177
exhaustive_put_item_response,
7278
exhaustive_query_response,
7379
exhaustive_scan_response,
80+
exhaustive_update_item_response,
7481
)
7582

7683
client_to_resource_converter = ClientShapeToResourceShapeConverter()
@@ -560,15 +567,13 @@ def test_GIVEN_test_transact_get_items_response_WHEN_client_to_resource_THEN_ret
560567
def test_update_item_request_ddb():
561568
# Select unsigned attribute without loss of generality;
562569
# resource/client logic doesn't care about signed attributes
563-
# TODO: Add exhaustive request
564570
return basic_update_item_request_ddb_unsigned_attribute
565571

566572

567573
@pytest.fixture
568574
def test_update_item_request_dict():
569575
# Select unsigned attribute without loss of generality;
570576
# resource/client logic doesn't care about signed attributes
571-
# TODO: Add exhaustive request
572577
return basic_update_item_request_dict_unsigned_attribute
573578

574579

@@ -584,8 +589,9 @@ def test_GIVEN_test_update_item_request_WHEN_client_to_resource_THEN_returns_dic
584589

585590

586591
@pytest.fixture
587-
def test_update_item_response():
588-
# TODO: Add exhaustive response
592+
def test_update_item_response(use_exhaustive_request):
593+
if use_exhaustive_request:
594+
return exhaustive_update_item_response
589595
return basic_update_item_response
590596

591597

@@ -616,14 +622,22 @@ def test_GIVEN_test_execute_statement_request_WHEN_client_to_resource_THEN_retur
616622
assert dict_item == test_execute_statement_request(test_dict_item)
617623

618624

619-
def test_GIVEN_test_execute_statement_response_WHEN_client_to_resource_THEN_raises_NotImplementedError():
625+
@pytest.fixture
626+
def test_execute_statement_response(use_exhaustive_request):
627+
if use_exhaustive_request:
628+
return exhaustive_execute_statement_response
629+
return basic_execute_statement_response
630+
631+
632+
def test_GIVEN_test_execute_statement_response_WHEN_client_to_resource_THEN_returns_dict_value(
633+
test_execute_statement_response, test_ddb_item, test_dict_item
634+
):
620635
# Given: Execute statement response
621-
# TODO: this
622-
ddb_response = {}
636+
ddb_response = test_execute_statement_response([test_ddb_item])
623637
# When: Converting to resource format
624638
resource_response = client_to_resource_converter.execute_statement_response(ddb_response)
625639
# Then: Returns dict value
626-
assert resource_response == {}
640+
assert resource_response == test_execute_statement_response([test_dict_item])
627641

628642

629643
@pytest.fixture
@@ -642,18 +656,24 @@ def test_GIVEN_test_execute_transaction_request_WHEN_client_to_resource_THEN_ret
642656
assert dict_item == test_execute_transaction_request(test_dict_item)
643657

644658

645-
def test_GIVEN_test_execute_transaction_response_WHEN_client_to_resource_THEN_returns_dict_value():
659+
@pytest.fixture
660+
def test_execute_transaction_response():
661+
return basic_execute_transaction_response
662+
663+
664+
def test_GIVEN_test_execute_transaction_response_WHEN_client_to_resource_THEN_returns_dict_value(
665+
test_execute_transaction_response, test_ddb_item, test_dict_item
666+
):
646667
# Given: Execute transaction response
647-
# TODO: this
648-
ddb_response = {}
668+
ddb_response = test_execute_transaction_response([test_ddb_item])
649669
# When: Converting to resource format
650670
resource_response = client_to_resource_converter.execute_transaction_response(ddb_response)
651671
# Then: Returns dict value
652-
assert resource_response == {}
672+
assert resource_response == test_execute_transaction_response([test_dict_item])
653673

654674

655675
@pytest.fixture
656-
def test_batch_execute_statement_request():
676+
def test_batch_execute_statement_request(use_exhaustive_request):
657677
return basic_batch_execute_statement_request_encrypted_table
658678

659679

@@ -668,14 +688,22 @@ def test_GIVEN_test_batch_execute_statement_request_WHEN_client_to_resource_THEN
668688
assert dict_item == test_batch_execute_statement_request()
669689

670690

671-
def test_GIVEN_test_batch_execute_statement_response_WHEN_client_to_resource_THEN_raises_NotImplementedError():
691+
@pytest.fixture
692+
def test_batch_execute_statement_response(use_exhaustive_request):
693+
if use_exhaustive_request:
694+
return exhaustive_batch_execute_statement_response
695+
return basic_batch_execute_statement_response
696+
697+
698+
def test_GIVEN_test_batch_execute_statement_response_WHEN_client_to_resource_THEN_returns_dict_value(
699+
test_batch_execute_statement_response, test_ddb_item, test_dict_item
700+
):
672701
# Given: Batch execute statement response
673-
# TODO: this
674-
ddb_response = {}
702+
ddb_response = test_batch_execute_statement_response([test_ddb_item])
675703
# When: Converting to resource format
676704
resource_response = client_to_resource_converter.batch_execute_statement_response(ddb_response)
677705
# Then: Returns dict value
678-
assert resource_response == {}
706+
assert resource_response == test_batch_execute_statement_response([test_dict_item])
679707

680708

681709
@pytest.fixture
@@ -700,7 +728,9 @@ def test_GIVEN_test_delete_item_request_WHEN_client_to_resource_THEN_returns_dic
700728

701729

702730
@pytest.fixture
703-
def test_delete_item_response():
731+
def test_delete_item_response(use_exhaustive_request):
732+
if use_exhaustive_request:
733+
return exhaustive_delete_item_response
704734
return basic_delete_item_response
705735

706736

0 commit comments

Comments
 (0)