1111# ANY KIND, either express or implied. See the License for the specific
1212# language governing permissions and limitations under the License.
1313"""Functional tests for ``dynamodb_encryption_sdk.material_providers.most_recent``."""
14+ from collections import defaultdict
15+
1416import pytest
1517from mock import MagicMock , sentinel
1618
19+ from dynamodb_encryption_sdk .exceptions import NoKnownVersionError
20+ from dynamodb_encryption_sdk .material_providers import CryptographicMaterialsProvider
1721from dynamodb_encryption_sdk .material_providers .most_recent import MostRecentProvider
1822from dynamodb_encryption_sdk .material_providers .store import ProviderStore
1923
2024pytestmark = [pytest .mark .functional , pytest .mark .local ]
2125
2226
27+ class SentinelCryptoMaterialsProvider (CryptographicMaterialsProvider ):
28+ def __init__ (self , name , version ):
29+ self .name = name
30+ self .version = version
31+ self ._material_description = version
32+ self .provider_calls = []
33+
34+ def encryption_materials (self , encryption_context ):
35+ self .provider_calls .append (("encryption_materials" , encryption_context ))
36+ return getattr (sentinel , "{name}_{version}_encryption" .format (name = self .name , version = self .version ))
37+
38+ def decryption_materials (self , encryption_context ):
39+ self .provider_calls .append (("decryption_materials" , encryption_context ))
40+ return getattr (sentinel , "{name}_{version}_decryption" .format (name = self .name , version = self .version ))
41+
42+
43+ class MockProviderStore (ProviderStore ):
44+ def __init__ (self ):
45+ self .provider_calls = []
46+ self ._providers = defaultdict (dict )
47+
48+ def get_or_create_provider (self , material_name , version ):
49+ self .provider_calls .append (("get_or_create_provider" , material_name , version ))
50+ try :
51+ return self ._providers [material_name ][version ]
52+ except KeyError :
53+ self ._providers [material_name ][version ] = SentinelCryptoMaterialsProvider (material_name , version )
54+ return self ._providers [material_name ][version ]
55+
56+ def max_version (self , material_name ):
57+ self .provider_calls .append (("max_version" , material_name ))
58+ try :
59+ return sorted (self ._providers [material_name ].keys ())[- 1 ]
60+ except IndexError :
61+ raise NoKnownVersionError ('No known version for name: "{}"' .format (material_name ))
62+
63+ def version_from_material_description (self , material_description ):
64+ self .provider_calls .append (("version_from_material_description" , material_description ))
65+ return material_description
66+
67+
2368def test_failed_lock_acquisition ():
2469 store = MagicMock (__class__ = ProviderStore )
2570 provider = MostRecentProvider (provider_store = store , material_name = "my material" , version_ttl = 10.0 )
@@ -31,3 +76,57 @@ def test_failed_lock_acquisition():
3176
3277 assert test is sentinel .nine
3378 assert not store .mock_calls
79+
80+
81+ def test_encryption_materials_cache_use ():
82+ store = MockProviderStore ()
83+ name = "material"
84+ provider = MostRecentProvider (provider_store = store , material_name = name , version_ttl = 10.0 )
85+
86+ test1 = provider .encryption_materials (sentinel .encryption_context_1 )
87+ assert test1 is sentinel .material_0_encryption
88+
89+ assert provider ._version == 0
90+ assert len (provider ._cache ._cache ) == 1
91+
92+ expected_calls = [
93+ ("max_version" , name ),
94+ ("get_or_create_provider" , name , 0 ),
95+ ("version_from_material_description" , 0 ),
96+ ]
97+
98+ assert store .provider_calls == expected_calls
99+
100+ test2 = provider .encryption_materials (sentinel .encryption_context_1 )
101+ assert test2 is sentinel .material_0_encryption
102+
103+ assert provider ._version == 0
104+ assert len (provider ._cache ._cache ) == 1
105+
106+ assert store .provider_calls == expected_calls
107+
108+
109+ def test_decryption_materials_cache_use ():
110+ store = MockProviderStore ()
111+ name = "material"
112+ provider = MostRecentProvider (provider_store = store , material_name = name , version_ttl = 10.0 )
113+
114+ context = MagicMock (material_description = 0 )
115+
116+ test1 = provider .decryption_materials (context )
117+ assert test1 is sentinel .material_0_decryption
118+
119+ assert len (provider ._cache ._cache ) == 1
120+
121+ expected_calls = [("version_from_material_description" , 0 ), ("get_or_create_provider" , name , 0 )]
122+
123+ assert store .provider_calls == expected_calls
124+
125+ test2 = provider .decryption_materials (context )
126+ assert test2 is sentinel .material_0_decryption
127+
128+ assert len (provider ._cache ._cache ) == 1
129+
130+ expected_calls .append (("version_from_material_description" , 0 ))
131+
132+ assert store .provider_calls == expected_calls
0 commit comments