@@ -15,6 +15,10 @@ class InMemoryTester(object):
1515 def init_store (self ):
1616 return InMemoryFeatureStore ()
1717
18+ @property
19+ def supports_prefix (self ):
20+ return False
21+
1822
1923class RedisTester (object ):
2024 redis_host = 'localhost'
@@ -23,19 +27,27 @@ class RedisTester(object):
2327 def __init__ (self , cache_config ):
2428 self ._cache_config = cache_config
2529
26- def init_store (self ):
30+ def init_store (self , prefix = None ):
2731 self ._clear_data ()
28- return Redis .new_feature_store (caching = self ._cache_config )
32+ return Redis .new_feature_store (caching = self ._cache_config , prefix = prefix )
33+
34+ @property
35+ def supports_prefix (self ):
36+ return True
2937
3038 def _clear_data (self ):
3139 r = redis .StrictRedis (host = self .redis_host , port = self .redis_port , db = 0 )
32- r .delete ( "launchdarkly:features" )
40+ r .flushdb ( )
3341
3442
3543class RedisWithDeprecatedConstructorTester (RedisTester ):
36- def init_store (self ):
44+ def init_store (self , prefix = None ):
3745 self ._clear_data ()
38- return RedisFeatureStore (expiration = (30 if self ._cache_config .enabled else 0 ))
46+ return RedisFeatureStore (expiration = (30 if self ._cache_config .enabled else 0 ), prefix = prefix )
47+
48+ @property
49+ def supports_prefix (self ):
50+ return True
3951
4052
4153class DynamoDBTester (object ):
@@ -51,10 +63,14 @@ class DynamoDBTester(object):
5163 def __init__ (self , cache_config ):
5264 self ._cache_config = cache_config
5365
54- def init_store (self ):
66+ def init_store (self , prefix = None ):
5567 self ._create_table ()
5668 self ._clear_data ()
57- return DynamoDB .new_feature_store (self .table_name , dynamodb_opts = self .options )
69+ return DynamoDB .new_feature_store (self .table_name , prefix = prefix , dynamodb_opts = self .options )
70+
71+ @property
72+ def supports_prefix (self ):
73+ return True
5874
5975 def _create_table (self ):
6076 if self .table_created :
@@ -131,6 +147,10 @@ class TestFeatureStore:
131147 DynamoDBTester (CacheConfig .disabled ())
132148 ]
133149
150+ @pytest .fixture (params = params )
151+ def tester (self , request ):
152+ return request .param
153+
134154 @pytest .fixture (params = params )
135155 def store (self , request ):
136156 return request .param .init_store ()
@@ -230,6 +250,39 @@ def test_upsert_older_version_after_delete(self, store):
230250 store .upsert (FEATURES , old_ver )
231251 assert store .get (FEATURES , 'foo' , lambda x : x ) is None
232252
253+ def test_stores_with_different_prefixes_are_independent (self , tester ):
254+ # This verifies that init(), get(), all(), and upsert() are all correctly using the specified key prefix.
255+ # The delete() method isn't tested separately because it's implemented as a variant of upsert().
256+ if not tester .supports_prefix :
257+ return
258+
259+ flag_a1 = { 'key' : 'flagA1' , 'version' : 1 }
260+ flag_a2 = { 'key' : 'flagA2' , 'version' : 1 }
261+ flag_b1 = { 'key' : 'flagB1' , 'version' : 1 }
262+ flag_b2 = { 'key' : 'flagB2' , 'version' : 1 }
263+ store_a = tester .init_store ('a' )
264+ store_b = tester .init_store ('b' )
265+
266+ store_a .init ({ FEATURES : { 'flagA1' : flag_a1 } })
267+ store_a .upsert (FEATURES , flag_a2 )
268+
269+ store_b .init ({ FEATURES : { 'flagB1' : flag_b1 } })
270+ store_b .upsert (FEATURES , flag_b2 )
271+
272+ item = store_a .get (FEATURES , 'flagA1' , lambda x : x )
273+ assert item == flag_a1
274+ item = store_a .get (FEATURES , 'flagB1' , lambda x : x )
275+ assert item is None
276+ items = store_a .all (FEATURES , lambda x : x )
277+ assert items == { 'flagA1' : flag_a1 , 'flagA2' : flag_a2 }
278+
279+ item = store_b .get (FEATURES , 'flagB1' , lambda x : x )
280+ assert item == flag_b1
281+ item = store_b .get (FEATURES , 'flagA1' , lambda x : x )
282+ assert item is None
283+ items = store_b .all (FEATURES , lambda x : x )
284+ assert items == { 'flagB1' : flag_b1 , 'flagB2' : flag_b2 }
285+
233286
234287class TestRedisFeatureStoreExtraTests :
235288 def test_upsert_race_condition_against_external_client_with_higher_version (self ):
0 commit comments