Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 23b1857

Browse files
authoredJan 26, 2019
Merge pull request #107 from woodyza/handle-unprocessed-items
Handle unprocessed items in batch write responses
2 parents 7f9582f + 0e0ce65 commit 23b1857

File tree

6 files changed

+383
-7
lines changed

6 files changed

+383
-7
lines changed
 

‎src/dynamodb_encryption_sdk/internal/utils.py

Lines changed: 97 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,17 +16,20 @@
1616
No guarantee is provided on the modules and APIs within this
1717
namespace staying consistent. Directly reference at your own risk.
1818
"""
19+
import copy
20+
from functools import partial
21+
1922
import attr
2023
import botocore.client
2124

2225
from dynamodb_encryption_sdk.encrypted import CryptoConfig
2326
from dynamodb_encryption_sdk.encrypted.item import decrypt_python_item, encrypt_python_item
2427
from dynamodb_encryption_sdk.exceptions import InvalidArgumentError
25-
from dynamodb_encryption_sdk.structures import EncryptionContext, TableInfo
28+
from dynamodb_encryption_sdk.structures import CryptoAction, EncryptionContext, TableInfo
2629
from dynamodb_encryption_sdk.transform import dict_to_ddb
2730

2831
try: # Python 3.5.0 and 3.5.1 have incompatible typing modules
29-
from typing import Any, Callable, Dict, Text # noqa pylint: disable=unused-import
32+
from typing import Any, Bool, Callable, Dict, Text # noqa pylint: disable=unused-import
3033
except ImportError: # pragma: no cover
3134
# We only actually need these imports when running the mypy checks
3235
pass
@@ -271,19 +274,22 @@ def encrypt_batch_write_item(encrypt_method, crypto_config_method, write_method,
271274
"""Transparently encrypt multiple items before putting them in a batch request.
272275
273276
:param callable encrypt_method: Method to use to encrypt items
274-
:param callable crypto_config_method: Method that accepts ``kwargs`` and provides a :class:`CryptoConfig`
277+
:param callable crypto_config_method: Method that accepts a table name string and provides a :class:`CryptoConfig`
275278
:param callable write_method: Method that writes to the table
276279
:param **kwargs: Keyword arguments to pass to ``write_method``
277280
:return: DynamoDB response
278281
:rtype: dict
279282
"""
280283
request_crypto_config = kwargs.pop("crypto_config", None)
284+
table_crypto_configs = {}
285+
plaintext_items = copy.deepcopy(kwargs["RequestItems"])
281286

282287
for table_name, items in kwargs["RequestItems"].items():
283288
if request_crypto_config is not None:
284289
crypto_config = request_crypto_config
285290
else:
286291
crypto_config = crypto_config_method(table_name=table_name)
292+
table_crypto_configs[table_name] = crypto_config
287293

288294
for pos, value in enumerate(items):
289295
for request_type, item in value.items():
@@ -293,4 +299,91 @@ def encrypt_batch_write_item(encrypt_method, crypto_config_method, write_method,
293299
item=item["Item"],
294300
crypto_config=crypto_config.with_item(_item_transformer(encrypt_method)(item["Item"])),
295301
)
296-
return write_method(**kwargs)
302+
303+
response = write_method(**kwargs)
304+
return _process_batch_write_response(plaintext_items, response, table_crypto_configs)
305+
306+
307+
def _process_batch_write_response(request, response, table_crypto_config):
308+
# type: (Dict, Dict, Dict[Text, CryptoConfig]) -> Dict
309+
"""Handle unprocessed items in the response from a transparently encrypted write.
310+
311+
:param dict request: The DynamoDB plaintext request dictionary
312+
:param dict response: The DynamoDB response from the batch operation
313+
:param Dict[Text, CryptoConfig] table_crypto_config: table level CryptoConfig used in encrypting the request items
314+
:return: DynamoDB response, with any unprocessed items reverted back to the original plaintext values
315+
:rtype: dict
316+
"""
317+
try:
318+
unprocessed_items = response["UnprocessedItems"]
319+
except KeyError:
320+
return response
321+
322+
# Unprocessed items need to be returned in their original state
323+
for table_name, unprocessed in unprocessed_items.items():
324+
original_items = request[table_name]
325+
crypto_config = table_crypto_config[table_name]
326+
327+
if crypto_config.encryption_context.partition_key_name:
328+
items_match = partial(_item_keys_match, crypto_config)
329+
else:
330+
items_match = partial(_item_attributes_match, crypto_config)
331+
332+
for pos, operation in enumerate(unprocessed):
333+
for request_type, item in operation.items():
334+
if request_type != "PutRequest":
335+
continue
336+
337+
for plaintext_item in original_items:
338+
if plaintext_item.get(request_type) and items_match(
339+
plaintext_item[request_type]["Item"], item["Item"]
340+
):
341+
unprocessed[pos] = plaintext_item.copy()
342+
break
343+
344+
return response
345+
346+
347+
def _item_keys_match(crypto_config, item1, item2):
348+
# type: (CryptoConfig, Dict, Dict) -> Bool
349+
"""Determines whether the values in the primary and sort keys (if they exist) are the same
350+
351+
:param CryptoConfig crypto_config: CryptoConfig used in encrypting the given items
352+
:param dict item1: The first item to compare
353+
:param dict item2: The second item to compare
354+
:return: Bool response, True if the key attributes match
355+
:rtype: bool
356+
"""
357+
partition_key_name = crypto_config.encryption_context.partition_key_name
358+
sort_key_name = crypto_config.encryption_context.sort_key_name
359+
360+
partition_keys_match = item1[partition_key_name] == item2[partition_key_name]
361+
362+
if sort_key_name is None:
363+
return partition_keys_match
364+
365+
return partition_keys_match and item1[sort_key_name] == item2[sort_key_name]
366+
367+
368+
def _item_attributes_match(crypto_config, plaintext_item, encrypted_item):
369+
# type: (CryptoConfig, Dict, Dict) -> Bool
370+
"""Determines whether the unencrypted values in the plaintext items attributes are the same as those in the
371+
encrypted item. Essentially this uses brute force to cover when we don't know the primary and sort
372+
index attribute names, since they can't be encrypted.
373+
374+
:param CryptoConfig crypto_config: CryptoConfig used in encrypting the given items
375+
:param dict plaintext_item: The plaintext item
376+
:param dict encrypted_item: The encrypted item
377+
:return: Bool response, True if the unencrypted attributes in the plaintext item match those in
378+
the encrypted item
379+
:rtype: bool
380+
"""
381+
382+
for name, value in plaintext_item.items():
383+
if crypto_config.attribute_actions.action(name) == CryptoAction.ENCRYPT_AND_SIGN:
384+
continue
385+
386+
if encrypted_item.get(name) != value:
387+
return False
388+
389+
return True

‎test/functional/encrypted/test_client.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
from ..functional_test_utils import example_table # noqa pylint: disable=unused-import
1818
from ..functional_test_utils import (
1919
TEST_TABLE_NAME,
20+
build_static_jce_cmp,
21+
client_batch_items_unprocessed_check,
2022
client_cycle_batch_items_check,
2123
client_cycle_batch_items_check_paginators,
2224
client_cycle_single_item_check,
@@ -53,6 +55,12 @@ def _client_cycle_batch_items_check_paginators(materials_provider, initial_actio
5355
)
5456

5557

58+
def _client_batch_items_unprocessed_check(materials_provider, initial_actions, initial_item):
59+
client_batch_items_unprocessed_check(
60+
materials_provider, initial_actions, initial_item, TEST_TABLE_NAME, "us-west-2"
61+
)
62+
63+
5664
def test_ephemeral_item_cycle(example_table, some_cmps, parametrized_actions, parametrized_item):
5765
"""Test a small number of curated CMPs against a small number of curated items."""
5866
_client_cycle_single_item_check(some_cmps, parametrized_actions, parametrized_item)
@@ -68,6 +76,12 @@ def test_ephemeral_batch_item_cycle_paginators(example_table, some_cmps, paramet
6876
_client_cycle_batch_items_check_paginators(some_cmps, parametrized_actions, parametrized_item)
6977

7078

79+
def test_batch_item_unprocessed(example_table, parametrized_actions, parametrized_item):
80+
"""Test Unprocessed Items handling with a single ephemeral static CMP against a small number of curated items."""
81+
cmp = build_static_jce_cmp("AES", 256, "HmacSHA256", 256)
82+
_client_batch_items_unprocessed_check(cmp, parametrized_actions, parametrized_item)
83+
84+
7185
@pytest.mark.slow
7286
def test_ephemeral_item_cycle_slow(example_table, all_the_cmps, parametrized_actions, parametrized_item):
7387
"""Test ALL THE CMPS against a small number of curated items."""

‎test/functional/encrypted/test_resource.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@
1616
from ..functional_test_utils import example_table # noqa pylint: disable=unused-import
1717
from ..functional_test_utils import (
1818
TEST_TABLE_NAME,
19+
build_static_jce_cmp,
20+
resource_batch_items_unprocessed_check,
1921
resource_cycle_batch_items_check,
2022
set_parametrized_actions,
2123
set_parametrized_cmp,
@@ -35,11 +37,24 @@ def _resource_cycle_batch_items_check(materials_provider, initial_actions, initi
3537
resource_cycle_batch_items_check(materials_provider, initial_actions, initial_item, TEST_TABLE_NAME, "us-west-2")
3638

3739

40+
def _resource_batch_items_unprocessed_check(materials_provider, initial_actions, initial_item):
41+
resource_batch_items_unprocessed_check(
42+
materials_provider, initial_actions, initial_item, TEST_TABLE_NAME, "us-west-2"
43+
)
44+
45+
3846
def test_ephemeral_batch_item_cycle(example_table, some_cmps, parametrized_actions, parametrized_item):
3947
"""Test a small number of curated CMPs against a small number of curated items."""
4048
_resource_cycle_batch_items_check(some_cmps, parametrized_actions, parametrized_item)
4149

4250

51+
def test_batch_item_unprocessed(example_table, parametrized_actions, parametrized_item):
52+
"""Test Unprocessed Items handling with a single ephemeral static CMP against a small number of curated items."""
53+
_resource_batch_items_unprocessed_check(
54+
build_static_jce_cmp("AES", 256, "HmacSHA256", 256), parametrized_actions, parametrized_item
55+
)
56+
57+
4358
@pytest.mark.travis_isolation
4459
@pytest.mark.slow
4560
def test_ephemeral_batch_item_cycle_slow(example_table, all_the_cmps, parametrized_actions, parametrized_item):

‎test/functional/encrypted/test_table.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
from ..functional_test_utils import example_table # noqa pylint: disable=unused-import
1818
from ..functional_test_utils import (
1919
TEST_TABLE_NAME,
20+
build_static_jce_cmp,
2021
set_parametrized_actions,
2122
set_parametrized_cmp,
2223
set_parametrized_item,
24+
table_batch_writer_unprocessed_items_check,
2325
table_cycle_batch_writer_check,
2426
table_cycle_check,
2527
)
@@ -48,6 +50,14 @@ def test_ephemeral_item_cycle_batch_writer(example_table, some_cmps, parametrize
4850
table_cycle_batch_writer_check(some_cmps, parametrized_actions, parametrized_item, TEST_TABLE_NAME, "us-west-2")
4951

5052

53+
def test_batch_writer_unprocessed(example_table, parametrized_actions, parametrized_item):
54+
"""Test Unprocessed Items handling with a single ephemeral static CMP against a small number of curated items."""
55+
cmp = build_static_jce_cmp("AES", 256, "HmacSHA256", 256)
56+
table_batch_writer_unprocessed_items_check(
57+
cmp, parametrized_actions, parametrized_item, TEST_TABLE_NAME, "us-west-2"
58+
)
59+
60+
5161
@pytest.mark.slow
5262
def test_ephemeral_item_cycle_slow(example_table, all_the_cmps, parametrized_actions, parametrized_item):
5363
"""Test ALL THE CMPS against a small number of curated items."""

‎test/functional/functional_test_utils.py

Lines changed: 95 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import pytest
2525
from boto3.dynamodb.types import Binary
2626
from botocore.exceptions import NoRegionError
27+
from mock import patch
2728
from moto import mock_dynamodb2
2829

2930
from dynamodb_encryption_sdk.delegated_keys.jce import JceNameLocalDelegatedKey
@@ -336,6 +337,10 @@ def diverse_item():
336337
_reserved_attributes = set([attr.value for attr in ReservedAttributes])
337338

338339

340+
def return_requestitems_as_unprocessed(*args, **kwargs):
341+
return {"UnprocessedItems": kwargs["RequestItems"]}
342+
343+
339344
def check_encrypted_item(plaintext_item, ciphertext_item, attribute_actions):
340345
# Verify that all expected attributes are present
341346
ciphertext_attributes = set(ciphertext_item.keys())
@@ -374,12 +379,20 @@ def _nop_transformer(item):
374379
return item
375380

376381

382+
def assert_items_exist_in_list(source, expected, transformer):
383+
for actual_item in source:
384+
expected_item = _matching_key(actual_item, expected)
385+
assert transformer(actual_item) == transformer(expected_item)
386+
387+
377388
def assert_equal_lists_of_items(actual, expected, transformer=_nop_transformer):
378389
assert len(actual) == len(expected)
390+
assert_items_exist_in_list(actual, expected, transformer)
379391

380-
for actual_item in actual:
381-
expected_item = _matching_key(actual_item, expected)
382-
assert transformer(actual_item) == transformer(expected_item)
392+
393+
def assert_list_of_items_contains(full, subset, transformer=_nop_transformer):
394+
assert len(full) >= len(subset)
395+
assert_items_exist_in_list(subset, full, transformer)
383396

384397

385398
def check_many_encrypted_items(actual, expected, attribute_actions, transformer=_nop_transformer):
@@ -479,6 +492,25 @@ def cycle_batch_writer_check(raw_table, encrypted_table, initial_actions, initia
479492
del items
480493

481494

495+
def batch_write_item_unprocessed_check(
496+
encrypted, initial_item, write_transformer=_nop_transformer, table_name=TEST_TABLE_NAME
497+
):
498+
"""Check that unprocessed items in a batch result are unencrypted."""
499+
items = _generate_items(initial_item, write_transformer)
500+
501+
request_items = {table_name: [{"PutRequest": {"Item": _item}} for _item in items]}
502+
_put_result = encrypted.batch_write_item(RequestItems=request_items)
503+
504+
# we expect results to include Unprocessed items, or the test case is invalid!
505+
unprocessed_items = _put_result["UnprocessedItems"]
506+
assert unprocessed_items != {}
507+
508+
unprocessed = [operation["PutRequest"]["Item"] for operation in unprocessed_items[TEST_TABLE_NAME]]
509+
assert_list_of_items_contains(items, unprocessed, transformer=_nop_transformer)
510+
511+
del items
512+
513+
482514
def cycle_item_check(plaintext_item, crypto_config):
483515
"""Check that cycling (plaintext->encrypted->decrypted) an item has the expected results."""
484516
ciphertext_item = encrypt_python_item(plaintext_item, crypto_config)
@@ -527,6 +559,30 @@ def table_cycle_batch_writer_check(materials_provider, initial_actions, initial_
527559
cycle_batch_writer_check(table, e_table, initial_actions, initial_item)
528560

529561

562+
def table_batch_writer_unprocessed_items_check(
563+
materials_provider, initial_actions, initial_item, table_name, region_name=None
564+
):
565+
kwargs = {}
566+
if region_name is not None:
567+
kwargs["region_name"] = region_name
568+
resource = boto3.resource("dynamodb", **kwargs)
569+
table = resource.Table(table_name)
570+
571+
items = _generate_items(initial_item, _nop_transformer)
572+
request_items = {table_name: [{"PutRequest": {"Item": _item}} for _item in items]}
573+
574+
with patch.object(table.meta.client, "batch_write_item") as batch_write_mock:
575+
# Check that unprocessed items returned to a BatchWriter are successfully retried
576+
batch_write_mock.side_effect = [{"UnprocessedItems": request_items}, {"UnprocessedItems": {}}]
577+
e_table = EncryptedTable(table=table, materials_provider=materials_provider, attribute_actions=initial_actions)
578+
579+
with e_table.batch_writer() as writer:
580+
for item in items:
581+
writer.put_item(item)
582+
583+
del items
584+
585+
530586
def resource_cycle_batch_items_check(materials_provider, initial_actions, initial_item, table_name, region_name=None):
531587
kwargs = {}
532588
if region_name is not None:
@@ -550,6 +606,24 @@ def resource_cycle_batch_items_check(materials_provider, initial_actions, initia
550606
assert not e_scan_result["Items"]
551607

552608

609+
def resource_batch_items_unprocessed_check(
610+
materials_provider, initial_actions, initial_item, table_name, region_name=None
611+
):
612+
kwargs = {}
613+
if region_name is not None:
614+
kwargs["region_name"] = region_name
615+
resource = boto3.resource("dynamodb", **kwargs)
616+
617+
with patch.object(resource, "batch_write_item", return_requestitems_as_unprocessed):
618+
e_resource = EncryptedResource(
619+
resource=resource, materials_provider=materials_provider, attribute_actions=initial_actions
620+
)
621+
622+
batch_write_item_unprocessed_check(
623+
encrypted=e_resource, initial_item=initial_item, write_transformer=dict_to_ddb, table_name=table_name
624+
)
625+
626+
553627
def client_cycle_single_item_check(materials_provider, initial_actions, initial_item, table_name, region_name=None):
554628
check_attribute_actions = initial_actions.copy()
555629
check_attribute_actions.set_index_keys(*list(TEST_KEY.keys()))
@@ -600,6 +674,24 @@ def client_cycle_batch_items_check(materials_provider, initial_actions, initial_
600674
assert not e_scan_result["Items"]
601675

602676

677+
def client_batch_items_unprocessed_check(
678+
materials_provider, initial_actions, initial_item, table_name, region_name=None
679+
):
680+
kwargs = {}
681+
if region_name is not None:
682+
kwargs["region_name"] = region_name
683+
client = boto3.client("dynamodb", **kwargs)
684+
685+
with patch.object(client, "batch_write_item", return_requestitems_as_unprocessed):
686+
e_client = EncryptedClient(
687+
client=client, materials_provider=materials_provider, attribute_actions=initial_actions
688+
)
689+
690+
batch_write_item_unprocessed_check(
691+
encrypted=e_client, initial_item=initial_item, write_transformer=dict_to_ddb, table_name=table_name
692+
)
693+
694+
603695
def client_cycle_batch_items_check_paginators(
604696
materials_provider, initial_actions, initial_item, table_name, region_name=None
605697
):
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
# -*- coding: utf-8 -*-
2+
# Copyright 2018 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License"). You
5+
# may not use this file except in compliance with the License. A copy of
6+
# the License is located at
7+
#
8+
# http://aws.amazon.com/apache2.0/
9+
#
10+
# or in the "license" file accompanying this file. This file is
11+
# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF
12+
# ANY KIND, either express or implied. See the License for the specific
13+
# language governing permissions and limitations under the License.
14+
"""Test suite for ``dynamodb_encryption_sdk.internal.utils``."""
15+
import copy
16+
17+
import pytest
18+
from mock import Mock
19+
20+
from dynamodb_encryption_sdk.encrypted import CryptoConfig
21+
from dynamodb_encryption_sdk.identifiers import CryptoAction
22+
from dynamodb_encryption_sdk.internal.utils import encrypt_batch_write_item
23+
from dynamodb_encryption_sdk.material_providers import CryptographicMaterialsProvider
24+
from dynamodb_encryption_sdk.structures import AttributeActions, EncryptionContext
25+
from dynamodb_encryption_sdk.transform import dict_to_ddb
26+
27+
from ..functional_test_utils import diverse_item
28+
29+
30+
def get_test_item(standard_dict_format, partition_key, sort_key=None):
31+
attributes = diverse_item()
32+
33+
attributes["partition-key"] = partition_key
34+
if sort_key is not None:
35+
attributes["sort-key"] = sort_key
36+
37+
if not standard_dict_format:
38+
attributes = dict_to_ddb(attributes)
39+
return attributes
40+
41+
42+
def get_test_items(standard_dict_format, table_name="table", with_sort_keys=False):
43+
44+
if with_sort_keys:
45+
items = [
46+
get_test_item(standard_dict_format, partition_key="key-1", sort_key="sort-1"),
47+
get_test_item(standard_dict_format, partition_key="key-2", sort_key="sort-1"),
48+
get_test_item(standard_dict_format, partition_key="key-2", sort_key="sort-2"),
49+
]
50+
else:
51+
items = [
52+
get_test_item(standard_dict_format, partition_key="key-1"),
53+
get_test_item(standard_dict_format, partition_key="key-2"),
54+
]
55+
56+
for pos, item in enumerate(items):
57+
item["encrypt-me"] = table_name + str(pos)
58+
59+
return {table_name: [{"PutRequest": {"Item": item}} for item in items]}
60+
61+
62+
def get_dummy_crypto_config(partition_key_name=None, sort_key_name=None, sign_keys=False):
63+
context = EncryptionContext(partition_key_name=partition_key_name, sort_key_name=sort_key_name)
64+
actions = AttributeActions(
65+
default_action=CryptoAction.DO_NOTHING, attribute_actions={"encrypt-me": CryptoAction.ENCRYPT_AND_SIGN}
66+
)
67+
if sign_keys:
68+
actions.attribute_actions["partition-key"] = CryptoAction.SIGN_ONLY
69+
actions.attribute_actions["sort-key"] = CryptoAction.SIGN_ONLY
70+
71+
materials = Mock(spec=CryptographicMaterialsProvider) # type: CryptographicMaterialsProvider
72+
return CryptoConfig(materials_provider=materials, encryption_context=context, attribute_actions=actions)
73+
74+
75+
def check_encrypt_batch_write_item_call(request_items, crypto_config):
76+
def dummy_encrypt(item, **kwargs):
77+
result = item.copy()
78+
result["encrypt-me"] = "pretend Im encrypted"
79+
return result
80+
81+
# execute a batch write, but make the write method return ALL the provided items as unprocessed
82+
result = encrypt_batch_write_item(
83+
encrypt_method=dummy_encrypt,
84+
write_method=lambda **kwargs: {"UnprocessedItems": kwargs["RequestItems"]},
85+
crypto_config_method=lambda **kwargs: crypto_config,
86+
RequestItems=copy.deepcopy(request_items),
87+
)
88+
89+
# assert the returned items equal the submitted items
90+
unprocessed = result["UnprocessedItems"]
91+
92+
assert unprocessed == request_items
93+
94+
95+
@pytest.mark.parametrize(
96+
"items", (get_test_items(standard_dict_format=True), get_test_items(standard_dict_format=False))
97+
)
98+
def test_encrypt_batch_write_returns_plaintext_unprocessed_items_with_known_partition_key(items):
99+
crypto_config = get_dummy_crypto_config("partition-key")
100+
check_encrypt_batch_write_item_call(items, crypto_config)
101+
102+
103+
@pytest.mark.parametrize(
104+
"items",
105+
(
106+
get_test_items(standard_dict_format=True, with_sort_keys=True),
107+
get_test_items(standard_dict_format=False, with_sort_keys=True),
108+
),
109+
)
110+
def test_encrypt_batch_write_returns_plaintext_unprocessed_items_with_known_partition_and_sort_keys(items):
111+
crypto_config = get_dummy_crypto_config("partition-key", "sort-key")
112+
check_encrypt_batch_write_item_call(items, crypto_config)
113+
114+
115+
@pytest.mark.parametrize(
116+
"items",
117+
(
118+
get_test_items(standard_dict_format=True),
119+
get_test_items(standard_dict_format=False),
120+
get_test_items(standard_dict_format=True, with_sort_keys=True),
121+
get_test_items(standard_dict_format=False, with_sort_keys=True),
122+
),
123+
)
124+
def test_encrypt_batch_write_returns_plaintext_unprocessed_items_with_unknown_keys(items):
125+
crypto_config = get_dummy_crypto_config(None, None)
126+
127+
check_encrypt_batch_write_item_call(items, crypto_config)
128+
129+
130+
@pytest.mark.parametrize(
131+
"items",
132+
(
133+
get_test_items(standard_dict_format=True),
134+
get_test_items(standard_dict_format=False),
135+
get_test_items(standard_dict_format=True, with_sort_keys=True),
136+
get_test_items(standard_dict_format=False, with_sort_keys=True),
137+
),
138+
)
139+
def test_encrypt_batch_write_returns_plaintext_unprocessed_items_with_unknown_signed_keys(items):
140+
crypto_config = get_dummy_crypto_config(None, None, sign_keys=True)
141+
142+
check_encrypt_batch_write_item_call(items, crypto_config)
143+
144+
145+
def test_encrypt_batch_write_returns_plaintext_unprocessed_items_over_multiple_tables():
146+
crypto_config = get_dummy_crypto_config("partition-key", "sort-key")
147+
148+
items = get_test_items(standard_dict_format=True, table_name="table-one", with_sort_keys=True)
149+
more_items = get_test_items(standard_dict_format=False, table_name="table-two", with_sort_keys=True)
150+
items.update(more_items)
151+
152+
check_encrypt_batch_write_item_call(items, crypto_config)

0 commit comments

Comments
 (0)
Please sign in to comment.