summaryrefslogtreecommitdiff
path: root/tests/test_pubsub.py
blob: 52825afb59141e0261e902f972af21960544b6c7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
from __future__ import with_statement
import pytest
import time

import redis
from redis.exceptions import ConnectionError


def wait_for_message(pubsub, timeout=0.1, ignore_subscribe_messages=False):
    now = time.time()
    timeout = now + timeout
    while now < timeout:
        message = pubsub.get_message(
            ignore_subscribe_messages=ignore_subscribe_messages)
        if message is not None:
            return message
        time.sleep(0.01)
        now = time.time()
    return None


def make_message(type, channel, data, pattern=None):
    return {
        'type': type,
        'pattern': pattern,
        'channel': channel,
        'data': data
    }


class TestPubSubSubscribeUnsubscribe(object):

    def test_subscribe_unsubscribe(self, r):
        p = r.pubsub()

        assert p.subscribe('foo', 'bar') is None

        # should be 2 messages indicating that we've subscribed
        assert wait_for_message(p) == make_message('subscribe', 'foo', 1)
        assert wait_for_message(p) == make_message('subscribe', 'bar', 2)

        assert p.unsubscribe('foo', 'bar') is None

        # should be 2 messages indicating that we've unsubscribed
        assert wait_for_message(p) == make_message('unsubscribe', 'foo', 1)
        assert wait_for_message(p) == make_message('unsubscribe', 'bar', 0)

    def test_pattern_subscribe_unsubscribe(self, r):
        p = r.pubsub()

        assert p.psubscribe('f*', 'b*') is None

        # should be 2 messages indicating that we've subscribed
        assert wait_for_message(p) == make_message('psubscribe', 'f*', 1)
        assert wait_for_message(p) == make_message('psubscribe', 'b*', 2)

        assert p.punsubscribe('f*', 'b*') is None

        # should be 2 messages indicating that we've unsubscribed
        assert wait_for_message(p) == make_message('punsubscribe', 'f*', 1)
        assert wait_for_message(p) == make_message('punsubscribe', 'b*', 0)

    def test_resubscribe_to_channels_on_reconnection(self, r):
        channels = ['foo', 'bar']
        p = r.pubsub()

        assert p.subscribe(*channels) is None

        for i, channel in enumerate(channels):
            i += 1  # enumerate is 0 index, but we want 1 based indexing
            assert wait_for_message(p) == make_message('subscribe', channel, i)

        # manually disconnect
        p.connection.disconnect()

        # calling get_message again reconnects and resubscribes
        for i, channel in enumerate(channels):
            i += 1  # enumerate is 0 index, but we want 1 based indexing
            assert wait_for_message(p) == make_message('subscribe', channel, i)

    def test_resubscribe_to_patterns_on_reconnection(self, r):
        patterns = ['f*', 'b*']
        p = r.pubsub()

        assert p.psubscribe(*patterns) is None

        for i, pattern in enumerate(patterns):
            i += 1  # enumerate is 0 index, but we want 1 based indexing
            assert wait_for_message(p) == make_message(
                'psubscribe', pattern, i)

        # manually disconnect
        p.connection.disconnect()

        # calling get_message again reconnects and resubscribes
        for i, pattern in enumerate(patterns):
            i += 1  # enumerate is 0 index, but we want 1 based indexing
            assert wait_for_message(p) == make_message(
                'psubscribe', pattern, i)

    def test_ignore_all_subscribe_messages(self, r):
        p = r.pubsub(ignore_subscribe_messages=True)

        checks = (
            (p.subscribe, 'foo'),
            (p.unsubscribe, 'foo'),
            (p.psubscribe, 'f*'),
            (p.unsubscribe, 'f*'),
        )

        for func, channel in checks:
            assert func(channel) is None
            assert wait_for_message(p) is None

    def test_ignore_individual_subscribe_messages(self, r):
        p = r.pubsub()

        checks = (
            (p.subscribe, 'foo'),
            (p.unsubscribe, 'foo'),
            (p.psubscribe, 'f*'),
            (p.unsubscribe, 'f*'),
        )

        for func, channel in checks:
            assert func(channel) is None
            message = wait_for_message(p, ignore_subscribe_messages=True)
            assert message is None


class TestPubSubRedisDown(object):

    def test_channel_subscribe(self, r):
        r = redis.Redis(host='localhost', port=6390)
        p = r.pubsub()
        with pytest.raises(ConnectionError):
            p.subscribe('foo')