twirl 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/test/helper.rb ADDED
@@ -0,0 +1,65 @@
1
+ require "minitest/autorun"
2
+ require_relative "support/fake_udp_socket"
3
+ require "twirl/instrumenters/noop"
4
+
5
+ module ClusterTestHelpers
6
+ def build(what, *args)
7
+ send "build_#{what}", *args
8
+ end
9
+
10
+ private
11
+
12
+ def build_item(key, value, client, instrumenter = nil)
13
+ Twirl::Item.new(key, value, client, instrumenter)
14
+ end
15
+
16
+ # Builds a cluster that will connect to what script/kestrel starts.
17
+ def build_cluster(options = {})
18
+ clients = [
19
+ KJess::Client.new(host: "localhost", port: 9444),
20
+ KJess::Client.new(host: "localhost", port: 9544),
21
+ ]
22
+ queue_names = Array(options.delete(:queues))
23
+ Twirl::Cluster.new(clients, options).tap { |cluster|
24
+ queue_names.each { |queue_name|
25
+ cluster.each { |client|
26
+ client.flush queue_name
27
+ }
28
+ }
29
+ }
30
+ end
31
+
32
+ def build_mock_cluster(options = {})
33
+ number_of_clients = options.delete(:number_of_clients) || 3
34
+ clients = number_of_clients.times.map { Minitest::Mock.new }
35
+
36
+ Twirl::Cluster.new clients, options
37
+ end
38
+ end
39
+
40
+ module StatsdTestHelpers
41
+ def assert_timer(socket, metric)
42
+ regex = /#{Regexp.escape metric}\:\d+\|ms/
43
+ assert socket.buffer.detect { |op| op.first =~ regex },
44
+ "Timer #{metric} was not found in #{socket.buffer.inspect}"
45
+ end
46
+
47
+ def assert_counter(socket, metric)
48
+ assert socket.buffer.detect { |op| op.first == "#{metric}:1|c" },
49
+ "Counter #{metric} was not found in #{socket.buffer.inspect}"
50
+ end
51
+
52
+ def assert_no_timer(socket, metric)
53
+ regex = /#{Regexp.escape metric}\:\d+\|ms/
54
+ assert_nil socket.buffer.detect { |op| op.first =~ regex },
55
+ "Timer #{metric} was found in #{socket.buffer.inspect}"
56
+ end
57
+
58
+ def assert_no_counter(socket, metric)
59
+ assert_nil socket.buffer.detect { |op| op.first == "#{metric}:1|c" },
60
+ "Counter #{metric} was found in #{socket.buffer.inspect}"
61
+ end
62
+ end
63
+
64
+ Minitest::Test.send :include, ClusterTestHelpers
65
+ Minitest::Test.send :include, StatsdTestHelpers
@@ -0,0 +1,51 @@
1
+ require "helper"
2
+ require "logger"
3
+ require "twirl/instrumentation/log_subscriber"
4
+
5
+ class LogSubscriberInstrumentationTest < Minitest::Test
6
+ def setup
7
+ @cluster = build(:cluster, {
8
+ instrumenter: ActiveSupport::Notifications,
9
+ queues: %w(testing),
10
+ })
11
+ @io = StringIO.new
12
+ logger = Logger.new(@io)
13
+ logger.formatter = proc { |severity, datetime, progname, msg| "#{msg}\n" }
14
+ Twirl::Instrumentation::LogSubscriber.logger = logger
15
+ end
16
+
17
+ def log
18
+ @io.string
19
+ end
20
+
21
+ def teardown
22
+ Twirl::Instrumentation::LogSubscriber.logger = nil
23
+ end
24
+
25
+ def test_set
26
+ @cluster.set "testing", "data"
27
+ line = find_line("Twirl op(set)")
28
+ assert_match %r{queue_name=testing bytes=4}, line
29
+ end
30
+
31
+ def test_get
32
+ @cluster.set "testing", "data"
33
+ @cluster.get "testing"
34
+ line = find_line("Twirl op(get)")
35
+ assert_match %r{queue_name=testing bytes=4}, line
36
+ end
37
+
38
+ def test_reserve
39
+ @cluster.set "testing", "data"
40
+ @cluster.reserve "testing"
41
+ line = find_line("Twirl op(reserve)")
42
+ assert_match %r{queue_name=testing bytes=4}, line
43
+ end
44
+
45
+ def find_line(str)
46
+ regex = /#{Regexp.escape(str)}/
47
+ lines = log.split("\n")
48
+ lines.detect { |line| line =~ regex } ||
49
+ raise("Could not find line matching #{str.inspect} in #{lines.inspect}")
50
+ end
51
+ end
@@ -0,0 +1,173 @@
1
+ require "helper"
2
+ require "twirl/instrumentation/statsd"
3
+
4
+ class StatsdInstrumentationTest < Minitest::Test
5
+ def setup
6
+ @statsd = Statsd.new
7
+ @socket = FakeUDPSocket.new
8
+ @cluster = build(:cluster, {
9
+ instrumenter: ActiveSupport::Notifications,
10
+ queues: %w(testing),
11
+ })
12
+ Thread.current[:statsd_socket] = @socket
13
+ Twirl::Instrumentation::StatsdSubscriber.client = @statsd
14
+ end
15
+
16
+ def teardown
17
+ Thread.current[:statsd_socket] = nil
18
+ Twirl::Instrumentation::StatsdSubscriber.client = nil
19
+ end
20
+
21
+ def test_set
22
+ @cluster.set "testing", "data"
23
+ assert_timer @socket, "twirl.op_set"
24
+ assert_timer @socket, "twirl.queue_testing_op_set"
25
+ assert_counter @socket, "twirl.bytes_op_set"
26
+ assert_counter @socket, "twirl.bytes_queue_testing_op_set"
27
+ end
28
+
29
+ def test_get
30
+ @cluster.set "testing", "data"
31
+ @cluster.get "testing"
32
+ assert_timer @socket, "twirl.op_get"
33
+ assert_timer @socket, "twirl.queue_testing_op_get"
34
+ assert_counter @socket, "twirl.bytes_op_get"
35
+ assert_counter @socket, "twirl.bytes_queue_testing_op_get"
36
+ end
37
+
38
+ def test_item_close
39
+ @cluster.set "testing", "data"
40
+ item = @cluster.reserve("testing")
41
+ item.close
42
+ assert_timer @socket, "twirl.op_item_close"
43
+ assert_timer @socket, "twirl.queue_testing_op_item_close"
44
+ end
45
+
46
+ def test_item_abort
47
+ @cluster.set "testing", "data"
48
+ item = @cluster.reserve("testing")
49
+ item.abort
50
+ assert_timer @socket, "twirl.op_item_abort"
51
+ assert_timer @socket, "twirl.queue_testing_op_item_abort"
52
+ end
53
+
54
+ def test_reserve
55
+ @cluster.set "testing", "data"
56
+ @cluster.reserve "testing"
57
+ assert_timer @socket, "twirl.op_reserve"
58
+ assert_timer @socket, "twirl.queue_testing_op_reserve"
59
+ assert_counter @socket, "twirl.bytes_op_reserve"
60
+ assert_counter @socket, "twirl.bytes_queue_testing_op_reserve"
61
+ end
62
+
63
+ def test_peek
64
+ @cluster.set "testing", "data"
65
+ @cluster.peek "testing"
66
+ assert_timer @socket, "twirl.op_peek"
67
+ assert_timer @socket, "twirl.queue_testing_op_peek"
68
+ assert_counter @socket, "twirl.bytes_op_peek"
69
+ assert_counter @socket, "twirl.bytes_queue_testing_op_peek"
70
+ end
71
+
72
+ def test_delete
73
+ @cluster.delete "testing"
74
+ assert_timer @socket, "twirl.op_delete"
75
+ assert_timer @socket, "twirl.queue_testing_op_delete"
76
+ end
77
+
78
+ def test_flush
79
+ @cluster.flush "testing"
80
+ assert_timer @socket, "twirl.op_flush"
81
+ assert_timer @socket, "twirl.queue_testing_op_flush"
82
+ end
83
+
84
+ def test_flush_all
85
+ @cluster.flush_all
86
+ assert_timer @socket, "twirl.op_flush_all"
87
+ end
88
+
89
+ def test_version
90
+ @cluster.version
91
+ assert_timer @socket, "twirl.op_version"
92
+ end
93
+
94
+ def test_ping
95
+ @cluster.ping
96
+ assert_timer @socket, "twirl.op_ping"
97
+ end
98
+
99
+ def test_quit
100
+ @cluster.quit
101
+ assert_timer @socket, "twirl.op_quit"
102
+ end
103
+
104
+ def test_stats
105
+ @cluster.stats
106
+ assert_timer @socket, "twirl.op_stats"
107
+ end
108
+
109
+ def test_disconnect
110
+ @cluster.disconnect
111
+ assert_timer @socket, "twirl.op_disconnect"
112
+ end
113
+
114
+ def test_rotate
115
+ cluster = build(:cluster, {
116
+ commands_per_client: 1,
117
+ instrumenter: ActiveSupport::Notifications,
118
+ })
119
+
120
+ # set data to avoid nil rotation
121
+ cluster[0].set "testing", "data"
122
+ cluster[1].set "testing", "data"
123
+ cluster.get("testing")
124
+ cluster.get("testing")
125
+
126
+ assert_counter @socket, "twirl.op_rotate"
127
+ end
128
+
129
+ def test_get_nil_instruments_rotate
130
+ @cluster.get("testing")
131
+ @cluster.get("testing")
132
+
133
+ assert_counter @socket, "twirl.op_rotate"
134
+ end
135
+
136
+ def test_get_retries_instrumented
137
+ @cluster.get("testing")
138
+ assert_no_timer @socket, "twirl.retries_op_get"
139
+
140
+ # force all clients to error
141
+ @cluster.each do |client|
142
+ def client.get(*args)
143
+ raise KJess::NetworkError
144
+ end
145
+ end
146
+
147
+ begin
148
+ @cluster.get("testing")
149
+ flunk "Should raise error and not get here."
150
+ rescue KJess::NetworkError
151
+ assert_counter @socket, "twirl.retries_op_get"
152
+ end
153
+ end
154
+
155
+ def test_set_retries_instrumented
156
+ @cluster.set("testing", "data")
157
+ assert_no_timer @socket, "twirl.retries_op_set"
158
+
159
+ # force all clients to error
160
+ @cluster.each do |client|
161
+ def client.set(*args)
162
+ raise KJess::NetworkError
163
+ end
164
+ end
165
+
166
+ begin
167
+ @cluster.set("testing", "data")
168
+ flunk "Should raise error and not get here."
169
+ rescue KJess::NetworkError
170
+ assert_counter @socket, "twirl.retries_op_set"
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,22 @@
1
+ require "helper"
2
+ require "twirl/instrumenters/memory"
3
+
4
+ class MemoryInstrumenterTest < Minitest::Test
5
+ def test_initialize
6
+ instrumentor = Twirl::Instrumenters::Memory.new
7
+ assert_equal [], instrumentor.events
8
+ end
9
+
10
+ def test_instrument
11
+ instrumentor = Twirl::Instrumenters::Memory.new
12
+ name = 'user.signup'
13
+ payload = {:email => 'john@doe.com'}
14
+ block_result = :yielded
15
+
16
+ result = instrumentor.instrument(name, payload) { block_result }
17
+ assert_equal block_result, result
18
+
19
+ event = Twirl::Instrumenters::Memory::Event.new(name, payload, block_result)
20
+ assert_equal [event], instrumentor.events
21
+ end
22
+ end
@@ -0,0 +1,16 @@
1
+ require "helper"
2
+ require "twirl/instrumenters/noop"
3
+
4
+ class NoopInstrumenterTest < Minitest::Test
5
+ def test_instrument
6
+ yielded = false
7
+ Twirl::Instrumenters::Noop.instrument(:foo) { yielded = true }
8
+ assert yielded
9
+ end
10
+
11
+ def test_instrument_with_payload
12
+ yielded = false
13
+ Twirl::Instrumenters::Noop.instrument(:foo, pay: :load) { yielded = true }
14
+ assert yielded
15
+ end
16
+ end
@@ -0,0 +1,199 @@
1
+ require "helper"
2
+ require "twirl/cluster"
3
+
4
+ class ClusterIntegrationTest < Minitest::Test
5
+ def test_set
6
+ cluster = build(:cluster, queues: %w(testing))
7
+ client = cluster[0]
8
+ cluster.set("testing", "data")
9
+ assert_equal "data", client.get("testing")
10
+ end
11
+
12
+ def test_set_rotates
13
+ cluster = build(:cluster, commands_per_client: 1, queues: %w(testing))
14
+ client1 = cluster[0]
15
+ client2 = cluster[1]
16
+ cluster.set("testing", "data1")
17
+ cluster.set("testing", "data2")
18
+ cluster.set("testing", "data3")
19
+ cluster.set("testing", "data4")
20
+
21
+ results = []
22
+ assert_equal "data1", client1.get("testing")
23
+ assert_equal "data2", client2.get("testing")
24
+ assert_equal "data3", client1.get("testing")
25
+ assert_equal "data4", client2.get("testing")
26
+ end
27
+
28
+ def test_get
29
+ cluster = build(:cluster, queues: %w(testing))
30
+ client1 = cluster[0]
31
+ client2 = cluster[1]
32
+
33
+ client1.set("testing", "data1")
34
+ client1.set("testing", "data2")
35
+ client2.set("testing", "data3")
36
+
37
+ results = []
38
+ results << cluster.get("testing")
39
+ results << cluster.get("testing")
40
+ results << cluster.get("testing")
41
+ results << cluster.get("testing")
42
+ results << cluster.get("testing")
43
+
44
+ assert_equal ["data1", "data2", nil, "data3", nil], results.map { |result| result.value if result }
45
+ end
46
+
47
+ def test_get_when_item_reserved_raises_error
48
+ cluster = build(:cluster, queues: %w(testing))
49
+ client1 = cluster[0]
50
+
51
+ client1.set("testing", "data1")
52
+ cluster.get("testing", open: true)
53
+
54
+ assert_raises KJess::ClientError do
55
+ cluster.get("testing")
56
+ end
57
+ end
58
+
59
+ def test_reserve_with_item_close_and_item_abort
60
+ cluster = build(:cluster, queues: %w(testing))
61
+ client1 = cluster[0]
62
+ client2 = cluster[1]
63
+
64
+ client1.set("testing", "data1")
65
+ client2.set("testing", "data2")
66
+
67
+ item = cluster.reserve("testing")
68
+ assert_equal "data1", item.value
69
+
70
+ item.abort
71
+ item = cluster.reserve("testing")
72
+ assert_equal "data1", item.value
73
+
74
+ item.close
75
+ cluster.reserve("testing") # => nil, then rotate
76
+ item = cluster.reserve("testing")
77
+ assert_equal "data2", item.value
78
+
79
+ cluster.rotate_for_next_op # force rotation to client1
80
+ assert_nil cluster.reserve("testing") # client1 returns nil
81
+ item.close # close item hopefully with client2
82
+ assert_nil client2.get("testing") # make sure that client2 queue is empty
83
+ end
84
+
85
+ def test_peek
86
+ cluster = build(:cluster, queues: %w(testing))
87
+ client1 = cluster[0]
88
+ client2 = cluster[1]
89
+
90
+ client1.set("testing", "data")
91
+
92
+ item = Twirl::Item.new("testing", "data", client1)
93
+ assert_equal item, cluster.peek("testing")
94
+ assert_equal "data", client1.get("testing")
95
+ end
96
+
97
+ def test_peek_with_no_items
98
+ cluster = build(:cluster, queues: %w(testing))
99
+ assert_nil cluster.peek("testing")
100
+ end
101
+
102
+ def test_version
103
+ cluster = build(:cluster)
104
+ expected = {
105
+ "localhost:9444" => "2.4.1",
106
+ "localhost:9544" => "2.4.1",
107
+ }
108
+
109
+ assert_equal expected, cluster.version
110
+ end
111
+
112
+ def test_flush
113
+ cluster = build(:cluster, queues: %w(testing))
114
+ cluster.each do |client|
115
+ client.set("testing", "data")
116
+ end
117
+ cluster.flush("testing")
118
+
119
+ cluster.each do |client|
120
+ assert_nil client.get("testing")
121
+ end
122
+ end
123
+
124
+ def test_flush
125
+ cluster = build(:cluster, queues: %w(testing))
126
+ cluster.each { |client| client.set("testing", "data") }
127
+ cluster.flush("testing")
128
+
129
+ cluster.each do |client|
130
+ assert_nil client.get("testing")
131
+ end
132
+ end
133
+
134
+ def test_flush_all
135
+ cluster = build(:cluster, queues: %w(testing))
136
+ cluster.each { |client| client.set("testing", "data") }
137
+ cluster.flush_all
138
+
139
+ cluster.each do |client|
140
+ assert_nil client.get("testing")
141
+ end
142
+ end
143
+
144
+ def test_disconnect
145
+ cluster = build(:cluster)
146
+ cluster.each(&:ping)
147
+ expected = [true] * cluster.size
148
+ assert_equal expected, cluster.map(&:connected?)
149
+ cluster.disconnect
150
+ expected = [false] * cluster.size
151
+ assert_equal expected, cluster.map(&:connected?)
152
+ end
153
+
154
+ def test_quit
155
+ cluster = build(:cluster)
156
+ cluster.each(&:ping)
157
+ expected = [true] * cluster.size
158
+ assert_equal expected, cluster.map(&:connected?)
159
+ cluster.quit
160
+ expected = [false] * cluster.size
161
+ assert_equal expected, cluster.map(&:connected?)
162
+ end
163
+
164
+ def test_delete
165
+ cluster = build(:cluster)
166
+ cluster.each do |client|
167
+ client.delete("testing")
168
+ client.set("testing", "data")
169
+ assert_instance_of Hash, client.queue_stats("testing")
170
+ end
171
+ cluster.delete("testing")
172
+ cluster.each do |client|
173
+ assert_nil client.queue_stats("testing")
174
+ end
175
+ end
176
+
177
+ def test_ping
178
+ cluster = build(:cluster)
179
+ expected = {
180
+ "localhost:9444" => true,
181
+ "localhost:9544" => true,
182
+ }
183
+
184
+ assert_equal expected, cluster.ping
185
+ end
186
+
187
+ def test_stats
188
+ cluster = build(:cluster)
189
+ result = cluster.stats
190
+ assert_equal [
191
+ "localhost:9444",
192
+ "localhost:9544",
193
+ ], result.keys.sort
194
+
195
+ result.values.each do |value|
196
+ assert_instance_of Hash, value
197
+ end
198
+ end
199
+ end