From a8d4feb06c7263ae97e91a40ecb9331722ab0097 Mon Sep 17 00:00:00 2001 From: Julien Letessier Date: Wed, 14 Jan 2026 21:50:45 +0100 Subject: [PATCH] Add pubsub tests for Redis parity --- test/pubsub.py | 59 ++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 48 insertions(+), 11 deletions(-) diff --git a/test/pubsub.py b/test/pubsub.py index a5c5bfb..3af3f62 100755 --- a/test/pubsub.py +++ b/test/pubsub.py @@ -16,28 +16,28 @@ def test(): ps = c1.pubsub() stats = [ [ps, 'subscribe', ['ch']], - [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch', 'data': 1L}], + [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch', 'data': 1}], [c2, 'publish', ['ch', 'hello'], 1], [ps, 'get_message', [], {'pattern': None, 'type': 'message', 'channel': 'ch', 'data': 'hello'}], [ps, 'subscribe', ['ch1', 'ch2']], - [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch1', 'data': 2L}], - [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch2', 'data': 3L}], + [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch1', 'data': 2}], + [ps, 'get_message', [], {'pattern': None, 'type': 'subscribe', 'channel': 'ch2', 'data': 3}], [c2, 'publish', ['ch1', 'channel1'], lambda x:True], [c2, 'publish', ['ch2', 'channel2'], lambda x:True], [ps, 'get_message', [], {'pattern': None, 'type': 'message', 'channel': 'ch1', 'data': 'channel1'}], [ps, 'get_message', [], {'pattern': None, 'type': 'message', 'channel': 'ch2', 'data': 'channel2'}], [ps, 'psubscribe', ['ch*']], - [ps, 'get_message', [], {'pattern': None, 'type': 'psubscribe', 'channel': 'ch*', 'data': 4L}], + [ps, 'get_message', [], {'pattern': None, 'type': 'psubscribe', 'channel': 'ch*', 'data': 4}], [c2, 'publish', ['ch', 'hello'], 2], - [ps, 'get_message', [], lambda x:type(x)==type({}) and x['data']=='hello'], - [ps, 'get_message', [], lambda x:type(x)==type({}) and x['data']=='hello'], + [ps, 'get_message', [], lambda x:type(x)==type({}) and (x.get('data') == 'hello' or (isinstance(x.get('data'), bytes) and x.get('data').decode('utf-8') == 'hello'))], + [ps, 'get_message', [], lambda x:type(x)==type({}) and (x.get('data') == 'hello' or (isinstance(x.get('data'), bytes) and x.get('data').decode('utf-8') == 'hello'))], [ps, 'psubscribe', ['ch1*', 'ch2*']], [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='psubscribe'], [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='psubscribe'], [ps, 'unsubscribe', ['ch']], [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='unsubscribe'], [c2, 'publish', ['ch', 'hello'], 1], - [ps, 'get_message', [], lambda x:type(x)==type({}) and x['data']=='hello'], + [ps, 'get_message', [], lambda x:type(x)==type({}) and (x.get('data') == 'hello' or (isinstance(x.get('data'), bytes) and x.get('data').decode('utf-8') == 'hello'))], [ps, 'punsubscribe', ['ch*']], [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='punsubscribe'], [ps, 'unsubscribe', ['ch1', 'ch2']], @@ -47,21 +47,58 @@ def test(): [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='punsubscribe'], [ps, 'get_message', [], lambda x:type(x)==type({}) and x['type']=='punsubscribe'], ] + def normalize_value(v): + """Convert byte strings to strings for comparison.""" + if isinstance(v, bytes): + return v.decode('utf-8') + elif isinstance(v, dict): + return {normalize_value(k): normalize_value(val) for k, val in v.items()} + elif isinstance(v, (list, tuple)): + return [normalize_value(item) for item in v] + return v + + def compare_values(actual, expected): + """Compare actual and expected values, handling byte strings.""" + if hasattr(expected, '__call__'): + return expected(actual) + + # Normalize actual value + actual_norm = normalize_value(actual) + expected_norm = normalize_value(expected) + + # Direct comparison + if actual_norm == expected_norm: + return True + + # Handle dict comparison + if isinstance(actual_norm, dict) and isinstance(expected_norm, dict): + if set(actual_norm.keys()) != set(expected_norm.keys()): + return False + for key in actual_norm.keys(): + if not compare_values(actual_norm[key], expected_norm.get(key)): + return False + return True + + return False + def run(stat): func = getattr(stat[0], stat[1]) r = func(*stat[2]) if len(stat) == 3: print('EXEC %s(*%s)' % (stat[1], repr(stat[2]))) + print(' =>', r) return True if hasattr(stat[3], '__call__'): isPass = stat[3](r) else: - isPass = r == stat[3] + isPass = compare_values(r, stat[3]) if isPass: print('PASS %s(*%s):%s' % (stat[1], repr(stat[2]), repr(r))) + print(' =>', r) return True else: print('FAIL %s(*%s):%s != %s' % (stat[1], repr(stat[2]), repr(r), repr(stat[3]))) + print(' =>', r) return False succ = True @@ -69,11 +106,11 @@ def test(): if not run(stat): succ = False time.sleep(0.2) - print '---------------------------------' + print('---------------------------------') if succ: - print 'Good! PubSub test pass' + print('Good! PubSub test pass') else: - print 'Oh! PubSub some case fail' + print('Oh! PubSub some case fail')