clockwork-test 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 9e5400764eb671ac7bd63e5fbd30ffd6d173f65d
4
+ data.tar.gz: e73c3fa5290e7683b17f89b7e0f1b3c206a56bdb
5
+ SHA512:
6
+ metadata.gz: 48a656be5270cfac4ea3821b8fa55f8db3c67fc2abca37147fd7eefe3105110c90849447d5cb688549dd6b38a38a6e1ad1ad2ae9e3b6611d157f82296263331a
7
+ data.tar.gz: 82fdef73b35c494bfc44a3239c94affca04e4ded03d0627576c6591e55a7e9e8d8b10e4a9e7fbbfeeadf800c5e53fed87dc6fc78a6af187599d79b8689c3efa4
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,14 @@
1
+ sudo: false
2
+ language: ruby
3
+ cache: bundler
4
+ rvm:
5
+ - 1.9.3
6
+ - 2.0
7
+ - 2.1
8
+ - 2.2
9
+ - ruby-head
10
+ script:
11
+ - bundle exec rspec
12
+ matrix:
13
+ allow_failures:
14
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in clockwork-test.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Kevin Murphy
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,190 @@
1
+ # Clockwork::Test
2
+
3
+ [![Build Status](https://travis-ci.org/kevin-j-m/clockwork-test.svg?branch=master)](https://travis-ci.org/kevin-j-m/clockwork-test)
4
+
5
+ [Clockwork](https://rubygems.org/gems/clockwork) is a scheduler process for running scheduled jobs. These scheduled jobs are likely of critical importance to your application. You need to ensure that the jobs run when they should, as often as they should, and with the proper behavior when run.
6
+
7
+ `Clockwork::Test` includes additional functionality that makes testing of your `clock.rb` file easy. This gem can help make sure that you have scheduled your events appropriately, including testing `if:` or `at:` conditions, as well as allowing you to run assertions against the code that is executed when a job is run.
8
+
9
+ `Clockwork::Test` has been verified against the latest release of clockwork at the time of its development, which is version 1.2.0. It does not require any specific test framework to be used with it, though all examples will use `rspec`.
10
+
11
+ ## Installation
12
+
13
+ Add this to your application's Gemfile:
14
+
15
+ ```ruby
16
+ group :test do
17
+ gem 'clockwork-test'
18
+ end
19
+ ```
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install clockwork-test
28
+
29
+ ## Example Tests
30
+
31
+ Given the following `clock.rb` file:
32
+
33
+ ```ruby
34
+ require "clockwork"
35
+
36
+ module Clockwork
37
+ configure do |config|
38
+ config[:tz] = "US/Eastern"
39
+ end
40
+
41
+ handler { |job| logger.info "Running #{job}" }
42
+
43
+ every(1.minute, "Run a job") do
44
+ "Here's a running job"
45
+ end
46
+ end
47
+ ```
48
+
49
+ The following tests may be run against it:
50
+
51
+ ```ruby
52
+ describe Clockwork do
53
+ after(:each) { Clockwork::Test.clear! }
54
+
55
+ it "runs the job once" do
56
+ Clockwork::Test.run(max_ticks: 1)
57
+
58
+ expect(Clockwork::Test.ran_job?("Run a job")).to be_truthy
59
+ expect(Clockwork::Test.times_run("Run a job")).to eq 1
60
+ expect(Clockwork::Test.block_for("Run a job").call).to eq "Here's a running job"
61
+ end
62
+
63
+ it "runs the job every minute over the course of an hour" do
64
+ start_time = Time.new(2015, 11, 2, 2, 0, 0)
65
+ end_time = Time.new(2015, 11, 2, 3, 0, 0)
66
+
67
+ Clockwork::Test.run(start_time: start_time, end_time: end_time, tick_speed:
68
+ 1.minute)
69
+
70
+ expect(Clockwork::Test.times_run("Run a job").to eq 60
71
+ end
72
+
73
+ describe "RSpec Custom Matcher" do
74
+ subject(:clockwork) { Clockwork::Test }
75
+
76
+ before { Clockwork::Test.run(max_ticks: 1) }
77
+
78
+ it { should have_run("Run a job") }
79
+ it { should have_run("Run a job").once }
80
+
81
+ it { should_not have_run("Run a job").exactly(2).times }
82
+ end
83
+ end
84
+ ```
85
+
86
+ ## Usage
87
+
88
+ Replacing any calls to `Clockwork` with `Clockwork::Test` in your tests should perform as expected, with the one caveat that a job running in `Clockwork::Test` will not actually execute its event's handler code. That is suppressed, but made available via the `block_for` method.
89
+
90
+ ### Running Clockwork in Tests
91
+
92
+ A call to `Clockwork.run` will run the clockwork process until it is signaled to stop, such as by receiving the `SIGINT` signal. `Clockwork::Test.run` provides two ways to easily stop the current execution:
93
+
94
+ 1. After a particular number of clock ticks (See the `max_ticks` configuration setting).
95
+ 3. After a moment in time has been reached (See the `end_time` configuration setting).
96
+
97
+ Either of these methods can be used by passing the proper configuration option into the `run` method of `Clockwork::Test`. If both are provided, whichever occurs first will be used to halt execution.
98
+
99
+ ### Configuration Settings
100
+
101
+ #### max_ticks
102
+
103
+ Setting the `max_ticks` will determine the number of times that clockwork will run any events scheduled at that time. This is commonly used to let clockwork execute once, to then see if particular jobs are triggered right when clockwork starts up.
104
+
105
+ #### start_time
106
+
107
+ This is the time that you would like the clock file to start processing it. This may be used with either the `max_ticks` or `end_time` option to halt execution of the run loop.
108
+
109
+ If the `start_time` option is used, `Time.now` will be modified to the `start_time` at the beginning of execution of `Clockwork::Test.run`, and will be returned to the proper time at the conclusion of the run.
110
+
111
+ #### end_time
112
+
113
+ This specifies the time at which execution of clockwork should terminate. The run loop will continue until the `end_time` is reached, provided the `max_ticks` has not been exceeded, if that is provided.
114
+
115
+ This is commonly used along with `start_time` and `tick_speed` to quickly see if jobs run as often as expected over a certain period of time.
116
+
117
+ #### tick_speed
118
+
119
+ `tick_speed` should be provided as a unit of time, such as `1.minute` or `3.hours`. It is the amount of time that the test will progress time to on every subsequent clock tick.
120
+
121
+ If `tick_speed` is set to `1.minute` and the current time is `13:00`, the first tick will occur at `13:00`, and the second will appear occur at `13:01`.
122
+
123
+ This can be used in concert with `start_time` and `end_time` to quickly simulate moving across a large period of time, without having to wait for the full time period to occur in reality.
124
+
125
+ Note that `tick_speed` should not be set any higher than the minimum amount of granularity used in the clock file under test. Should the `tick_speed` be set to a higher amount, the number of times a job is run, or if the job is run at all, is subject to inconsistent and inaccurate results.
126
+
127
+ For example, with a `tick_speed` of `30.minutes` and a job that runs every `1.minute` over the course of an hour, the `times_ran` for that job will *not* be 60. It will be 2, assuming minimal passage of time in evaluating the actual clock tick.
128
+
129
+ #### file
130
+
131
+ The location of the clock file to test. This defaults to `"./config/clock.rb"` if a value is not provided.
132
+
133
+ ### Assertions Against Test Runs
134
+
135
+ After running your clock file with `Clockwork::Test.run`, you can use the following methods to assert the clock file ran as expected:
136
+
137
+ #### ran_job?
138
+
139
+ `ran_job?` lets you know if a particular job ran during previous executions of `Clockwork::Test.run`.
140
+
141
+ If the job "test_job" has run, `ran_job?("test_job")` will be true; otherwise, the method will return false.
142
+
143
+ #### times_run
144
+
145
+ `times_run` provides the number of times a job has run during previous executions of `Clockwork::Test.run`.
146
+
147
+ If the job "test_job" never ran, `times_run("test_job")` will be 0; otherwise, it will be the number of times that the event was executed.
148
+
149
+ #### block_for
150
+
151
+ `block_for` returns the block that would be handled and run on each execution of a job. This can be useful for ensuring that the block you associated with an event does whatever it is that you expect it to do.
152
+
153
+ If the job "test_job" never ran, `block_for("test_job")` will return an empty proc; otherwise, the block of code that the event would run is returned, which can then be `call`ed to test.
154
+
155
+ ### RSpec Matchers
156
+
157
+ `Clockwork::Test` includes a rspec test matcher to check if a job has been run.
158
+
159
+ To use, add the following to your `spec_helper.rb`:
160
+
161
+ ```ruby
162
+ RSpec.configure do |config|
163
+ config.include(Clockwork::Test::RSpec::Matchers)
164
+ end
165
+ ```
166
+
167
+ The `have_run` matcher checks if a job by the name provided has been run, and allows for a specific number of times to be checked against.
168
+
169
+ `expect(Clockwork::Test).to have_run("Job Name").exactly(42).times`
170
+
171
+ You can alternatively pass the number of times in as an optional argument in the `have_run` method, as such:
172
+
173
+ `expect(Clockwork::Test).to have_run("Job Name", times: 42)`
174
+
175
+ Chaining `once` to `have_run` will check that the job was run one time only.
176
+
177
+ If you do not pass the number of times, either with a chained method call or as an optional argument to `have_run`, the matcher will just check if the job was run at all. It provides no assertion as to how often it was run, just that it's at least once.
178
+
179
+ ### Resetting the clock
180
+
181
+ After each invocation of `Clockwork::Test.run` and assertions made against it, `Clockwork::Test.clear!` should be called. This will wipe all history away and load up the clock file fresh on the next `run`. Failure to do so across tests will carry over past history, which will cause the number of times a job has run to be incorrect and subject to a test ordering bug.
182
+
183
+ ## Contributing
184
+
185
+ 1. Fork it ( https://github.com/kevin-j-m/clockwork-test/fork )
186
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
187
+ 3. Run the tests (`rspec`) and ensure they pass
188
+ 4. Commit your changes (`git commit -am 'Add some feature'`)
189
+ 5. Push to the branch (`git push origin my-new-feature`)
190
+ 6. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'clockwork/test/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "clockwork-test"
8
+ spec.version = Clockwork::Test::VERSION
9
+ spec.authors = ["Kevin Murphy"]
10
+ spec.email = ["murphy.kevin.mail@gmail.com"]
11
+ spec.summary = "Test clockwork scheduled jobs"
12
+ spec.description = "Run clockwork jobs in a test environment and assert jobs are run as expected"
13
+ spec.homepage = "https://github.com/kevin-j-m/clockwork-test"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "pry"
23
+ spec.add_development_dependency "rake", "~> 10.0"
24
+ spec.add_development_dependency "rspec"
25
+ spec.add_development_dependency "rspec-its"
26
+
27
+ spec.add_runtime_dependency "clockwork"
28
+ spec.add_runtime_dependency "timecop"
29
+ end
@@ -0,0 +1,12 @@
1
+ module Clockwork
2
+ module Test
3
+ class Event < Clockwork::Event
4
+ attr_reader :block
5
+
6
+ private
7
+
8
+ def execute
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,40 @@
1
+ module Clockwork
2
+ module Test
3
+ class JobHistory
4
+ def initialize(prior_history = {}, prior_work = {})
5
+ @history = prior_history
6
+ @work_done = prior_work
7
+ end
8
+
9
+ def jobs
10
+ history.keys
11
+ end
12
+
13
+ def ran_job?(job)
14
+ jobs.include?(job)
15
+ end
16
+
17
+ def times_run(job)
18
+ history[job] || 0
19
+ end
20
+
21
+ def block_for(job)
22
+ work_done[job] || Proc.new {}
23
+ end
24
+
25
+ def record(new_events)
26
+ new_events.each do |event|
27
+ job = event.job
28
+
29
+ prior_runs = times_run(job)
30
+ history[job] = prior_runs > 0 ? prior_runs + 1 : 1
31
+ work_done[job] = event.block
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :history, :work_done
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,89 @@
1
+ module Clockwork
2
+ module Test
3
+ class Manager < Clockwork::Manager
4
+ attr_reader :total_ticks, :max_ticks, :end_time
5
+
6
+ def initialize(opts = {})
7
+ super()
8
+ @history = JobHistory.new
9
+
10
+ @total_ticks = 0
11
+ @max_ticks = opts[:max_ticks]
12
+ @end_time = opts[:end_time]
13
+ config[:logger].level = Logger::ERROR
14
+ end
15
+
16
+ def run(opts = {})
17
+ @max_ticks = opts[:max_ticks] if opts[:max_ticks]
18
+ @end_time = opts[:end_time] if opts[:end_time]
19
+ @tick_speed = opts[:tick_speed]
20
+
21
+ if opts[:start_time]
22
+ @time_altered = true
23
+ Timecop.travel(opts[:start_time])
24
+ end
25
+
26
+ super()
27
+
28
+ Timecop.return if @time_altered
29
+ end
30
+
31
+ def ran_job?(job)
32
+ history.ran_job?(job)
33
+ end
34
+
35
+ def times_run(job)
36
+ history.times_run(job)
37
+ end
38
+
39
+ def block_for(job)
40
+ history.block_for(job)
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :history
46
+
47
+ def loop(&block)
48
+ while 1 == 1 do
49
+ update_job_history
50
+
51
+ block.call
52
+
53
+ @total_ticks += 1
54
+ break if ticks_exceeded? || time_exceeded?
55
+ end
56
+ end
57
+
58
+ def sleep(interval)
59
+ if @tick_speed
60
+ Timecop.travel(Time.now + @tick_speed)
61
+ else
62
+ super
63
+ end
64
+ end
65
+
66
+ def register(period, job, block, options)
67
+ event = ::Clockwork::Test::Event.new(self, period, job, block || handler, options)
68
+ @events << event
69
+ event
70
+ end
71
+
72
+ def update_job_history
73
+ history.record(events_run_now)
74
+ end
75
+
76
+ def events_run_now
77
+ events_to_run(Time.now)
78
+ end
79
+
80
+ def ticks_exceeded?
81
+ max_ticks && total_ticks >= max_ticks
82
+ end
83
+
84
+ def time_exceeded?
85
+ end_time && Time.now >= end_time
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,67 @@
1
+ module Clockwork
2
+ module Test
3
+ module RSpec
4
+ module Matchers
5
+ class HaveRun
6
+ def initialize(job_name = nil, opts = {})
7
+ @job_name = job_name
8
+ @times_run = opts[:times] || 1
9
+ @exactly = false
10
+ end
11
+
12
+ def matches?(clock_test)
13
+ if @exactly
14
+ clock_test.manager.times_run(@job_name) == @times_run
15
+ else
16
+ clock_test.manager.ran_job?(@job_name)
17
+ end
18
+ end
19
+
20
+ def once
21
+ @times_run = 1
22
+ @exactly = true
23
+
24
+ self
25
+ end
26
+
27
+ def exactly(times)
28
+ @times_run = times
29
+ @exactly = true
30
+
31
+ self
32
+ end
33
+
34
+ def times(times = nil)
35
+ if times
36
+ @times_run = times
37
+ @exactly = true
38
+ end
39
+
40
+ self
41
+ end
42
+
43
+ def time(times = nil)
44
+ if times
45
+ @times_run = times
46
+ @exactly = true
47
+ end
48
+
49
+ self
50
+ end
51
+
52
+ def description
53
+ "run \"#{@job_name}\" #{@times_run} #{@times_run == 1 ? 'time' : 'times'}"
54
+ end
55
+
56
+ def failure_message
57
+ "expected Clockwork::Test to have " + description
58
+ end
59
+
60
+ def failure_message_when_negated
61
+ "expected Clockwork::Test to not have run \"#{@job_name}\"."
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,13 @@
1
+ require "clockwork/test/rspec/matchers/have_run"
2
+
3
+ module Clockwork
4
+ module Test
5
+ module RSpec
6
+ module Matchers
7
+ def have_run(job, opts = {})
8
+ Matchers::HaveRun.new(job, opts)
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ module Clockwork
2
+ module Test
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,74 @@
1
+ require "clockwork"
2
+ require "timecop"
3
+
4
+ require "clockwork/test/event"
5
+ require "clockwork/test/job_history"
6
+ require "clockwork/test/manager"
7
+ require "clockwork/test/version"
8
+ require "clockwork/test/rspec/matchers"
9
+
10
+ module Clockwork
11
+ module Methods
12
+ def every(period, job, options={}, &block)
13
+ ::Clockwork.manager.every(period, job, options, &block)
14
+ ::Clockwork::Test.manager.every(period, job, options, &block)
15
+ end
16
+ end
17
+
18
+ module Test
19
+ class << self
20
+ def included(klass)
21
+ klass.send "include", Methods
22
+ klass.extend Methods
23
+
24
+ klass.send "include", ::Clockwork::Methods
25
+ klass.extend ::Clockwork::Methods
26
+ end
27
+
28
+ def manager
29
+ @manager ||= Clockwork::Test::Manager.new
30
+ end
31
+
32
+ def manager=(manager)
33
+ @manager = manager
34
+ end
35
+ end
36
+
37
+ module Methods
38
+ def run(opts = {})
39
+ file = opts[:file] || "./config/clock.rb"
40
+
41
+ run_opts = {
42
+ max_ticks: opts[:max_ticks],
43
+ start_time: opts[:start_time],
44
+ end_time: opts[:end_time],
45
+ tick_speed: opts[:tick_speed]
46
+ }
47
+
48
+ # TODO parse file rather than loading it
49
+ # and overloading Clockwork::Methods::every
50
+ load file
51
+
52
+ manager.run(run_opts)
53
+ end
54
+
55
+ def clear!
56
+ Clockwork::Test.manager = Clockwork::Test::Manager.new
57
+ end
58
+
59
+ def ran_job?(job)
60
+ manager.ran_job?(job)
61
+ end
62
+
63
+ def times_run(job)
64
+ manager.times_run(job)
65
+ end
66
+
67
+ def block_for(job)
68
+ manager.block_for(job)
69
+ end
70
+ end
71
+
72
+ extend Methods, ::Clockwork::Methods
73
+ end
74
+ end
@@ -0,0 +1,50 @@
1
+ require "spec_helper"
2
+
3
+ describe "Clockwork" do
4
+ let(:clock_file) { "spec/fixtures/clock.rb" }
5
+ let(:test_job_name) { "Run a job" }
6
+
7
+ after(:each) { Clockwork::Test.clear! }
8
+
9
+ describe "Run a job" do
10
+ before { Clockwork::Test.run(file: clock_file, max_ticks: 1) }
11
+
12
+ it "runs the test job in the file" do
13
+ expect(Clockwork::Test.ran_job?(test_job_name)).to be_truthy
14
+ end
15
+
16
+ it "knows the job ran a single time" do
17
+ expect(Clockwork::Test.times_run(test_job_name)).to eq 1
18
+ end
19
+
20
+ it "retains a record of the work that the job would have done" do
21
+ expect(Clockwork::Test.block_for(test_job_name).call).to eq "Here's a running job"
22
+ end
23
+ end
24
+
25
+ describe "Custom matchers" do
26
+ subject(:clockwork) { Clockwork::Test }
27
+
28
+ before { Clockwork::Test.run(file: clock_file, max_ticks: 1) }
29
+
30
+ it { should have_run(test_job_name) }
31
+ it { should have_run(test_job_name).once }
32
+
33
+ it { should_not have_run(test_job_name).exactly(2).times }
34
+
35
+ it "works as an explicit expectation as well" do
36
+ expect(Clockwork::Test).to have_run(test_job_name)
37
+ expect(Clockwork::Test).to_not have_run(test_job_name).exactly(2).times
38
+ end
39
+
40
+ context "running multiple times" do
41
+ before { Clockwork::Test.run(file: clock_file, max_ticks: 2, tick_speed: 1.minute) }
42
+
43
+ it { should have_run(test_job_name).exactly(2).times }
44
+ it { should have_run(test_job_name) }
45
+ it { should have_run(test_job_name).times(2) }
46
+ it { should have_run(test_job_name).exactly(2) }
47
+ it { should have_run(test_job_name, times: 2) }
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,96 @@
1
+ require "spec_helper"
2
+ require "clockwork/test/job_history"
3
+
4
+ describe Clockwork::Test::JobHistory do
5
+ subject(:history) { Clockwork::Test::JobHistory.new(prior_history, prior_work) }
6
+ let(:prior_history) { {} }
7
+ let(:prior_work) { {} }
8
+
9
+ describe "#jobs" do
10
+ let(:prior_history) do
11
+ {
12
+ "#{first_job}" => 1,
13
+ "#{second_job}" => 1
14
+ }
15
+ end
16
+
17
+ let(:first_job) { "job1" }
18
+ let(:second_job) { "job2" }
19
+
20
+ its(:jobs) { should include first_job, second_job }
21
+ end
22
+
23
+ describe "#ran_job?" do
24
+ let(:prior_history) { { "#{first_job}" => 1 } }
25
+ let(:first_job) { "job1" }
26
+ let(:second_job) { "job2" }
27
+
28
+ it "finds history of a job that ran" do
29
+ expect(history.ran_job?(first_job)).to be_truthy
30
+ end
31
+
32
+ it "does not find history of a job never run" do
33
+ expect(history.ran_job?(second_job)).to be_falsey
34
+ end
35
+ end
36
+
37
+ describe "#times_run" do
38
+ let(:prior_history) { { "#{first_job}" => number_of_first_job_runs } }
39
+ let(:first_job) { "job1" }
40
+ let(:number_of_first_job_runs) { 42 }
41
+ let(:second_job) { "job2" }
42
+
43
+ it "finds the number of times a previously job has run" do
44
+ expect(history.times_run(first_job)).to eq number_of_first_job_runs
45
+ end
46
+
47
+ it "finds no runs for a job it has no history of" do
48
+ expect(history.times_run(second_job)).to eq 0
49
+ end
50
+ end
51
+
52
+ describe "#block_for" do
53
+ let(:prior_work) { { "#{job}" => block } }
54
+ let(:job) { "job" }
55
+ let(:block) { Proc.new { "Running #{job}" } }
56
+
57
+ it "recalls the block that was run for a previously run job" do
58
+ expect(history.block_for(job).call).to eq "Running #{job}"
59
+ end
60
+
61
+ it "does nothing for a job that was not run" do
62
+ expect(history.block_for("not_run").call).to be_nil
63
+ end
64
+ end
65
+
66
+ describe "#record" do
67
+ let(:events) { [first_event, second_event] }
68
+
69
+ let(:first_event) { double(job: first_job, block: first_block) }
70
+ let(:first_job) { "job1" }
71
+ let(:first_block) { Proc.new { "Running #{first_job}" } }
72
+
73
+ let(:second_event) { double(job: second_job, block: second_block) }
74
+ let(:second_job) { "job2" }
75
+ let(:second_block) { Proc.new {} }
76
+
77
+ before { history.record(events) }
78
+
79
+ it "keeps a record of the job occurring" do
80
+ expect(history.jobs).to include first_job, second_job
81
+ end
82
+
83
+ it "knows the work the job would perform" do
84
+ expect(history.block_for(first_job).call).to eq "Running #{first_job}"
85
+ end
86
+
87
+ context "recording jobs that already ran" do
88
+ let(:prior_history) { { "#{first_job}" => 1 } }
89
+
90
+ it "increments the counter of the job already recording and adds a new entry for the job never recorded prior" do
91
+ expect(history.times_run(first_job)).to eq 2
92
+ expect(history.times_run(second_job)).to eq 1
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,176 @@
1
+ require "spec_helper"
2
+ require "clockwork/test/manager"
3
+
4
+ describe Clockwork::Test::Manager do
5
+ subject(:manager) { Clockwork::Test::Manager.new(opts) }
6
+ let(:opts) { {} }
7
+
8
+ context "initial state" do
9
+ its(:total_ticks) { should eq 0 }
10
+ its(:max_ticks) { should be_nil }
11
+ its(:end_time) { should be_nil }
12
+
13
+ context "providing a maximum number of ticks" do
14
+ let(:opts) { { max_ticks: max_ticks } }
15
+ let(:max_ticks) { 42 }
16
+
17
+ its(:max_ticks) { should eq max_ticks }
18
+ end
19
+
20
+ context "providing a time the clock should stop" do
21
+ let(:opts) { { end_time: end_time } }
22
+ let(:end_time) { Time.current }
23
+
24
+ its(:end_time) { should eq end_time }
25
+ end
26
+ end
27
+
28
+ describe "#run" do
29
+ context "limiting the number of ticks" do
30
+ let(:opts) { { max_ticks: max_ticks } }
31
+ let(:max_ticks) { 1 }
32
+
33
+ it "only runs the number of times specified" do
34
+ manager.run
35
+ expect(manager.total_ticks).to eq 1
36
+ end
37
+
38
+ context "specifying number of ticks in the method invocation" do
39
+ let(:method_max_ticks) { 3 }
40
+
41
+ it "runs the number of times specified in the run call" do
42
+ manager.run(max_ticks: method_max_ticks)
43
+ expect(manager.total_ticks).to eq method_max_ticks
44
+ end
45
+ end
46
+ end
47
+
48
+ context "limiting the time the clock should run" do
49
+ let(:opts) { { end_time: end_time } }
50
+ let(:end_time) { Time.current + 2.seconds }
51
+
52
+ it "stops running after reaching the end time" do
53
+ manager.run
54
+ expect(Time.current).to be_within(1.second).of(end_time)
55
+ expect(manager.total_ticks).to be > 0
56
+ end
57
+
58
+ context "specifying the end time in the method invocation" do
59
+ let(:method_end_time) { Time.current + 4.seconds }
60
+
61
+ it "stops running after reaching the end time specified in the call" do
62
+ manager.run(end_time: method_end_time)
63
+ expect(Time.current).to be_within(1.second).of(method_end_time)
64
+ expect(manager.total_ticks).to be > 0
65
+ end
66
+ end
67
+ end
68
+
69
+ context "recording job history" do
70
+ let(:opts) { { max_ticks: max_ticks } }
71
+ let(:max_ticks) { 1 }
72
+ let(:job_name) { "Test job" }
73
+
74
+ before do
75
+ manager.handler { }
76
+ manager.every(1.minute, job_name)
77
+ manager.run
78
+ end
79
+
80
+ it "knows the job has run" do
81
+ expect(manager.ran_job?(job_name)).to be_truthy
82
+ end
83
+
84
+ it "finds a single run of the job" do
85
+ expect(manager.times_run(job_name)).to eq 1
86
+ end
87
+ end
88
+
89
+ context "modifying when the test is run" do
90
+ let(:opts) { { max_ticks: max_ticks } }
91
+ let(:max_ticks) { 1 }
92
+ let(:job_name) { "Test job" }
93
+ let(:start_time) { Time.new(2000) }
94
+
95
+ before do
96
+ manager.handler { }
97
+ manager.every(1.minute, job_name, if: lambda { |t| t.year == 2000 })
98
+ manager.run(start_time: start_time)
99
+ end
100
+
101
+ it "runs the jobs at the time specified" do
102
+ expect(manager.ran_job?(job_name)).to be_truthy
103
+ end
104
+
105
+ it "resets time at the conclusion of the run" do
106
+ expect(Time.now.year).to be > 2000
107
+ end
108
+ end
109
+
110
+ context "speeding up the test run" do
111
+ let(:start_time) { Time.new(2015, 11, 2, 2, 0, 0) }
112
+ let(:end_time) { Time.new(2015, 11, 2, 3, 0, 0) }
113
+ let(:tick_speed) { 1.minute }
114
+ let(:job_name) { "Test job" }
115
+
116
+ before do
117
+ manager.handler { }
118
+ manager.every(1.minute, job_name)
119
+ manager.run(start_time: start_time, end_time: end_time, tick_speed: tick_speed)
120
+ end
121
+
122
+ it "runs the job as often as expected" do
123
+ expect(manager.times_run(job_name)).to eq 60
124
+ end
125
+
126
+ context "speeding up time" do
127
+ let(:tick_speed) { 30.minutes }
128
+ it "runs the job fewer times than it otherwise would" do
129
+ expect(manager.times_run(job_name)).to be < 60
130
+ end
131
+ end
132
+ end
133
+ end
134
+
135
+ describe "#ran_job?" do
136
+ let(:history) { double("history") }
137
+ let(:job) { double("job") }
138
+
139
+ it "determines if the job has run from the job history" do
140
+ allow(manager).to receive(:history).and_return(history)
141
+
142
+ expect(history).to receive(:ran_job?).with(job)
143
+ manager.ran_job?(job)
144
+ end
145
+ end
146
+
147
+ describe "#times_run" do
148
+ let(:history) { double("history") }
149
+ let(:job) { double("job") }
150
+
151
+ it "determines the number of times the job has run from the history" do
152
+ allow(manager).to receive(:history).and_return(history)
153
+
154
+ expect(history).to receive(:times_run).with(job)
155
+ manager.times_run(job)
156
+ end
157
+ end
158
+
159
+ describe "#block_for" do
160
+ let(:opts) { { max_ticks: max_ticks } }
161
+ let(:max_ticks) { 1 }
162
+ let(:job_name) { "Test job" }
163
+ before do
164
+ manager.every(1.minute, job_name) { "Running #{job_name}" }
165
+ manager.run
166
+ end
167
+
168
+ it "recalls the block that was run for a previously run job" do
169
+ expect(manager.block_for(job_name).call).to eq "Running #{job_name}"
170
+ end
171
+
172
+ it "does nothing for a job that was not run" do
173
+ expect(manager.block_for("not_run").call).to be_nil
174
+ end
175
+ end
176
+ end
@@ -0,0 +1,28 @@
1
+ require "spec_helper"
2
+
3
+ describe Clockwork::Test do
4
+ let(:clock_file) { "spec/fixtures/clock.rb" }
5
+ let(:test_job_name) { "Run a job" }
6
+ let(:test_job_output) { "Here's a running job" }
7
+
8
+ it "has a version number" do
9
+ expect(Clockwork::Test::VERSION).not_to be nil
10
+ end
11
+
12
+ describe ".run" do
13
+ before { Clockwork::Test.run(file: clock_file, max_ticks: 1) }
14
+ after { Clockwork::Test.clear! }
15
+
16
+ it "runs the test job in the file" do
17
+ expect(Clockwork::Test.ran_job?(test_job_name)).to be_truthy
18
+ end
19
+
20
+ it "knows the job ran a single time" do
21
+ expect(Clockwork::Test.times_run(test_job_name)).to eq 1
22
+ end
23
+
24
+ it "retains a record of the work that the job would have done" do
25
+ expect(Clockwork::Test.block_for(test_job_name).call).to eq test_job_output
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,13 @@
1
+ require "clockwork"
2
+
3
+ module Clockwork
4
+ configure do |config|
5
+ config[:tz] = "US/Eastern"
6
+ end
7
+
8
+ handler { |job| logger.info "Running #{job}" }
9
+
10
+ every(1.minute, "Run a job") do
11
+ "Here's a running job"
12
+ end
13
+ end
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'clockwork/test'
3
+
4
+ require 'pry'
5
+ require 'rspec/its'
6
+
7
+ RSpec.configure do |config|
8
+ config.include(Clockwork::Test::RSpec::Matchers)
9
+ end
metadata ADDED
@@ -0,0 +1,169 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: clockwork-test
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Kevin Murphy
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: pry
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '10.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '10.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: clockwork
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: timecop
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :runtime
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ description: Run clockwork jobs in a test environment and assert jobs are run as expected
112
+ email:
113
+ - murphy.kevin.mail@gmail.com
114
+ executables: []
115
+ extensions: []
116
+ extra_rdoc_files: []
117
+ files:
118
+ - ".gitignore"
119
+ - ".rspec"
120
+ - ".travis.yml"
121
+ - Gemfile
122
+ - LICENSE.txt
123
+ - README.md
124
+ - Rakefile
125
+ - clockwork-test.gemspec
126
+ - lib/clockwork/test.rb
127
+ - lib/clockwork/test/event.rb
128
+ - lib/clockwork/test/job_history.rb
129
+ - lib/clockwork/test/manager.rb
130
+ - lib/clockwork/test/rspec/matchers.rb
131
+ - lib/clockwork/test/rspec/matchers/have_run.rb
132
+ - lib/clockwork/test/version.rb
133
+ - spec/acceptance/clock_spec.rb
134
+ - spec/clockwork/test/job_history_spec.rb
135
+ - spec/clockwork/test/manager_spec.rb
136
+ - spec/clockwork/test_spec.rb
137
+ - spec/fixtures/clock.rb
138
+ - spec/spec_helper.rb
139
+ homepage: https://github.com/kevin-j-m/clockwork-test
140
+ licenses:
141
+ - MIT
142
+ metadata: {}
143
+ post_install_message:
144
+ rdoc_options: []
145
+ require_paths:
146
+ - lib
147
+ required_ruby_version: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - ">="
150
+ - !ruby/object:Gem::Version
151
+ version: '0'
152
+ required_rubygems_version: !ruby/object:Gem::Requirement
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ version: '0'
157
+ requirements: []
158
+ rubyforge_project:
159
+ rubygems_version: 2.2.2
160
+ signing_key:
161
+ specification_version: 4
162
+ summary: Test clockwork scheduled jobs
163
+ test_files:
164
+ - spec/acceptance/clock_spec.rb
165
+ - spec/clockwork/test/job_history_spec.rb
166
+ - spec/clockwork/test/manager_spec.rb
167
+ - spec/clockwork/test_spec.rb
168
+ - spec/fixtures/clock.rb
169
+ - spec/spec_helper.rb