1
- import os
2
- import random
1
+ import socket
3
2
import struct
3
+
4
+ import mock
4
5
import unittest2
5
- import kafka .conn
6
+
7
+ from kafka .common import *
8
+ from kafka .conn import *
6
9
7
10
class ConnTest (unittest2 .TestCase ):
11
+ def setUp (self ):
12
+ self .config = {
13
+ 'host' : 'localhost' ,
14
+ 'port' : 9090 ,
15
+ 'request_id' : 0 ,
16
+ 'payload' : 'test data' ,
17
+ 'payload2' : 'another packet'
18
+ }
19
+
20
+ # Mocking socket.create_connection will cause _sock to always be a
21
+ # MagicMock()
22
+ patcher = mock .patch ('socket.create_connection' , spec = True )
23
+ self .MockCreateConn = patcher .start ()
24
+ self .addCleanup (patcher .stop )
25
+
26
+ # Also mock socket.sendall() to appear successful
27
+ socket .create_connection ().sendall .return_value = None
28
+
29
+ # And mock socket.recv() to return two payloads, then '', then raise
30
+ # Note that this currently ignores the num_bytes parameter to sock.recv()
31
+ payload_size = len (self .config ['payload' ])
32
+ payload2_size = len (self .config ['payload2' ])
33
+ socket .create_connection ().recv .side_effect = [
34
+ struct .pack ('>i' , payload_size ),
35
+ struct .pack ('>%ds' % payload_size , self .config ['payload' ]),
36
+ struct .pack ('>i' , payload2_size ),
37
+ struct .pack ('>%ds' % payload2_size , self .config ['payload2' ]),
38
+ ''
39
+ ]
40
+
41
+ # Create a connection object
42
+ self .conn = KafkaConnection (self .config ['host' ], self .config ['port' ])
43
+
44
+ # Reset any mock counts caused by __init__
45
+ socket .create_connection .reset_mock ()
46
+
8
47
def test_collect_hosts__happy_path (self ):
9
48
hosts = "localhost:1234,localhost"
10
- results = kafka . conn . collect_hosts (hosts )
49
+ results = collect_hosts (hosts )
11
50
12
51
self .assertEqual (set (results ), set ([
13
52
('localhost' , 1234 ),
@@ -20,7 +59,7 @@ def test_collect_hosts__string_list(self):
20
59
'localhost' ,
21
60
]
22
61
23
- results = kafka . conn . collect_hosts (hosts )
62
+ results = collect_hosts (hosts )
24
63
25
64
self .assertEqual (set (results ), set ([
26
65
('localhost' , 1234 ),
@@ -29,41 +68,97 @@ def test_collect_hosts__string_list(self):
29
68
30
69
def test_collect_hosts__with_spaces (self ):
31
70
hosts = "localhost:1234, localhost"
32
- results = kafka . conn . collect_hosts (hosts )
71
+ results = collect_hosts (hosts )
33
72
34
73
self .assertEqual (set (results ), set ([
35
74
('localhost' , 1234 ),
36
75
('localhost' , 9092 ),
37
76
]))
38
77
39
- @unittest2 .skip ("Not Implemented" )
40
78
def test_send (self ):
41
- pass
79
+ self .conn .send (self .config ['request_id' ], self .config ['payload' ])
80
+ self .conn ._sock .sendall .assert_called_with (self .config ['payload' ])
81
+
82
+ def test_init_creates_socket_connection (self ):
83
+ KafkaConnection (self .config ['host' ], self .config ['port' ])
84
+ socket .create_connection .assert_called_with ((self .config ['host' ], self .config ['port' ]), DEFAULT_SOCKET_TIMEOUT_SECONDS )
85
+
86
+ def test_init_failure_raises_connection_error (self ):
87
+
88
+ def raise_error (* args ):
89
+ raise socket .error
90
+
91
+ assert socket .create_connection is self .MockCreateConn
92
+ socket .create_connection .side_effect = raise_error
93
+ with self .assertRaises (ConnectionError ):
94
+ KafkaConnection (self .config ['host' ], self .config ['port' ])
42
95
43
- @unittest2 .skip ("Not Implemented" )
44
96
def test_send__reconnects_on_dirty_conn (self ):
45
- pass
46
97
47
- @unittest2 .skip ("Not Implemented" )
98
+ # Dirty the connection
99
+ try :
100
+ self .conn ._raise_connection_error ()
101
+ except ConnectionError :
102
+ pass
103
+
104
+ # Now test that sending attempts to reconnect
105
+ self .assertEqual (socket .create_connection .call_count , 0 )
106
+ self .conn .send (self .config ['request_id' ], self .config ['payload' ])
107
+ self .assertEqual (socket .create_connection .call_count , 1 )
108
+
48
109
def test_send__failure_sets_dirty_connection (self ):
49
- pass
50
110
51
- @unittest2 .skip ("Not Implemented" )
111
+ def raise_error (* args ):
112
+ raise socket .error
113
+
114
+ assert isinstance (self .conn ._sock , mock .Mock )
115
+ self .conn ._sock .sendall .side_effect = raise_error
116
+ try :
117
+ self .conn .send (self .config ['request_id' ], self .config ['payload' ])
118
+ except ConnectionError :
119
+ self .assertIsNone (self .conn ._sock )
120
+
52
121
def test_recv (self ):
53
- pass
54
122
55
- @unittest2 .skip ("Not Implemented" )
123
+ self .assertEquals (self .conn .recv (self .config ['request_id' ]), self .config ['payload' ])
124
+
56
125
def test_recv__reconnects_on_dirty_conn (self ):
57
- pass
58
126
59
- @unittest2 .skip ("Not Implemented" )
127
+ # Dirty the connection
128
+ try :
129
+ self .conn ._raise_connection_error ()
130
+ except ConnectionError :
131
+ pass
132
+
133
+ # Now test that recv'ing attempts to reconnect
134
+ self .assertEqual (socket .create_connection .call_count , 0 )
135
+ self .conn .recv (self .config ['request_id' ])
136
+ self .assertEqual (socket .create_connection .call_count , 1 )
137
+
60
138
def test_recv__failure_sets_dirty_connection (self ):
61
- pass
62
139
63
- @unittest2 .skip ("Not Implemented" )
140
+ def raise_error (* args ):
141
+ raise socket .error
142
+
143
+ # test that recv'ing attempts to reconnect
144
+ assert isinstance (self .conn ._sock , mock .Mock )
145
+ self .conn ._sock .recv .side_effect = raise_error
146
+ try :
147
+ self .conn .recv (self .config ['request_id' ])
148
+ except ConnectionError :
149
+ self .assertIsNone (self .conn ._sock )
150
+
64
151
def test_recv__doesnt_consume_extra_data_in_stream (self ):
65
- pass
66
152
67
- @unittest2 .skip ("Not Implemented" )
153
+ # Here just test that each call to recv will return a single payload
154
+ self .assertEquals (self .conn .recv (self .config ['request_id' ]), self .config ['payload' ])
155
+ self .assertEquals (self .conn .recv (self .config ['request_id' ]), self .config ['payload2' ])
156
+
68
157
def test_close__object_is_reusable (self ):
69
- pass
158
+
159
+ # test that sending to a closed connection
160
+ # will re-connect and send data to the socket
161
+ self .conn .close ()
162
+ self .conn .send (self .config ['request_id' ], self .config ['payload' ])
163
+ self .assertEqual (socket .create_connection .call_count , 1 )
164
+ self .conn ._sock .sendall .assert_called_with (self .config ['payload' ])
0 commit comments