async def test_declare_after_main(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'cache_size', {'outcome': 'created'}) async with AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b', 'c']) as client: setting = Setting('cache_size', int, ['b', 'c'], 50) assert not client._undeclared.empty() await client._undeclared.join() assert setting.get(b='', c='') == 50 with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'cache_size': { 'rules': [{ 'context_features': [], 'value': 100, 'rule_id': 1 }], 'default_value': 100 } } })): await client.reload() assert setting.get(b='', c='') == 100
async def test_regular_update(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'cache_size', {'outcome': 'created'}) setting = Setting('cache_size', int, ['b', 'c'], 50) async with AsyncHeksherClient(fake_heksher_service.local_url(), 0.02, ['a', 'b', 'c']): assert setting.get(b='', c='') == 50 with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'cache_size': { 'rules': [{ 'context_features': [], 'value': 100, 'rule_id': 1 }], 'default_value': 100 } } })): await sleep(1) assert setting.get(b='', c='') == 100
async def test_flags_setting_validator(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'c', {'outcome': 'created'}) class Color(IntFlag): blue = auto() red = auto() green = auto() c = Setting('c', Color, 'abc', default_value=Color(0)) @c.add_validator def add_red(value, *_): return value | Color.red with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'c': { 'rules': [{ 'context_features': [['a', 'x']], 'value': ['green', 'blue'], 'rule_id': 1 }], 'default_value': [] } } })): async with AsyncHeksherClient(fake_heksher_service.local_url(), 1000, ['a', 'b', 'c']): assert c.get(a='x', b='', c='') == Color.green | Color.blue | Color.red assert c.get(a='y', b='', c='') == Color.red
def test_nested_update(): a = Setting('a', int, 'abcx', default_value=-1) with SyncStubHeksherClient() as client: client.set_defaults(a='', b='', c='', x='') assert a.get() == -1 with client.patch(a, 0): assert a.get() == 0 with client.patch(a, 1): assert a.get() == 1 assert a.get() == 0 assert a.get() == -1
def test_multi_update(): a = Setting('a', int, 'abcx', default_value=-1) with SyncStubHeksherClient() as client: client.set_defaults(a='', b='', c='') client.patch(a, 10) assert a.get(x='') == 10 with client.patch(a, [ Rule({'x': '0'}, 0), Rule({'x': '1'}, 1) ]): assert a.get(x='0') == 0 assert a.get(x='1') == 1 assert a.get(x='15') == -1 assert a.get(x='0') == 10
async def test_switch_main_different_contexts(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf1', {'outcome': 'created'}) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf2', {'outcome': 'created'}) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf3', {'outcome': 'created'}) setting1 = Setting('conf1', int, ['a'], 74) client1 = AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b']) client1.track_contexts(a=['a', 'b'], b=TRACK_ALL) await client1.set_as_main() setting2 = Setting('conf2', int, ['b'], 26) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'conf1': { 'rules': [{ 'context_features': [], 'value': 5, 'rule_id': 1 }], 'default_value': 74 }, 'conf2': { 'rules': [{ 'context_features': [], 'value': 4, 'rule_id': 2 }], 'default_value': 26 } } })): await client1.reload() assert len(client1._tracked_settings) == 2 assert setting1.get(a='') == 5 assert setting2.get(b='') == 4 client2 = AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['b', 'c']) client2.track_contexts(a=['a', 'b'], b=TRACK_ALL) await client1.aclose() with raises(RuntimeError): # not allowed await client2.set_as_main() await client2.aclose()
async def test_trackcontexts(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c', 'd']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'cache_size', {'outcome': 'created'}) setting = Setting('cache_size', int, ['b', 'c'], 50) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'cache_size': { 'rules': [{ 'context_features': [['b', 'B']], 'value': 100, 'rule_id': 1 }], 'default_value': 100 } } })), fake_heksher_service.query_rules.capture_calls( ) as query_calls: client = AsyncHeksherClient(fake_heksher_service.local_url(), 100000, ['a', 'b', 'c', 'd']) client.track_contexts(b='B', a=['a0', 'a1'], d=TRACK_ALL) async with client: assert setting.get(b='B', c='') == 100 query_calls.assert_requested_once_with( query_params={ 'settings': ['cache_size'], 'context_filters': ['a:(a0,a1),b:(B),d:*'], 'include_metadata': ['false'] })
def test_resolve(): a = Setting('a', int, 'abcx', default_value=-1) with SyncStubHeksherClient() as client, \ client.patch(a, [ Rule({'x': '0'}, 0), Rule({'x': '1'}, 1) ]): client.set_defaults(a='', b='', c='') assert a.get(x='0') == 0 assert a.get(x='1') == 1 assert a.get(x='15') == -1 with raises(RuntimeError): a.get() client.set_defaults(x='0') assert a.get(x='1') == 1
async def test_flags_setting_reject_default(fake_heksher_service, monkeypatch, caplog): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'c', {'outcome': 'created'}) class Color(IntFlag): blue = auto() red = auto() green = auto() c = Setting('c', Color, 'abc', default_value=Color(0)) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'c': { 'rules': [{ 'context_features': [['a', 'f']], 'value': ['green', 'blue'], 'rule_id': 1 }], 'default_value': [['blank']] } } })): caplog.clear() with assert_logs(caplog, WARNING): async with AsyncHeksherClient(fake_heksher_service.local_url(), 1000, ['a', 'b', 'c']): assert c.get(a='re', b='', c='') == Color(0)
async def test_redundant_defaults(fake_heksher_service, caplog, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'cache_size', {'outcome': 'created'}) setting = Setting('cache_size', int, ['b', 'c'], 50) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'cache_size': { 'rules': [{ 'context_features': [['b', 'B']], 'value': 100, 'rule_id': 1 }], 'default_value': 100 } } })): with assert_logs(caplog, WARNING): async with AsyncHeksherClient(fake_heksher_service.local_url(), 1000, ['a', 'b', 'c']) as client: client.set_defaults(b='B', d='im redundant') assert setting.get(c='') == 100
def test_heksher_unreachable(caplog): setting = Setting('cache_size', int, ['b', 'c'], 50) with assert_logs(caplog, ERROR): with ThreadHeksherClient('http://notreal.fake.notreal', 10000, ['a', 'b', 'c']): pass assert setting.get(b='', c='') == 50
async def test_heksher_unreachable(caplog): setting = Setting('cache_size', int, ['b', 'c'], 50) caplog.clear() with assert_logs(caplog, ERROR), raises(HTTPError): async with AsyncHeksherClient('http://notreal.fake.notreal', 10000000, ['a', 'b', 'c']): pass assert setting.get(b='', c='') == 50
def test_multi_switch(caplog): a = Setting('a', int, 'abcx', default_value=-1) c1 = SyncStubHeksherClient() c1.set_defaults(a='', b='', c='') c1.set_as_main() c1.patch(a, 10) assert a.get(x='') == 10 heksher.main_client.Main = TemporaryClient() c2 = SyncStubHeksherClient() c2.set_defaults(a='', b='', c='') c2.set_as_main() with assert_logs(caplog, WARNING): c2.patch(a, [ Rule({'x': '0'}, 0), Rule({'x': '1'}, 1) ]) assert a.get(x='0') == 0 assert a.get(x='1') == 1 assert a.get(x='15') == -1
async def test_async_stub(): a = Setting('a', int, ['user', 'theme'], default_value=0) async with AsyncStubHeksherClient() as client: b = Setting('b', Mapping[str, int], ['user', 'theme'], default_value=0) with client.patch(a, 10), client.patch(b, { 't': 1, 'z': 2 }): c = Setting('c', int, ['user', 'theme'], default_value=0) client.patch(c, [ Rule({}, 0), Rule({'theme': 'dark'}, 1), Rule({'user': '******', 'theme': None}, 2) ]) assert a.get(user='', theme='') == 10 assert b.get(user='', theme='') == { 't': 1, 'z': 2 } assert c.get(user='', theme='') == 0 assert c.get(user='', theme='dark') == 1 assert c.get(user='******', theme='') == 2 assert c.get(user='******', theme='dark') == 1
def test_sync_stub_patcher(monkeypatch): a = Setting('a', int, ['user', 'theme'], default_value=0) with SyncStubHeksherClient() as client: b = Setting('b', Mapping[str, int], ['user', 'theme'], default_value=0) monkeypatch.setattr(client[a], 'rules', 10) monkeypatch.setattr(client[b], 'rules', { 't': 1, 'z': 2 }) c = Setting('c', int, ['user', 'theme'], default_value=0) monkeypatch.setattr(client[c], 'rules', [ Rule({}, 0), Rule({'theme': 'dark'}, 1), Rule({'user': '******'}, 2) ]) assert a.get(user='', theme='') == 10 assert b.get(user='', theme='') == { 't': 1, 'z': 2 } assert c.get(user='', theme='') == 0 assert c.get(user='', theme='dark') == 1 assert c.get(user='******', theme='') == 2 assert c.get(user='******', theme='dark') == 1
async def test_switch_main_from_temp(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf1', {'outcome': 'created'}) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf2', {'outcome': 'created'}) setting1 = Setting('conf1', int, ['a'], 74) client = AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b']) client.track_contexts(a=['a', 'b'], b=TRACK_ALL) await client.set_as_main() setting2 = Setting('conf2', int, ['b'], 26) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'conf1': { 'rules': [{ 'context_features': [], 'value': 5, 'rule_id': 1 }], 'default_value': 74 }, 'conf2': { 'rules': [{ 'context_features': [], 'value': 4, 'rule_id': 2 }], 'default_value': 26 } } })): await client.reload() assert setting1.get(a='') == 5 assert setting2.get(b='') == 4 await client.aclose()
def test_setting_callback_stub(): # stubs don't trigger conversion a = Setting('a', int, 'abcx', default_value=-1) def setting_callback_1(value: int, rule, setting: Setting) -> int: if setting.name == 'a' and value == 10: return 12 return value def setting_callback_2(value: int, rule, setting: Setting) -> int: if setting.name == 'a' and value == 12: return 7 return value a.add_validator(setting_callback_1) a.add_validator(setting_callback_2) c1 = SyncStubHeksherClient() c1.set_as_main() c1.set_defaults(a='', b='', c='', x='') c1.patch(a, 10) assert a.get() == 10
async def test_upgraded_declaration_different_default(fake_heksher_service, monkeypatch, caplog): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem( fake_heksher_service.declare_responses, 'cache_size', { 'outcome': 'upgraded', 'latest_version': '0.5', 'differences': [{ 'level': 'minor', 'attribute': 'default_value', 'latest_value': 100 }] }) setting = Setting('cache_size', int, ['b', 'c'], 50) async with AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b', 'c']): assert setting.get(b='', c='') == 50
def test_setting_rules_collection_callback(): a = Setting('a', int, 'abcx', default_value=-1) def setting_callback_1(value: int, rule, setting: Setting) -> int: if setting.name == 'a' and value == 0: return 3 return value def setting_callback_2(value: int, rule, setting: Setting) -> int: if setting.name == 'a' and value == 3: return 7 return value a.add_validator(setting_callback_1) a.add_validator(setting_callback_2) c1 = SyncStubHeksherClient() c1.set_as_main() c1.set_defaults(a='', b='', c='', x='') c1.patch(a, [ Rule({'x': '0'}, 0) ]) assert a.get(x='0') == 0 assert a.get(x='1') == -1
def test_declare_before_main(fake_heksher_service, monkeypatch): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b', 'c']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'cache_size', {'outcome': 'created'}) setting = Setting('cache_size', int, ['b', 'c'], 50) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'cache_size': { 'rules': [{ 'context_features': [], 'value': 100, 'rule_id': 1 }], 'default_value': 100 } } })): with ThreadHeksherClient(fake_heksher_service.local_url(), 100000, ['a', 'b', 'c']): assert setting.get(b='', c='') == 100
def test_get_from_temp(caplog): a = Setting('a', int, 'abc', default_value=0) with assert_logs(caplog, INFO): assert a.get() == 0
async def test_switch_main_different_tracking(fake_heksher_service, monkeypatch, caplog): monkeypatch.setattr(fake_heksher_service, 'context_features', ['a', 'b']) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf1', {'outcome': 'created'}) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf2', {'outcome': 'created'}) monkeypatch.setitem(fake_heksher_service.declare_responses, 'conf3', {'outcome': 'created'}) setting1 = Setting('conf1', int, ['a'], 74) client1 = AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b']) client1.track_contexts(a=['a', 'b'], b=TRACK_ALL) await client1.set_as_main() setting2 = Setting('conf2', int, ['b'], 26) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'conf1': { 'rules': [{ 'context_features': [], 'value': 5, 'rule_id': 1 }], 'default_value': 74 }, 'conf2': { 'rules': [{ 'context_features': [], 'value': 4, 'rule_id': 2 }], 'default_value': 26 } } })): await client1.reload() assert len(client1._tracked_settings) == 2 assert setting1.get(a='') == 5 assert setting2.get(b='') == 4 client2 = AsyncHeksherClient(fake_heksher_service.local_url(), 10000000, ['a', 'b']) client2.track_contexts(a=['a', 'b', 'c'], b="shoobidoobi") await client1.aclose() with assert_logs( caplog, WARNING ): # it should warn you you're doing bad things, and that your tracking differs await client2.set_as_main() setting3 = Setting('conf3', int, ['b'], 59) with fake_heksher_service.query_rules.patch( JSONResponse({ 'settings': { 'conf1': { 'rules': [{ 'context_features': [], 'value': 5, 'rule_id': 1 }], 'default_value': 74, }, 'conf2': { 'rules': [{ 'context_features': [], 'value': 4, 'rule_id': 2 }], 'default_value': 26, }, 'conf3': { 'rules': [{ 'context_features': [], 'value': 3, 'rule_id': 3 }], 'default_value': 59, } } })): await client2.reload() assert setting1.get(a='') == 5 assert setting2.get(b='') == 4 assert setting3.get(b='') == 3 await client2.aclose()
def test_useless_vals(): a = Setting('a', int, 'abcx', default_value=-1) with raises(ValueError): a.get(a='', b='', c='', x='', d='')