twirl 0.1.0

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.
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