leffen-kafka-rb 0.0.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,251 @@
1
+ # encoding: utf-8
2
+
3
+ # Licensed to the Apache Software Foundation (ASF) under one or more
4
+ # contributor license agreements. See the NOTICE file distributed with
5
+ # this work for additional information regarding copyright ownership.
6
+ # The ASF licenses this file to You under the Apache License, Version 2.0
7
+ # (the "License"); you may not use this file except in compliance with
8
+ # the License. You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ require File.dirname(__FILE__) + '/spec_helper'
18
+
19
+ describe Encoder do
20
+ def check_message(bytes, message)
21
+ encoded = [message.magic].pack("C") + [message.calculate_checksum].pack("N") + message.payload
22
+ encoded = [encoded.length].pack("N") + encoded
23
+ bytes.should == encoded
24
+ end
25
+
26
+ describe "Message Encoding" do
27
+ it "should encode a message" do
28
+ message = Kafka::Message.new("alejandro")
29
+ check_message(described_class.message(message), message)
30
+ end
31
+
32
+ it "should encode an empty message" do
33
+ message = Kafka::Message.new
34
+ check_message(described_class.message(message), message)
35
+ end
36
+
37
+ it "should encode strings containing non-ASCII characters" do
38
+ message = Kafka::Message.new("ümlaut")
39
+ encoded = described_class.message(message)
40
+ message = Kafka::Message.parse_from(encoded).messages.first
41
+ if RUBY_VERSION[0,3] == "1.8" # Use old iconv on Ruby 1.8 for encoding
42
+ #ic = Iconv.new('UTF-8//IGNORE', 'UTF-8')
43
+ #ic.iconv(message.payload).should eql("ümlaut")
44
+ message.payload.should eql("ümlaut")
45
+ else
46
+ message.payload.force_encoding(Encoding::UTF_8).should eql("ümlaut")
47
+ end
48
+ end
49
+
50
+ it "should encode strings containing non-ASCII characters" do
51
+ message = Kafka::Message.new("\214")
52
+ encoded = described_class.message(message)
53
+ message = Kafka::Message.parse_from(encoded).messages.first
54
+ if RUBY_VERSION[0,3] == "1.8"
55
+ message.payload.should eql("\214")
56
+ else
57
+ message.payload.force_encoding(Encoding::UTF_8).should eql("\214")
58
+ end
59
+ end
60
+ end
61
+
62
+ describe :compression do
63
+ before do
64
+ @message = Kafka::Message.new "foo"
65
+ end
66
+
67
+ it "should default to no compression" do
68
+ msg = "foo"
69
+ checksum = Zlib.crc32 msg
70
+ magic = 0
71
+ msg_size = 5 + msg.size
72
+ raw = [msg_size, magic, checksum, msg].pack "NCNa#{msg.size}"
73
+
74
+ Encoder.message(@message).should == raw
75
+ end
76
+
77
+ it "should support GZip compression" do
78
+ buffer = StringIO.new
79
+ gz = Zlib::GzipWriter.new buffer, nil, nil
80
+ gz.write "foo"
81
+ gz.close
82
+ buffer.rewind
83
+ msg = buffer.string
84
+ checksum = Zlib.crc32 msg
85
+ magic = 1
86
+ attrs = 1
87
+ msg_size = 6 + msg.size
88
+ raw = [msg_size, magic, attrs, checksum, msg].pack "NCCNa#{msg.size}"
89
+ Encoder.message(@message, 1).should == raw
90
+ end
91
+
92
+ if Object.const_defined? "Snappy"
93
+ it "should support Snappy compression" do
94
+ buffer = StringIO.new
95
+ Snappy::Writer.new buffer do |w|
96
+ w << "foo"
97
+ end
98
+ buffer.rewind
99
+ msg = buffer.string
100
+ checksum = Zlib.crc32 msg
101
+ magic = 1
102
+ attrs = 2
103
+ msg_size = 6 + msg.size
104
+ raw = [msg_size, magic, attrs, checksum, msg].pack "NCCNa#{msg.size}"
105
+
106
+ Encoder.message(@message, 2).should == raw
107
+ end
108
+ end
109
+ end
110
+
111
+ describe "produce" do
112
+ it "should binary encode an empty request" do
113
+ bytes = described_class.produce("test", 0, [])
114
+ bytes.length.should eql(20)
115
+ bytes.should eql("\000\000\000\020\000\000\000\004test\000\000\000\000\000\000\000\000")
116
+ end
117
+
118
+ it "should binary encode a request with a message, using a specific wire format" do
119
+ message = Kafka::Message.new("ale")
120
+ bytes = described_class.produce("test", 3, message)
121
+ data_size = bytes[0, 4].unpack("N").shift
122
+ request_id = bytes[4, 2].unpack("n").shift
123
+ topic_length = bytes[6, 2].unpack("n").shift
124
+ topic = bytes[8, 4]
125
+ partition = bytes[12, 4].unpack("N").shift
126
+ messages_length = bytes[16, 4].unpack("N").shift
127
+ messages = bytes[20, messages_length]
128
+
129
+ bytes.length.should eql(32)
130
+ data_size.should eql(28)
131
+ request_id.should eql(0)
132
+ topic_length.should eql(4)
133
+ topic.should eql("test")
134
+ partition.should eql(3)
135
+ messages_length.should eql(12)
136
+ end
137
+ end
138
+
139
+ describe "message_set" do
140
+ it "should compress messages into a message set" do
141
+ message_one = Kafka::Message.new "foo"
142
+ message_two = Kafka::Message.new "bar"
143
+ bytes = described_class.message_set [message_one, message_two], Kafka::Message::GZIP_COMPRESSION
144
+
145
+ messages = Kafka::Message.parse_from bytes
146
+ messages.should be_a Kafka::Message::MessageSet
147
+ messages.messages.size.should == 2
148
+
149
+ messages.messages[0].should be_a Kafka::Message
150
+ messages.messages[0].payload.should == "foo"
151
+ messages.messages[1].should be_a Kafka::Message
152
+ messages.messages[1].payload.should == "bar"
153
+ end
154
+ end
155
+
156
+ describe "multiproduce" do
157
+ it "encodes an empty request" do
158
+ bytes = described_class.multiproduce([])
159
+ bytes.length.should == 8
160
+ bytes.should == "\x00\x00\x00\x04\x00\x03\x00\x00"
161
+ end
162
+
163
+ it "encodes a request with a single topic/partition" do
164
+ message = Kafka::Message.new("ale")
165
+ bytes = described_class.multiproduce(Kafka::ProducerRequest.new("test", message))
166
+
167
+ req_length = bytes[0, 4].unpack("N").shift
168
+ req_type = bytes[4, 2].unpack("n").shift
169
+ tp_count = bytes[6, 2].unpack("n").shift
170
+
171
+ req_type.should == Kafka::RequestType::MULTIPRODUCE
172
+ tp_count.should == 1
173
+
174
+ topic_length = bytes[8, 2].unpack("n").shift
175
+ topic = bytes[10, 4]
176
+ partition = bytes[14, 4].unpack("N").shift
177
+ messages_length = bytes[18, 4].unpack("N").shift
178
+ messages_data = bytes[22, messages_length]
179
+
180
+ topic_length.should == 4
181
+ topic.should == "test"
182
+ partition.should == 0
183
+ messages_length.should == 12
184
+ check_message(messages_data, message)
185
+ end
186
+
187
+ it "encodes a request with a single topic/partition but multiple messages" do
188
+ messages = [Kafka::Message.new("ale"), Kafka::Message.new("beer")]
189
+ bytes = described_class.multiproduce(Kafka::ProducerRequest.new("test", messages))
190
+
191
+ req_length = bytes[0, 4].unpack("N").shift
192
+ req_type = bytes[4, 2].unpack("n").shift
193
+ tp_count = bytes[6, 2].unpack("n").shift
194
+
195
+ req_type.should == Kafka::RequestType::MULTIPRODUCE
196
+ tp_count.should == 1
197
+
198
+ topic_length = bytes[8, 2].unpack("n").shift
199
+ topic = bytes[10, 4]
200
+ partition = bytes[14, 4].unpack("N").shift
201
+ messages_length = bytes[18, 4].unpack("N").shift
202
+ messages_data = bytes[22, messages_length]
203
+
204
+ topic_length.should == 4
205
+ topic.should == "test"
206
+ partition.should == 0
207
+ messages_length.should == 25
208
+ check_message(messages_data[0, 12], messages[0])
209
+ check_message(messages_data[12, 13], messages[1])
210
+ end
211
+
212
+ it "encodes a request with multiple topic/partitions" do
213
+ messages = [Kafka::Message.new("ale"), Kafka::Message.new("beer")]
214
+ bytes = described_class.multiproduce([
215
+ Kafka::ProducerRequest.new("test", messages[0]),
216
+ Kafka::ProducerRequest.new("topic", messages[1], :partition => 1),
217
+ ])
218
+
219
+ req_length = bytes[0, 4].unpack("N").shift
220
+ req_type = bytes[4, 2].unpack("n").shift
221
+ tp_count = bytes[6, 2].unpack("n").shift
222
+
223
+ req_type.should == Kafka::RequestType::MULTIPRODUCE
224
+ tp_count.should == 2
225
+
226
+ topic_length = bytes[8, 2].unpack("n").shift
227
+ topic = bytes[10, 4]
228
+ partition = bytes[14, 4].unpack("N").shift
229
+ messages_length = bytes[18, 4].unpack("N").shift
230
+ messages_data = bytes[22, 12]
231
+
232
+ topic_length.should == 4
233
+ topic.should == "test"
234
+ partition.should == 0
235
+ messages_length.should == 12
236
+ check_message(messages_data[0, 12], messages[0])
237
+
238
+ topic_length = bytes[34, 2].unpack("n").shift
239
+ topic = bytes[36, 5]
240
+ partition = bytes[41, 4].unpack("N").shift
241
+ messages_length = bytes[45, 4].unpack("N").shift
242
+ messages_data = bytes[49, 13]
243
+
244
+ topic_length.should == 5
245
+ topic.should == "topic"
246
+ partition.should == 1
247
+ messages_length.should == 13
248
+ check_message(messages_data[0, 13], messages[1])
249
+ end
250
+ end
251
+ end
@@ -0,0 +1,88 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership.
4
+ # The ASF licenses this file to You under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with
6
+ # the License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ require File.dirname(__FILE__) + '/spec_helper'
16
+
17
+ class IOTest
18
+ include Kafka::IO
19
+ end
20
+
21
+ describe IO do
22
+
23
+ before(:each) do
24
+ @mocked_socket = mock(TCPSocket)
25
+ TCPSocket.stub!(:new).and_return(@mocked_socket) # don't use a real socket
26
+ @io = IOTest.new
27
+ @io.connect("somehost", 9093)
28
+ end
29
+
30
+ describe "default methods" do
31
+ it "has a socket, a host and a port" do
32
+ [:socket, :host, :port].each do |m|
33
+ @io.should respond_to(m.to_sym)
34
+ end
35
+ end
36
+
37
+ it "raises an exception if no host and port is specified" do
38
+ lambda {
39
+ io = IOTest.new
40
+ io.connect
41
+ }.should raise_error(ArgumentError)
42
+ end
43
+
44
+ it "should remember the port and host on connect" do
45
+ @io.connect("somehost", 9093)
46
+ @io.host.should eql("somehost")
47
+ @io.port.should eql(9093)
48
+ end
49
+
50
+ it "should write to a socket" do
51
+ data = "some data"
52
+ @mocked_socket.should_receive(:write).with(data).and_return(9)
53
+ @io.write(data).should eql(9)
54
+ end
55
+
56
+ it "should read from a socket" do
57
+ length = 200
58
+ @mocked_socket.should_receive(:read).with(length).and_return("foo")
59
+ @io.read(length)
60
+ end
61
+
62
+ it "should disconnect on a timeout when reading from a socket (to aviod protocol desync state)" do
63
+ length = 200
64
+ @mocked_socket.should_receive(:read).with(length).and_raise(Errno::EAGAIN)
65
+ @io.should_receive(:disconnect)
66
+ lambda { @io.read(length) }.should raise_error(Kafka::SocketError)
67
+ end
68
+
69
+ it "should disconnect" do
70
+ @io.should respond_to(:disconnect)
71
+ @mocked_socket.should_receive(:close).and_return(nil)
72
+ @io.disconnect
73
+ end
74
+
75
+ it "should reconnect" do
76
+ TCPSocket.should_receive(:new)
77
+ @io.reconnect
78
+ end
79
+
80
+ it "should disconnect on a broken pipe error" do
81
+ [Errno::ECONNABORTED, Errno::EPIPE, Errno::ECONNRESET].each do |error|
82
+ @mocked_socket.should_receive(:write).exactly(:once).and_raise(error)
83
+ @mocked_socket.should_receive(:close).exactly(:once).and_return(nil)
84
+ lambda { @io.write("some data to send") }.should raise_error(Kafka::SocketError)
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,20 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership.
4
+ # The ASF licenses this file to You under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with
6
+ # the License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ require File.dirname(__FILE__) + '/spec_helper'
16
+
17
+ describe Kafka do
18
+ before(:each) do
19
+ end
20
+ end
@@ -0,0 +1,227 @@
1
+ # Licensed to the Apache Software Foundation (ASF) under one or more
2
+ # contributor license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright ownership.
4
+ # The ASF licenses this file to You under the Apache License, Version 2.0
5
+ # (the "License"); you may not use this file except in compliance with
6
+ # the License. You may obtain a copy of the License at
7
+ #
8
+ # http://www.apache.org/licenses/LICENSE-2.0
9
+ #
10
+ # Unless required by applicable law or agreed to in writing, software
11
+ # distributed under the License is distributed on an "AS IS" BASIS,
12
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ # See the License for the specific language governing permissions and
14
+ # limitations under the License.
15
+ require File.dirname(__FILE__) + '/spec_helper'
16
+
17
+ describe Message do
18
+
19
+ def pack_v1_message bytes, attributes
20
+ [6 + bytes.length, 1, attributes, Zlib.crc32(bytes), bytes].pack "NCCNa*"
21
+ end
22
+
23
+ before(:each) do
24
+ @message = Message.new
25
+ end
26
+
27
+ describe "Kafka Message" do
28
+ it "should have a default magic number" do
29
+ Message::MAGIC_IDENTIFIER_DEFAULT.should eql(0)
30
+ end
31
+
32
+ it "should have a magic field, a checksum and a payload" do
33
+ [:magic, :checksum, :payload].each do |field|
34
+ @message.should respond_to(field.to_sym)
35
+ end
36
+ end
37
+
38
+ it "should set a default value of zero" do
39
+ @message.magic.should eql(Kafka::Message::MAGIC_IDENTIFIER_DEFAULT)
40
+ end
41
+
42
+ it "should allow to set a custom magic number" do
43
+ @message = Message.new("ale", 1)
44
+ @message.magic.should eql(1)
45
+ end
46
+
47
+ it "should have an empty payload by default" do
48
+ @message.payload.should == ""
49
+ end
50
+
51
+ it "should calculate the checksum (crc32 of a given message)" do
52
+ @message.payload = "ale"
53
+ @message.calculate_checksum.should eql(1120192889)
54
+ @message.payload = "alejandro"
55
+ @message.calculate_checksum.should eql(2865078607)
56
+ end
57
+
58
+ it "should say if the message is valid using the crc32 signature" do
59
+ @message.payload = "alejandro"
60
+ @message.checksum = 2865078607
61
+ @message.valid?.should eql(true)
62
+ @message.checksum = 0
63
+ @message.valid?.should eql(false)
64
+ @message = Message.new("alejandro", 0, 66666666) # 66666666 is a funny checksum
65
+ @message.valid?.should eql(false)
66
+ end
67
+ end
68
+
69
+ describe "parsing" do
70
+ it "should parse a version-0 message from bytes" do
71
+ bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
72
+ message = Kafka::Message.parse_from(bytes).messages.first
73
+ message.valid?.should eql(true)
74
+ message.magic.should eql(0)
75
+ message.checksum.should eql(1120192889)
76
+ message.payload.should eql("ale")
77
+ end
78
+
79
+ it "should parse a version-1 message from bytes" do
80
+ bytes = [12, 1, 0, 755095536, 'martin'].pack('NCCNa*')
81
+ message = Kafka::Message.parse_from(bytes).messages.first
82
+ message.should be_valid
83
+ message.magic.should == 1
84
+ message.checksum.should == 755095536
85
+ message.payload.should == 'martin'
86
+ end
87
+
88
+ it "should raise an error if the magic number is not recognised" do
89
+ bytes = [12, 2, 0, 755095536, 'martin'].pack('NCCNa*') # 2 = some future format that's not yet invented
90
+ lambda {
91
+ Kafka::Message.parse_from(bytes)
92
+ }.should raise_error(RuntimeError, /Unsupported Kafka message version/)
93
+ end
94
+
95
+ it "should skip an incomplete message at the end of the response" do
96
+ bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
97
+ bytes += [8].pack('N') # incomplete message (only length, rest is truncated)
98
+ message_set = Message.parse_from(bytes)
99
+ message_set.messages.size.should == 1
100
+ message_set.size.should == 12 # bytes consumed
101
+ end
102
+
103
+ it "should skip an incomplete message at the end of the response which has the same length as an empty message" do
104
+ bytes = [8, 0, 1120192889, 'ale'].pack('NCNa*')
105
+ bytes += [8, 0, 1120192889].pack('NCN') # incomplete message (payload is missing)
106
+ message_set = Message.parse_from(bytes)
107
+ message_set.messages.size.should == 1
108
+ message_set.size.should == 12 # bytes consumed
109
+ end
110
+
111
+ it "should read empty messages correctly" do
112
+ # empty message
113
+ bytes = [5, 0, 0, ''].pack('NCNa*')
114
+ messages = Message.parse_from(bytes).messages
115
+ messages.size.should == 1
116
+ messages.first.payload.should == ''
117
+ end
118
+
119
+ it "should parse a gzip-compressed message" do
120
+ compressed = 'H4sIAG0LI1AAA2NgYBBkZBB/9XN7YlJRYnJiCogCAH9lueQVAAAA'.unpack('m*').shift
121
+ bytes = [45, 1, 1, 1303540914, compressed].pack('NCCNa*')
122
+ message = Message.parse_from(bytes).messages.first
123
+ message.should be_valid
124
+ message.payload.should == 'abracadabra'
125
+ end
126
+
127
+ if Object.const_defined? "Snappy"
128
+ it "should parse a snappy-compressed message" do
129
+ cleartext = "abracadabra"
130
+ bytes = pack_v1_message cleartext, 0
131
+ compressed = Snappy.deflate(bytes)
132
+ bytes = pack_v1_message compressed, 2
133
+ message = Message.parse_from(bytes).messages.first
134
+ message.should be_valid
135
+ message.payload.should == cleartext
136
+ end
137
+
138
+ it "should recursively parse nested snappy compressed messages" do
139
+ uncompressed = pack_v1_message('abracadabra', 0)
140
+ uncompressed << pack_v1_message('foobar', 0)
141
+ compressed = pack_v1_message(Snappy.deflate(uncompressed), 2)
142
+ messages = Message.parse_from(compressed).messages
143
+ messages.map(&:payload).should == ['abracadabra', 'foobar']
144
+ messages.map(&:valid?).should == [true, true]
145
+ end
146
+
147
+ it "should support a mixture of snappy compressed and uncompressed messages" do
148
+ bytes = pack_v1_message(Snappy.deflate(pack_v1_message("compressed", 0)), 2)
149
+ bytes << pack_v1_message('uncompressed', 0)
150
+ messages = Message.parse_from(bytes).messages
151
+ messages.map(&:payload).should == ["compressed", "uncompressed"]
152
+ messages.map(&:valid?).should == [true, true]
153
+ end
154
+ end
155
+
156
+ it "should recursively parse nested gzip compressed messages" do
157
+ uncompressed = [17, 1, 0, 401275319, 'abracadabra'].pack('NCCNa*')
158
+ uncompressed << [12, 1, 0, 2666930069, 'foobar'].pack('NCCNa*')
159
+ compressed_io = StringIO.new('')
160
+ Zlib::GzipWriter.new(compressed_io).tap{|gzip| gzip << uncompressed; gzip.close }
161
+ compressed = compressed_io.string
162
+ bytes = [compressed.size + 6, 1, 1, Zlib.crc32(compressed), compressed].pack('NCCNa*')
163
+ messages = Message.parse_from(bytes).messages
164
+ messages.map(&:payload).should == ['abracadabra', 'foobar']
165
+ messages.map(&:valid?).should == [true, true]
166
+ end
167
+
168
+ it "should support a mixture of gzip compressed and uncompressed messages" do
169
+ compressed = 'H4sIAG0LI1AAA2NgYBBkZBB/9XN7YlJRYnJiCogCAH9lueQVAAAA'.unpack('m*').shift
170
+ bytes = [45, 1, 1, 1303540914, compressed].pack('NCCNa*')
171
+ bytes << [11, 1, 0, 907060870, 'hello'].pack('NCCNa*')
172
+ messages = Message.parse_from(bytes).messages
173
+ messages.map(&:payload).should == ['abracadabra', 'hello']
174
+ messages.map(&:valid?).should == [true, true]
175
+ end
176
+
177
+ it "should raise an error if the compression codec is not supported" do
178
+ bytes = [6, 1, 3, 0, ''].pack('NCCNa*') # 3 = some unknown future compression codec
179
+ lambda {
180
+ Kafka::Message.parse_from(bytes)
181
+ }.should raise_error(RuntimeError, /Unsupported Kafka compression codec/)
182
+ end
183
+ end
184
+
185
+ describe "#ensure_snappy!" do
186
+ let(:message) { Kafka::Message.new }
187
+ before { Kafka::Message.instance_variable_set :@snappy, nil }
188
+
189
+ subject { message.ensure_snappy! { 42 } }
190
+
191
+ if Object.const_defined? "Snappy"
192
+ context "when snappy is available" do
193
+ before { Object.stub! :const_defined? => true }
194
+ it { should == 42 }
195
+ end
196
+ end
197
+
198
+ context "when snappy is not available" do
199
+ before { Object.stub! :const_defined? => false }
200
+
201
+ it "raises an error" do
202
+ expect { message.ensure_snappy! { 42 } }.to raise_error
203
+ end
204
+ end
205
+ end
206
+
207
+ describe ".ensure_snappy!" do
208
+ before { Kafka::Message.instance_variable_set :@snappy, nil }
209
+
210
+ subject { Kafka::Message.ensure_snappy! { 42 } }
211
+
212
+ if Object.const_defined? "Snappy"
213
+ context "when snappy is available" do
214
+ before { Object.stub! :const_defined? => true }
215
+ it { should == 42 }
216
+ end
217
+ end
218
+
219
+ context "when snappy is not available" do
220
+ before { Object.stub! :const_defined? => false }
221
+
222
+ it "raises an error" do
223
+ expect { Kafka::Message.ensure_snappy! { 42 } }.to raise_error
224
+ end
225
+ end
226
+ end
227
+ end