qu-spec 0.1.2 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,4 +1,4 @@
1
- Copyright 2011 YOURNAME
1
+ Copyright 2011 Morton Jonuschat <yabawock@gmail.com>
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,3 +1,184 @@
1
1
  # QuSpec
2
2
 
3
- This project rocks and uses MIT-LICENSE.
3
+ ## Description
4
+
5
+ A QuSpec implements a set of matchers to help testing Qu interaction
6
+ with RSpec and Cucumber. The code has been inspired by
7
+ [ResqueSpec](https://leshill/resque_spec) but
8
+ thanks to the modular architecture of Qu most of the heavy lifting has
9
+ been implemented as an in-memory Qu::Backend implementation that
10
+ passes the Qu test suite for backends.
11
+
12
+ ## Installation
13
+
14
+ # Rails 3.x: add it to your Gemfile
15
+ group :test do
16
+ gem 'qu-spec'
17
+ end
18
+
19
+ ## Usage
20
+
21
+ ### Configuring QuSpec
22
+
23
+ To use QuSpec in your tests you need to setup Qu to use the included
24
+ Memory Backend. The easiest way to accomplish this is with the
25
+ `QuSpec.setup!` method:
26
+
27
+ RSpec.configure do |config|
28
+ config.before do
29
+ QuSpec.setup!
30
+ end
31
+ end
32
+
33
+ ### Writing tests
34
+
35
+ Given this scenario
36
+
37
+ Given a person
38
+ When I recalculate
39
+ Then the person has calculate queued
40
+
41
+ And I write this spec using the `qu-spec` matcher
42
+
43
+ describe "#recalculate" do
44
+ before do
45
+ QuSpec.reset!
46
+ end
47
+
48
+ it "adds person.calculate to the Person queue" do
49
+ person.recalculate
50
+ Person.should have_queued(person.id, :calculate)
51
+ end
52
+ end
53
+
54
+ (And I take note of the `before` block that is calling `reset!` for every spec)
55
+
56
+ And I might use the `in` statement to specify the queue:
57
+
58
+ describe "#recalculate" do
59
+ before do
60
+ QuSpec.reset!
61
+ end
62
+
63
+ it "adds person.calculate to the Person queue" do
64
+ person.recalculate
65
+ Person.should have_queued(person.id, :calculate).in(:people)
66
+ end
67
+ end
68
+
69
+ And I might write this as a Cucumber step
70
+
71
+ Then /the (\w?) has (\w?) queued/ do |thing, method|
72
+ thing_obj = instance_variable_get("@#{thing}")
73
+ thing_obj.class.should have_queued(thing_obj.id, method.to_sym)
74
+ end
75
+
76
+ Then I write some code to make it pass:
77
+
78
+ class Person
79
+ @queue = :people
80
+
81
+ def recalculate
82
+ Qu.enqueue(Person, id, :calculate)
83
+ end
84
+ end
85
+
86
+ You can check the size of the queue in your specs too.
87
+
88
+ describe "#recalculate" do
89
+ before do
90
+ QuSpec.reset!
91
+ end
92
+
93
+ it "adds an entry to the Person queue" do
94
+ person.recalculate
95
+ Person.should have_queue_size_of(1)
96
+ end
97
+ end
98
+
99
+ ## QuMailer with Specs
100
+
101
+ To use with [QuMailer](https://github.com/yabawock/qu-mailer) you should
102
+ have an initializer that does *not* exclude the `test` (or `cucumber`)
103
+ environment. Your initializer will probably end up looking like:
104
+
105
+ # config/initializers/qu_mailer.rb
106
+ Qu::Mailer.excluded_environments = []
107
+
108
+ ## QuScheduler with Specs
109
+
110
+ Support for [QuScheduler](https://github.com/yabawock/qu-scheduler) is integrated into QuSpec,
111
+ no additional setup is needed.
112
+
113
+ ### Writing tests
114
+
115
+ Given this scenario
116
+
117
+ Given a person
118
+ When I schedule a recalculate
119
+ Then the person has calculate scheduled
120
+
121
+ And I write this spec using the `qu-spec` matcher
122
+
123
+ describe "#recalculate" do
124
+ before do
125
+ QuSpec.reset!
126
+ end
127
+
128
+ it "adds person.calculate to the Person queue" do
129
+ person.recalculate
130
+ Person.should have_scheduled(person.id, :calculate)
131
+ end
132
+ end
133
+
134
+ (And I take note of the `before` block that is calling `reset!` for every spec)
135
+
136
+ *(There is also a **have_scheduled_at** matcher)*
137
+
138
+ And I might write this as a Cucumber step
139
+
140
+ Then /the (\w?) has (\w?) scheduled/ do |thing, method|
141
+ thing_obj = instance_variable_get("@#{thing}")
142
+ thing_obj.class.should have_scheduled(thing_obj.id, method.to_sym)
143
+ end
144
+
145
+ Then I write some code to make it pass:
146
+
147
+ class Person
148
+ @queue = :people
149
+
150
+ def recalculate
151
+ Qu.enqueue_at(Time.now + 3600, Person, id, :calculate)
152
+ end
153
+ end
154
+
155
+ ## Performing Jobs in Specs
156
+
157
+ QuSpec doesn't currently support performing jobs within tests. This is a
158
+ feature that might get implemented in a future release.
159
+
160
+ ## Note on Patches / Pull Requests
161
+
162
+ * Fork the project.
163
+ * Make your feature addition or bug fix.
164
+ * Add tests for it. This is important so I don't break it in a future version unintentionally.
165
+ * Commit, do not mess with rakefile, version, or history.
166
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
167
+ * Send me a pull request. Bonus points for topic branches.
168
+
169
+ ## Credits
170
+
171
+ This work has been heavily inspired by [ResqueSpec](https://github.com/leshill/resque_spec) by Les Hill.
172
+
173
+ ## Maintainers
174
+
175
+ * [Morton Jonuschat](https://github.com/yabawock)
176
+
177
+ ## License
178
+
179
+ MIT License
180
+
181
+ ## Copyright
182
+
183
+ Copyright 2011 Morton Jonuschat
184
+ Some parts copyright 2010-2011 Les Hill
@@ -1,5 +1,6 @@
1
1
  require 'qu/backend/memory'
2
2
  require 'qu-spec/matchers'
3
+ require 'qu-spec/scheduler'
3
4
 
4
5
  module QuSpec
5
6
  def self.setup!
@@ -62,3 +62,40 @@ RSpec::Matchers.define :have_queue_size_of do |size|
62
62
  "have a queue size of #{size}"
63
63
  end
64
64
  end
65
+
66
+ RSpec::Matchers.define :have_scheduled do |*expected_args|
67
+ match do |actual|
68
+ QuSpec.schedule_for(actual).any? { |entry| entry.klass.to_s == actual.to_s && entry.args == expected_args }
69
+ end
70
+
71
+ failure_message_for_should do |actual|
72
+ "expected that #{actual} would have [#{expected_args.join(', ')}] scheduled"
73
+ end
74
+
75
+ failure_message_for_should_not do |actual|
76
+ "expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled"
77
+ end
78
+
79
+ description do
80
+ "have scheduled arguments"
81
+ end
82
+ end
83
+
84
+ RSpec::Matchers.define :have_scheduled_at do |*expected_args|
85
+ match do |actual|
86
+ time = expected_args.shift
87
+ QuSpec.schedule_for(actual).any? { |entry| entry.klass.to_s == actual.to_s && entry.time == time && entry.args == expected_args }
88
+ end
89
+
90
+ failure_message_for_should do |actual|
91
+ "expected that #{actual} would have [#{expected_args.join(', ')}] scheduled"
92
+ end
93
+
94
+ failure_message_for_should_not do |actual|
95
+ "expected that #{actual} would not have [#{expected_args.join(', ')}] scheduled"
96
+ end
97
+
98
+ description do
99
+ "have scheduled at the given time the arguments"
100
+ end
101
+ end
@@ -0,0 +1,33 @@
1
+ module QuSpec
2
+ module SchedulerExtension
3
+ def enqueue_at(time, klass, *args)
4
+ QuSpec.enqueue_at(time, klass, *args)
5
+ end
6
+
7
+ def enqueue_in(time, klass, *args)
8
+ QuSpec.enqueue_in(time, klass, *args)
9
+ end
10
+
11
+ def remove_delayed(klass, *args)
12
+ QuSpec.remove_delayed(klass, *args)
13
+ end
14
+ end
15
+
16
+ def self.enqueue_at(time, klass, *args)
17
+ Qu.backend.delayed_push(time, Qu::Payload.new(:klass => klass, :args => args))
18
+ end
19
+
20
+ def self.enqueue_in(time, klass, *args)
21
+ enqueue_at(Time.now + time, klass, *args)
22
+ end
23
+
24
+ def self.remove_delayed(klass, *args)
25
+ Qu.backend.remove_delayed(klass, *args)
26
+ end
27
+
28
+ def self.schedule_for(klass)
29
+ Qu.backend.get_schedule_by_klass(klass)
30
+ end
31
+ end
32
+
33
+ Qu.send :extend, QuSpec::SchedulerExtension
@@ -1,3 +1,3 @@
1
1
  module QuSpec
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -57,6 +57,20 @@ module Qu
57
57
  payload
58
58
  end
59
59
 
60
+ def delayed_push(time, payload)
61
+ payload.id = SimpleUUID::UUID.new.to_guid
62
+ payload.time = time
63
+ @queues[scheduled_queue_name(payload.klass)] << payload
64
+ logger.debug { "Enqueued delayed job #{payload}" }
65
+ payload
66
+ end
67
+
68
+ def remove_delayed(klass, *args)
69
+ get_queue_by_name(scheduled_queue_name(klass)).delete_if do |payload|
70
+ payload.klass.to_s == klass.to_s && payload.args == args
71
+ end
72
+ end
73
+
60
74
  def reserve(worker, options = {:block => true})
61
75
  loop do
62
76
  worker.queues.each do |queue|
@@ -94,6 +108,16 @@ module Qu
94
108
  payload = Payload.new(:klass => klass)
95
109
  get_queue_by_name(payload.queue)
96
110
  end
111
+
112
+ def get_schedule_by_klass(klass)
113
+ get_queue_by_name(scheduled_queue_name(klass))
114
+ end
115
+
116
+ private
117
+
118
+ def scheduled_queue_name(klass)
119
+ "#{klass.to_s}_scheduled"
120
+ end
97
121
  end
98
122
  end
99
123
  end
@@ -74,4 +74,20 @@ describe "QuSpec Matchers" do
74
74
  it { should_not have_queue_size_of(1).in('other_people') }
75
75
  end
76
76
  end
77
+
78
+ describe "#have_scheduled_at" do
79
+ let(:scheduled_at) { Time.now + 5 * 60 }
80
+
81
+ before do
82
+ Qu.enqueue_at(scheduled_at, Person, first_name, last_name)
83
+ end
84
+
85
+ it "returns true if the arguments are found in the queue" do
86
+ Person.should have_scheduled_at(scheduled_at, first_name, last_name)
87
+ end
88
+
89
+ it "returns false if the arguments are not found in the queue" do
90
+ Qu.should_not have_scheduled_at(scheduled_at, last_name, first_name)
91
+ end
92
+ end
77
93
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: qu-spec
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -13,7 +13,7 @@ date: 2011-12-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: qu
16
- requirement: &70160136907080 !ruby/object:Gem::Requirement
16
+ requirement: &70131682168640 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 0.1.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70160136907080
24
+ version_requirements: *70131682168640
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70160136906420 !ruby/object:Gem::Requirement
27
+ requirement: &70131682167960 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ! '>='
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 2.5.0
33
33
  type: :runtime
34
34
  prerelease: false
35
- version_requirements: *70160136906420
35
+ version_requirements: *70131682167960
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: simple_uuid
38
- requirement: &70160136905640 !ruby/object:Gem::Requirement
38
+ requirement: &70131682167180 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ! '>='
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 0.2.0
44
44
  type: :runtime
45
45
  prerelease: false
46
- version_requirements: *70160136905640
46
+ version_requirements: *70131682167180
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: rake
49
- requirement: &70160136904880 !ruby/object:Gem::Requirement
49
+ requirement: &70131682165160 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ! '>='
@@ -54,7 +54,7 @@ dependencies:
54
54
  version: '0'
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *70160136904880
57
+ version_requirements: *70131682165160
58
58
  description: RSpec matchers and an in-memory backend for Qu
59
59
  email:
60
60
  - yabawock@gmail.com
@@ -64,6 +64,7 @@ extra_rdoc_files: []
64
64
  files:
65
65
  - lib/qu/backend/memory.rb
66
66
  - lib/qu-spec/matchers.rb
67
+ - lib/qu-spec/scheduler.rb
67
68
  - lib/qu-spec/version.rb
68
69
  - lib/qu-spec.rb
69
70
  - MIT-LICENSE
@@ -87,7 +88,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
87
88
  version: '0'
88
89
  segments:
89
90
  - 0
90
- hash: 4087639463335953036
91
+ hash: 1380140734345455011
91
92
  required_rubygems_version: !ruby/object:Gem::Requirement
92
93
  none: false
93
94
  requirements:
@@ -96,7 +97,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
96
97
  version: '0'
97
98
  segments:
98
99
  - 0
99
- hash: 4087639463335953036
100
+ hash: 1380140734345455011
100
101
  requirements: []
101
102
  rubyforge_project:
102
103
  rubygems_version: 1.8.10