bean_counter 0.0.1

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,227 @@
1
+ module BeanCounter::TestAssertions
2
+
3
+ # :call-seq:
4
+ # assert_enqueued(options = {Symbol,String => Numeric,Proc,Range,Regexp,String})
5
+ #
6
+ # Asserts that some number of jobs are enqueued that match the given Hash of
7
+ # +options+. If no +options+ are given, asserts that at least one job exists.
8
+ #
9
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
10
+ # of a job that the corresponding _value_ should be compared against. All attribute
11
+ # comparisons are performed using the triple equal (===) operator/method of
12
+ # the given _value_.
13
+ #
14
+ # +options+ may additionally include a _count_ key of 'count' or :count that
15
+ # can be used to assert that a particular number of matching jobs are found.
16
+ #
17
+ # If no _count_ option is provided, the assertion succeeds if any job is found
18
+ # that matches all of the +options+ given. If no jobs are found that match the
19
+ # +options+ given, the assertion fails.
20
+ #
21
+ # If a _count_ option is provided the assertion only succeeds if the triple equal
22
+ # (===) operator/method of the value of _count_ evaluates to true when given the
23
+ # total number of matching jobs. Otherwise the assertion fails. The use of ===
24
+ # allows for more advanced comparisons using Procs, Ranges, Regexps, etc.
25
+ #
26
+ # See Strategy#job_matches? and/or the #job_matches? method of the strategy
27
+ # in use for more detailed information on how it is determined whether or not
28
+ # a job matches the given +options+.
29
+ #
30
+ # assert_enqueued
31
+ #
32
+ # assert_enqueued(:body => /test/, :count => 5)
33
+ #
34
+ # assert_enqueued('state' => 'buried', 'count' => 3)
35
+ def assert_enqueued(options = {})
36
+ enqueue_expectation(:assert, options)
37
+ end
38
+
39
+
40
+ # :call-seq:
41
+ # assert_enqueues(options = {Symbol,String => Numeric,Proc,Range,Regexp,String}) { block }
42
+ #
43
+ # Asserts that the given +block+ enqueues some number of jobs that match the
44
+ # given Hash of +options+. If no +options+ are given, asserts that at least
45
+ # one job is enqueued during the execution of the +block+.
46
+ #
47
+ # Unlike #assert_enqueued, which will evaluate all jobs in the beanstalkd pool,
48
+ # this method will only evaluate jobs that were enqueued during the
49
+ # execution of the given +block+. This can be useful for performance and for
50
+ # making assertions that could return false positives if all jobs were
51
+ # evaluated.
52
+ #
53
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
54
+ # of a job that the corresponding _value_ should be compared against. All attribute
55
+ # comparisons are performed using the triple equal (===) operator/method of
56
+ # the given _value_.
57
+ #
58
+ # +options+ may additionally include a _count_ key of 'count' or :count that
59
+ # can be used to assert that a particular number of matching jobs are found.
60
+ #
61
+ # If no _count_ option is provided, the assertion succeeds if any job is found
62
+ # that matches all of the +options+ given. If no jobs are found that match the
63
+ # +options+ given, the assertion fails.
64
+ #
65
+ # If a _count_ option is provided the assertion only succeeds if the triple equal
66
+ # (===) operator/method of the value of _count_ evaluates to true when given the
67
+ # total number of matching jobs. Otherwise the assertion fails. The use of ===
68
+ # allows for more advanced comparisons using Procs, Ranges, Regexps, etc.
69
+ #
70
+ # See Strategy#job_matches? and/or the #job_matches? method of the strategy
71
+ # in use for more detailed information on how it is determined whether or not
72
+ # a job matches the given +options+.
73
+ #
74
+ # assert_enqueues(:body => /test/) do
75
+ # enqueue_some_jobs
76
+ # end
77
+ def assert_enqueues(options = {})
78
+ raise ArgumentError, 'Block required' unless block_given?
79
+
80
+ enqueue_expectation(:assert, options, &Proc.new)
81
+ end
82
+
83
+
84
+ # :call-seq:
85
+ # assert_tube(options = {Symbol,String => Numeric,Proc,Range,Regexp,String})
86
+ #
87
+ # Asserts that at least one tube exist that matches the given Hash of
88
+ # +options+. If no +options+ are given, asserts that at least one tube exists,
89
+ # which will always succeed.
90
+ #
91
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
92
+ # of a tube that the corresponding _value_ should be compared against. All attribute
93
+ # comparisons are performed using the triple equal (===) operator/method of
94
+ # the given _value_.
95
+ #
96
+ # The assertion succeeds if any tube exists that matches all of the given
97
+ # +options+. If no tube exists matching all of the given +options+, the assertion
98
+ # fails.
99
+ #
100
+ # See Strategy#tube_matches? and/or the #tube_matches? method of the strategy
101
+ # in use for more detailed information on how it is determined whether or not
102
+ # a tube matches the given +options+.
103
+ #
104
+ # assert_tube
105
+ #
106
+ # assert_tube(:name => /test/)
107
+ #
108
+ # assert_tube('current-jobs-ready' => 1..10)
109
+ def assert_tube(options = {})
110
+ tube_expectation(:assert, :failure_message, options)
111
+ end
112
+
113
+
114
+ # :call-seq:
115
+ # refute_enqueued(options = {Symbol,String => Numeric,Proc,Range,Regexp,String})
116
+ #
117
+ # Asserts that no jobs are enqueued that match the given Hash of +options+.
118
+ # If no +options+ are given, asserts that no jobs exist.
119
+ #
120
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
121
+ # of a job that the corresponding _value_ should be compared against. All attribute
122
+ # comparisons are performed using the triple equal (===) operator/method of
123
+ # the given _value_.
124
+ #
125
+ # The refutation succeeds if no jobs are found that match all of the +options+
126
+ # given. If any matching jobs are found, the refutation fails.
127
+ #
128
+ # See Strategy#job_matches? and/or the #job_matches? method of the strategy
129
+ # in use for more detailed information on how it is determined whether or not
130
+ # a job matches the given +options+.
131
+ #
132
+ # refute_enqueued
133
+ #
134
+ # refute_enqueued(:body => lambda {|body| body.length > 50})
135
+ #
136
+ # refute_enqueued('state' => 'buried')
137
+ def refute_enqueued(options = {})
138
+ enqueue_expectation(:refute, options)
139
+ end
140
+
141
+
142
+ # :call-seq:
143
+ # refute_enqueues(options = {Symbol,String => Numeric,Proc,Range,Regexp,String}) { block }
144
+ #
145
+ # Asserts that the given +block+ enqueues no jobs that match the given Hash
146
+ # of +options+. If no +options+ are given, asserts that no job are enqueued
147
+ # by the given +block+.
148
+ #
149
+ # Unlike #refute_enqueued, which will evaluate all jobs in the beanstalkd pool,
150
+ # this method will only evaluate jobs that were enqueued during the
151
+ # execution of the given +block+. This can be useful for performance and for
152
+ # making assertions that could return false positives if all jobs were
153
+ # evaluated.
154
+ #
155
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
156
+ # of a job that the corresponding _value_ should be compared against. All attribute
157
+ # comparisons are performed using the triple equal (===) operator/method of
158
+ # the given _value_.
159
+ #
160
+ # The refutation succeeds if no jobs are found that match all of the +options+
161
+ # given. If any matching jobs are found, the refutation fails.
162
+ #
163
+ # See Strategy#job_matches? and/or the #job_matches? method of the strategy
164
+ # in use for more detailed information on how it is determined whether or not
165
+ # a job matches the given +options+.
166
+ #
167
+ # refute_enqueues do
168
+ # do_some_work_that_should_not_enqueue_any_jobs
169
+ # end
170
+ def refute_enqueues(options = {})
171
+ raise ArgumentError, 'Block required' unless block_given?
172
+
173
+ enqueue_expectation(:refute, options, &Proc.new)
174
+ end
175
+
176
+
177
+ # :call-seq:
178
+ # refute_tube(options = {Symbol,String => Numeric,Proc,Range,Regexp,String})
179
+ #
180
+ # Asserts that no tube exist that matches the given Hash of +options+.
181
+ # If no options are given, asserts that no tubes exist which will always fail.
182
+ #
183
+ # Each _key_ in +options+ is a String or a Symbol that identifies an attribute
184
+ # of a tube that the corresponding _value_ should be compared against. All attribute
185
+ # comparisons are performed using the triple equal (===) operator/method of
186
+ # the given _value_.
187
+ #
188
+ # The refutation succeeds if no tube exists that matches all of the given
189
+ # +options+. If any tubes exist that match all of the given +options+, the
190
+ # refutation fails.
191
+ #
192
+ # See Strategy#tube_matches? and/or the #tube_matches? method of the strategy
193
+ # in use for more detailed information on how it is determined whether or not
194
+ # a tube matches the given +options+.
195
+ #
196
+ # refute_tube
197
+ # #=> always fails
198
+ #
199
+ # refute_tube(:name => /production/)
200
+ #
201
+ # refute_tube('current-jobs-ready' => 0)
202
+ def refute_tube(options = {})
203
+ tube_expectation(:refute, :negative_failure_message, options)
204
+ end
205
+
206
+ private
207
+
208
+
209
+ # Builds job expectation object, evaluates and makes appropriate assertion
210
+ # with appropriate failure message
211
+ def enqueue_expectation(assertion_method, options, &block)
212
+ message_type = assertion_method == :assert ? :failure_message : :negative_failure_message
213
+ expectation = BeanCounter::EnqueuedExpectation.new(options)
214
+ match = expectation.matches?(block_given? ? block : nil)
215
+ send(assertion_method, match, expectation.send(message_type))
216
+ end
217
+
218
+
219
+ # Builds tube expectation object, evaluates and makes appropriate assertion
220
+ # with appropriate failure message
221
+ def tube_expectation(assertion_method, message_type, options)
222
+ expectation = BeanCounter::TubeExpectation.new(options)
223
+ match = expectation.matches?
224
+ send(assertion_method, match, expectation.send(message_type))
225
+ end
226
+
227
+ end
@@ -0,0 +1,3 @@
1
+ require 'bean_counter/mini_test'
2
+
3
+ Test::Unit::TestCase.send(:include, BeanCounter::MiniTest)
@@ -0,0 +1,66 @@
1
+ class BeanCounter::TubeExpectation
2
+
3
+ extend Forwardable
4
+
5
+ def_delegators BeanCounter, :strategy
6
+
7
+ # The value that the expectation expects
8
+ attr_reader :expected
9
+
10
+ # The tube found by the expecation during matching
11
+ attr_reader :found
12
+
13
+
14
+ # Builds the failure message used in the event of a positive expectation
15
+ # failure
16
+ def failure_message
17
+ return '' unless found.nil?
18
+ return "expected tube matching #{expected.to_s}, found none."
19
+ end
20
+
21
+
22
+ # Create a new tube expectation. Uses the given +expected+ Hash to determine
23
+ # if any tubes exist that match the expected options.
24
+ #
25
+ # Each _key_ in the +expected+ Hash is a String or a Symbol that identifies
26
+ # an attribute of a tube that the corresponding _value_ should be compared
27
+ # against. All attribute comparisons are performed using the triple equal
28
+ # (===) operator/method of the given _value_.
29
+ #
30
+ # See BeanCounter::MiniTest and/or BeanCounter::SpecMatchers for more information.
31
+ def initialize(expected)
32
+ @expected = expected
33
+ end
34
+
35
+
36
+ # Checks all tubes in the beanstalkd pool for a tube matching the expected
37
+ # options hash given at instantiation. The expectation succeeds if any tube
38
+ # exists that matches all of the expected options. If no tube exists
39
+ # matching all of the given options, the expectation fails.
40
+ #
41
+ # See Strategy#tube_matches? and/or the #tube_matches? method of the strategy
42
+ # in use for more detailed information on how it is determined whether or not
43
+ # a tube matches the options expected.
44
+ #
45
+ # See also BeanCounter::MiniTest and/or BeanCounter::SpecMatchers for additional
46
+ # information.
47
+ def matches?(given = nil)
48
+ @found = strategy.tubes.detect do |tube|
49
+ strategy.tube_matches?(tube, expected)
50
+ end
51
+
52
+ return !found.nil?
53
+ end
54
+
55
+
56
+ # Builds the failure message used in the event of a negative expectation
57
+ # failure
58
+ def negative_failure_message
59
+ return '' if found.nil?
60
+ return [
61
+ "expected no tubes matching #{expected.to_s},",
62
+ "found #{strategy.pretty_print_tube(found)}",
63
+ ].join(' ')
64
+ end
65
+
66
+ end
@@ -0,0 +1,4 @@
1
+ module BeanCounter
2
+ # BeanCounter version number
3
+ VERSION = '0.0.1'
4
+ end
@@ -0,0 +1,16 @@
1
+ require 'coveralls'
2
+ Coveralls.wear_merged!
3
+
4
+ lib = File.expand_path('../../lib', __FILE__)
5
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
6
+
7
+ require 'rspec'
8
+ require 'mocha/api'
9
+ require 'bean_counter'
10
+ require 'bean_counter/spec'
11
+
12
+ BeanCounter.beanstalkd_url = 'beanstalk://localhost'
13
+
14
+ def client
15
+ return @client ||= Beaneater::Connection.new('localhost:11300')
16
+ end
@@ -0,0 +1,224 @@
1
+ require 'spec_helper'
2
+ require 'securerandom'
3
+
4
+ # Fuller testing of matchers handled by strategy and matcher tests. Just make
5
+ # sure matchers work as expected at high level
6
+ describe BeanCounter::SpecMatchers do
7
+
8
+ before(:each) do
9
+ BeanCounter.reset!
10
+ @tube_name = SecureRandom.uuid
11
+ client.transmit("use #{@tube_name}")
12
+ @message = SecureRandom.uuid
13
+ end
14
+
15
+
16
+ describe 'enqueued job matcher' do
17
+
18
+ describe 'positive match' do
19
+
20
+ it 'should match any matching job when not given a count' do
21
+ should_not have_enqueued(:body => @message)
22
+ proc do
23
+ 2.times do
24
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
25
+ end
26
+ end.should have_enqueued(:body => @message)
27
+ should have_enqueued(:body => @message)
28
+ end
29
+
30
+
31
+ it 'should match count exactly when given integer count' do
32
+ should_not have_enqueued(:body => @message)
33
+ proc do
34
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
35
+ end.should have_enqueued(:body => @message, :count => 1)
36
+ should have_enqueued(:body => @message, :count => 1)
37
+ end
38
+
39
+
40
+ it 'should match count to range when given range' do
41
+ should_not have_enqueued(:body => @message)
42
+ proc do
43
+ 2.times do
44
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
45
+ end
46
+ end.should have_enqueued(:body => @message, :count => 1..3)
47
+ should have_enqueued(:body => @message, :count => 1..3)
48
+ end
49
+
50
+
51
+ it 'should not match when no matching jobs are found' do
52
+ message = SecureRandom.uuid
53
+ ::RSpec::Expectations.expects(:fail_with)
54
+ proc do
55
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
56
+ end.should have_enqueued(:body => message)
57
+ should have_enqueued(:body => @message)
58
+ should_not have_enqueued(:body => message)
59
+ end
60
+
61
+
62
+ it 'should not match with exact count when given integer count' do
63
+ should_not have_enqueued(:body => @message)
64
+ ::RSpec::Expectations.expects(:fail_with)
65
+ proc do
66
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
67
+ end.should have_enqueued(:body => @message, :count => 2)
68
+ should have_enqueued(:body => @message, :count => 1)
69
+ end
70
+
71
+
72
+ it 'should not match when no matching jobs are enqueued during block' do
73
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
74
+ ::RSpec::Expectations.expects(:fail_with)
75
+ (proc {}).should have_enqueued(:body => @message)
76
+ should have_enqueued(:body => @message)
77
+ end
78
+
79
+ end
80
+
81
+
82
+ describe 'negative match' do
83
+
84
+ it 'should match when no matching jobs are found' do
85
+ message = SecureRandom.uuid
86
+ proc do
87
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
88
+ end.should_not have_enqueued(:body => message)
89
+ should have_enqueued(:body => @message)
90
+ should_not have_enqueued(:body => message)
91
+ end
92
+
93
+
94
+ it 'should match with exact count when given integer count' do
95
+ should_not have_enqueued(:body => @message)
96
+ proc do
97
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
98
+ end.should_not have_enqueued(:body => @message, :count => 2)
99
+ should have_enqueued(:body => @message, :count => 1)
100
+ end
101
+
102
+
103
+ it 'should match when no matching jobs are enqueued during block' do
104
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
105
+ (proc {}).should_not have_enqueued(:body => @message)
106
+ should have_enqueued(:body => @message)
107
+ end
108
+
109
+
110
+ it 'should not match when not given a count and job found' do
111
+ should_not have_enqueued(:body => @message)
112
+ ::RSpec::Expectations.expects(:fail_with)
113
+ proc do
114
+ 2.times do
115
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
116
+ end
117
+ end.should_not have_enqueued(:body => @message)
118
+ should have_enqueued(:body => @message)
119
+ end
120
+
121
+
122
+ it 'should not match when found count matches expected count' do
123
+ should_not have_enqueued(:body => @message)
124
+ ::RSpec::Expectations.expects(:fail_with)
125
+ proc do
126
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
127
+ end.should_not have_enqueued(:body => @message, :count => 1)
128
+ should have_enqueued(:body => @message, :count => 1)
129
+ end
130
+
131
+
132
+ it 'should not match when found count falls in expected range' do
133
+ should_not have_enqueued(:body => @message)
134
+ ::RSpec::Expectations.expects(:fail_with)
135
+ proc do
136
+ 2.times do
137
+ client.transmit("put 0 0 120 #{@message.bytesize}\r\n#{@message}")
138
+ end
139
+ end.should_not have_enqueued(:body => @message, :count => 1..3)
140
+ should have_enqueued(:body => @message, :count => 1..3)
141
+ end
142
+ end
143
+
144
+ end
145
+
146
+
147
+ describe 'tube matcher' do
148
+
149
+ describe 'positive match' do
150
+
151
+ it 'should match when matching tube found' do
152
+ client.transmit("put 0 0 120 2\r\nxx")
153
+ client.transmit("put 0 120 120 2\r\nxx")
154
+ client.transmit("pause-tube #{@tube_name} 0")
155
+
156
+ should have_tube({
157
+ 'name' => @tube_name,
158
+ 'cmd-pause' => 1..3,
159
+ 'current-jobs-ready' => 1,
160
+ 'current-jobs-delayed' => 1,
161
+ 'current-using' => 1,
162
+ })
163
+ end
164
+
165
+
166
+ it 'should not match when no matching tubes are found' do
167
+ should_not have_tube(:name => SecureRandom.uuid)
168
+
169
+ default_stats = client.transmit('stats-tube default')[:body]
170
+ urgent = default_stats['current-jobs-urgent']
171
+ should_not have_tube({
172
+ 'name' => 'default',
173
+ 'current-jobs-urgent' => (urgent + 5)..(urgent + 10),
174
+ })
175
+ watching = default_stats['current-watching']
176
+ ::RSpec::Expectations.expects(:fail_with)
177
+ should have_tube({
178
+ 'name' => 'default',
179
+ 'current-watching' => watching + 10,
180
+ })
181
+ end
182
+
183
+ end
184
+
185
+
186
+ describe 'negative match' do
187
+
188
+ it 'should match when no matching tubes are found' do
189
+ should_not have_tube(:name => SecureRandom.uuid)
190
+
191
+ default_stats = client.transmit('stats-tube default')[:body]
192
+ urgent = default_stats['current-jobs-urgent']
193
+ should_not have_tube({
194
+ 'name' => 'default',
195
+ 'current-jobs-urgent' => (urgent + 5)..(urgent + 10),
196
+ })
197
+ watching = default_stats['current-watching']
198
+ should_not have_tube({
199
+ 'name' => 'default',
200
+ 'current-watching' => watching + 10,
201
+ })
202
+ end
203
+
204
+
205
+ it 'should not match when matching tube found' do
206
+ client.transmit("put 0 0 120 2\r\nxx")
207
+ client.transmit("put 0 120 120 2\r\nxx")
208
+ client.transmit("pause-tube #{@tube_name} 0")
209
+
210
+ ::RSpec::Expectations.expects(:fail_with)
211
+ should_not have_tube({
212
+ 'name' => @tube_name,
213
+ 'cmd-pause' => 1..3,
214
+ 'current-jobs-ready' => 1,
215
+ 'current-jobs-delayed' => 1,
216
+ 'current-using' => 1,
217
+ })
218
+ end
219
+
220
+ end
221
+
222
+ end
223
+
224
+ end