crono 1.1.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/LICENSE +2 -3
- data/Rakefile +3 -4
- data/app/assets/javascripts/crono/materialize.min.js +6 -0
- data/app/assets/stylesheets/crono/application.css +26 -0
- data/app/assets/stylesheets/crono/materialize.min.css +31 -0
- data/app/controllers/crono/application_controller.rb +5 -0
- data/app/controllers/crono/jobs_controller.rb +11 -0
- data/app/models/crono/application_record.rb +5 -0
- data/{lib/crono/orm/active_record → app/models/crono}/crono_job.rb +1 -2
- data/app/views/crono/jobs/index.html.erb +50 -0
- data/app/views/crono/jobs/show.html.erb +16 -0
- data/app/views/layouts/crono/application.html.erb +31 -0
- data/{exe → bin}/crono +0 -0
- data/config/routes.rb +4 -0
- data/lib/crono/cli.rb +3 -20
- data/lib/crono/config.rb +2 -3
- data/lib/crono/engine.rb +15 -0
- data/lib/crono/job.rb +10 -6
- data/lib/crono/performer_proxy.rb +2 -2
- data/lib/crono/period.rb +6 -5
- data/lib/crono/time_of_day.rb +2 -2
- data/lib/crono/version.rb +1 -1
- data/lib/crono.rb +3 -3
- data/lib/generators/crono/install/install_generator.rb +12 -1
- data/lib/generators/crono/install/templates/migrations/create_crono_jobs.rb +2 -6
- data/spec/assets/bad_cronotab.rb +12 -0
- data/spec/assets/good_cronotab.rb +9 -0
- data/spec/cli_spec.rb +109 -0
- data/spec/config_spec.rb +47 -0
- data/spec/crono_spec.rb +7 -0
- data/spec/cronotab_spec.rb +20 -0
- data/spec/internal/:memory +0 -0
- data/{log/.keep → spec/internal/app/assets/config/manifest.js} +0 -0
- data/spec/internal/app/controllers/application_controller.rb +3 -0
- data/spec/internal/app/controllers/pages_controller.rb +5 -0
- data/spec/internal/app/views/pages/index.html.erb +1 -0
- data/spec/internal/config/application.rb +22 -0
- data/spec/internal/config/boot.rb +5 -0
- data/spec/internal/config/database.yml +3 -0
- data/spec/internal/config/environment.rb +2 -0
- data/spec/internal/config/routes.rb +3 -0
- data/spec/internal/config/storage.yml +3 -0
- data/spec/internal/db/crono_test.sqlite +0 -0
- data/spec/internal/db/schema.rb +10 -0
- data/spec/internal/log/test.log +3868 -0
- data/{tmp/.gitkeep → spec/internal/public/favicon.ico} +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/-5/-5qRtN26mFn5ud6yyw5E5MpFhT61YcDJPMTlAoDLkEs.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/-C/-C4fFLLfC1HB9flvtZbgPTj9oNiDtq1bvp8LPbnRv1k.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/01/01tpm-y3svQKpsPvOHFWMxWBaG_35EVrgmnB5HKppZw.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/0K/0KEVKDTACcfyL6Fl5QZ6qSNOeswfd6FloUlWXQvwsHg.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/1F/1FwwOzgRWJEUxIctLIsrhuh9z3QKl1XWDCcdfRhgKPk.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/1n/1n5Wp1qC4BYhgEaXBnkRpO1N0oVAwLVdn47Ebk5nduY.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/22/22NEh4OGcNmK5Qh9ss73s622T6309DH8zv5ABF6-6Uk.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/2n/2nEu77IGZHZPjlliLtJofREUZgmt-DGEhQnGIDMd0vA.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/2q/2qyBEUowJIKm_1EF_tbDWLjGDvVCbTjiHtRX_PEVn-U.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/3k/3khLNNSEoPg-2idoNmihX2Ba1loklzJsXvSRXujPaRk.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/7A/7A_YhyGyNk4Me5J0-p-XwK0qelTclb_2WX5pq0eY9dY.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/8d/8daZARHc9PDr5TZ5fqfR-_vJ5kCRzsZQKhQxFkrg_XI.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/90/90vzKM32bOX3CR_IP7i2_AHgZSmn82XN_9gLyqidtUc.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/9V/9V7dOKixQfEwAEIweL7rD4CUSL51MqiMeB67I5xhwXs.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/9a/9aYqlHyIyZYckdfIJ3GbxrCRrsRgbc0wfZBCfd1lVWE.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Ac/AcdbxQ2RjSGjrVJSdCjS-HN_RI3GZbW86dZGhSD8Ots.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Cj/Cjzu7j1PyqHJD0alH5TiAYfJDN0Zbxhg4zFZegEzuYM.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/DC/DC_euXbiKWPXx4qSkPGLNU4C2RIvjMBWliBrTgl6Mqo.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Db/DboywI_AoqjU7aQB5p90ZP76JW8sinmm4tMgjEG8Iu8.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Dz/DzN3uO_usT53JAsXp4Td8TcNu4kjXPgqTUbLJUG7220.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/F3/F3BxEXMKcd4KO0HPnLrN1g6pJIJ_iQXCz9vW--uEixY.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/FE/FEVuJQTU_YF19u2IzUvuuYlUOoKsyk4JA9kX5UCIelU.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/GG/GGT27f9SL2RsHpi8_pEJ1NHNgLBqDui3sDZOTgeD8z0.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Gj/Gj2Go21wf2SZAnyJXKNSGfiGzYXAtrIdSHB2wuGrB6k.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Hi/HicniSJ1JdM-SmZBY-KtuXL6pubtiugbtOJ-qF07hfk.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Hm/HmvhHkuG82Y4O19j_mfQOw9Q4VEq5hW1Ny0Pzh3gY00.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Ic/IcQIS6wTKG3ZKN6foQYUMxNU4dNQRhrAdwVrAi8u11k.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Kz/KzqcEtJH4QmChDeq7W5WHCOe_NOjqNrAWOzOI0b0RrU.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/LE/LE5Is_h8o9DKajRisizRsovSfpVc8T-Z_xScq0YxBMg.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Mc/Mcvqw-yzpBv2GpUMmKfPZed-6wXUYd8rDdGJ12xAEsA.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Nn/NneCOb3TsKS1FT6ImmBbFe5UuI_LxAmaYyk5o3st8jc.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/Ro/RogqNsDfrMolvoqWLn8lCbweCzxIhN-fgXnLfBMFo8A.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/T9/T9uigSdiu4wxaUieqeBq4uQYRdwGsT2acN15p0osfMU.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/TH/THqp0Tr7zg3pz3WISBe0_u-QcPXHIesH0WlH_6zKzco.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/VF/VFaPzl3--nBqsNzvUgJUt2TjANPLzaelSx_eOc_J090.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/XG/XG0dD9kx-qBYVC-WwhzECOTsSICqvnAqLOCq1JWWrRI.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/ZT/ZTJFqWngu6gOr5GuiqAV52YNHRuJeuwQelNDhtm8pKU.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/ZT/zT_x9gWE8eLO9s8Ul_dtwvAqzekg3DpeuSRkQepkKJ4.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/aW/aWSe6QsJ8Dk7ZAuTOYgjegrAoC2XYQtCCeo9KVzVVQs.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/at/ataSdIHBESkY5GN7sMG-XyOftCkIWDK0QaKEewVkqIU.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/au/aukwvh3eZhFFGyc5qZWT9KMJQHxFrTDEqfjXr4VM4dc.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/bu/bu6XdfzbXfiTnSQ_6Shml3tpB7ovoUAcuCGNs-eYehI.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/c2/c2yubRDZmdi6tFc__QitGtUTkpKt0Goc7FNKhRNw0Uk.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/fu/fuVeiMpLNyElLwjOR6EBQP7-ZUwuJTsQe_Ie11TlX7Y.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/hY/hY9drCAzyxN-KBhXav5VLmk4-AFlKT2yguKNLm7wGsA.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/hg/hgkvHB4OV3E0Gq-Ud_lH2ljTzyb-8nGquMAFTBr4O0o.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/jV/jVXcSpsjtyBHtJaFkxwLVcVm79OT5CVEA9XuzV01O2o.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/kK/kKAy2xIb8ozBaCVqr7LkIsO3-78l5U08uq1Ot7JvES0.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/kU/kUed8S8DBmWvnTdMtg0PzTYj51Rbq9R9yfr6bhPzp2I.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/kw/kwNFhW24nxXy_HfVKYdXJyRqhu7ljgl9wR2DcJUo-nA.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/ls/ls-nV5B50MgpIYfcYZWWWjiNsU2tD2DrEbiiEmGtArk.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/m4/m47fbWgjCvvfU0XLMaGJE1zb9zh2STuMZFp7umYjLTA.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/m_/m_VPZpr_Dhvko_jzP2-4VRhDpknOtciX8pqAq3jY4P8.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/n5/n5rslESPawJxj7Ii3QNWxFl-2sUJE4leiEyzbuFE9Mc.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/nz/nzkJWHKhKCtwI9w3TBcwyGzKEik4UFYwulf2jO7xPXc.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/o4/o42lMuA3eKF-eY5RsJO4kHIGfba2QHOCO0b9rz5iNIU.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/oF/oFrXjOEKX9hseSPY19vWwGHfET4QpdjGD5UdFIEN8mo.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/od/od3K7nGVNq7YsrTVF7ItWWkHs8F_XEGn_BlCD5wW_gc.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/qo/qoEv51qzi0h4mpNP5B4vQydW2FmhC_vbbx6vWJ99Jls.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/tQ/tQ-nAfm23F-ckwkWPuftbJS6tumDQhNxQgPIZSrfPM0.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/tr/trn1zjMZoBOIgSYgI84OzddhfICB2KnU4XbV8axLVGo.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/u3/u3VRW5zPBM7BcNRi3G4B8M6CdscKSLJ3xYmp44ekmjU.cache +0 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/uG/UgcLSZ8AKbE1AYf75cSUXI6uefPECNfBcVQ7L0BDzHg.cache +3 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/uG/uGhNDzX7DxymN3xqHLHTndSae3bN0A-MlTk05m6wV4A.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/wF/wFT7zoeQQwz-V8syWaS01xr7xgZWr3UNQLWbYSKXirw.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/wN/wNw4kAG0E_RrvLeFAizMmTp_0jh-HGw1ONLtRyw92z0.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/x-/x-qCUFpNxK1jTHH2qV_VgVk_KrGKdHzNGOnDMVFstuQ.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/yk/ykU9I1MzGh7clO4pu853ZKl4LtMlDtyr8BjpiozFQ8E.cache +2 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/yk/ykWLbyEuqa0JHcwHJ-SdVxv4ImwnymlVxvbPKtk6HUM.cache +1 -0
- data/spec/internal/tmp/cache/assets/sprockets/v4.0.0/zO/zOr1jCikukgv8-bNHvK43vGLa3CSjtmiwf2K7dUjTnw.cache +0 -0
- data/spec/job_spec.rb +183 -0
- data/spec/models/crono/crono_job_spec.rb +31 -0
- data/spec/performer_proxy_spec.rb +39 -0
- data/spec/period_spec.rb +141 -0
- data/spec/rails_helper.rb +89 -0
- data/spec/scheduler_spec.rb +62 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/tasks/crono_tasks_spec.rb +23 -0
- data/spec/web_spec.rb +61 -0
- metadata +233 -68
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.travis.yml +0 -14
- data/Changes.md +0 -88
- data/Gemfile +0 -4
- data/Gemfile.lock +0 -78
- data/NOTICE +0 -2
- data/README.md +0 -218
- data/bin/console +0 -14
- data/bin/setup +0 -7
- data/crono.gemspec +0 -34
- data/examples/crono_web_ui.png +0 -0
- data/examples/cronotab.rb +0 -14
- data/examples/monitrc.conf +0 -6
- data/lib/crono/web.rb +0 -22
- data/web/assets/css/custom.css +0 -19
- data/web/assets/css/materialize.min.css +0 -16
- data/web/assets/font/material-design-icons/LICENSE.txt +0 -428
- data/web/assets/font/material-design-icons/Material-Design-Icons.eot +0 -0
- data/web/assets/font/material-design-icons/Material-Design-Icons.svg +0 -751
- data/web/assets/font/material-design-icons/Material-Design-Icons.ttf +0 -0
- data/web/assets/font/material-design-icons/Material-Design-Icons.woff +0 -0
- data/web/assets/font/material-design-icons/Material-Design-Icons.woff2 +0 -0
- data/web/assets/font/roboto/Roboto-Bold.ttf +0 -0
- data/web/assets/font/roboto/Roboto-Bold.woff +0 -0
- data/web/assets/font/roboto/Roboto-Bold.woff2 +0 -0
- data/web/assets/font/roboto/Roboto-Light.ttf +0 -0
- data/web/assets/font/roboto/Roboto-Light.woff +0 -0
- data/web/assets/font/roboto/Roboto-Light.woff2 +0 -0
- data/web/assets/font/roboto/Roboto-Medium.ttf +0 -0
- data/web/assets/font/roboto/Roboto-Medium.woff +0 -0
- data/web/assets/font/roboto/Roboto-Medium.woff2 +0 -0
- data/web/assets/font/roboto/Roboto-Regular.ttf +0 -0
- data/web/assets/font/roboto/Roboto-Regular.woff +0 -0
- data/web/assets/font/roboto/Roboto-Regular.woff2 +0 -0
- data/web/assets/font/roboto/Roboto-Thin.ttf +0 -0
- data/web/assets/font/roboto/Roboto-Thin.woff +0 -0
- data/web/assets/font/roboto/Roboto-Thin.woff2 +0 -0
- data/web/assets/js/jquery-2.1.3.min.js +0 -4
- data/web/assets/js/materialize.min.js +0 -10
- data/web/views/dashboard.haml +0 -36
- data/web/views/job.haml +0 -15
- data/web/views/layout.haml +0 -29
data/spec/period_spec.rb
ADDED
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Crono::Period do
|
4
|
+
describe '#description' do
|
5
|
+
it 'should return period description' do
|
6
|
+
@period = Crono::Period.new(1.week, on: :monday, at: '15:20')
|
7
|
+
expected_description = if ActiveSupport::VERSION::MAJOR >= 5
|
8
|
+
'every 1 week at 15:20 on Monday'
|
9
|
+
else
|
10
|
+
'every 7 days at 15:20 on Monday'
|
11
|
+
end
|
12
|
+
expect(@period.description).to be_eql(expected_description)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '#next' do
|
17
|
+
context 'in weakly basis' do
|
18
|
+
it "should raise error if 'on' is wrong" do
|
19
|
+
expect { @period = Crono::Period.new(7.days, on: :bad_day) }
|
20
|
+
.to raise_error("Wrong 'on' day")
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should raise error when period is less than 1 week' do
|
24
|
+
expect { @period = Crono::Period.new(6.days, on: :monday) }
|
25
|
+
.to raise_error("period should be at least 1 week to use 'on'")
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should return a 'on' day" do
|
29
|
+
@period = Crono::Period.new(1.week, on: :thursday, at: '15:30')
|
30
|
+
current_week = Time.zone.now.beginning_of_week
|
31
|
+
last_run_time = current_week.advance(days: 1) # last run on the tuesday
|
32
|
+
next_run_at = Time.zone.now.next_week.advance(days: 3)
|
33
|
+
.change(hour: 15, min: 30)
|
34
|
+
expect(@period.next(since: last_run_time)).to be_eql(next_run_at)
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should return a next week day 'on'" do
|
38
|
+
@period = Crono::Period.new(1.week, on: :thursday)
|
39
|
+
Timecop.freeze(Time.zone.now.beginning_of_week.advance(days: 4)) do
|
40
|
+
expect(@period.next).to be_eql(Time.zone.now.next_week.advance(days: 3))
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should return a current week day on the first run if not too late' do
|
45
|
+
@period = Crono::Period.new(7.days, on: :tuesday)
|
46
|
+
beginning_of_the_week = Time.zone.now.beginning_of_week
|
47
|
+
tuesday = beginning_of_the_week.advance(days: 1)
|
48
|
+
Timecop.freeze(beginning_of_the_week) do
|
49
|
+
expect(@period.next).to be_eql(tuesday)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should return today on the first run if not too late' do
|
54
|
+
@period = Crono::Period.new(1.week, on: :sunday, at: '22:00')
|
55
|
+
Timecop.freeze(Time.zone.now.beginning_of_week.advance(days: 6)
|
56
|
+
.change(hour: 21, min: 0)) do
|
57
|
+
expect(@period.next).to be_eql(
|
58
|
+
Time.zone.now.beginning_of_week.advance(days: 6).change(hour: 22, min: 0)
|
59
|
+
)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
context 'in daily basis' do
|
65
|
+
it "should return Time.zone.now if the next time in past" do
|
66
|
+
@period = Crono::Period.new(1.day, at: '06:00')
|
67
|
+
expect(@period.next(since: 2.days.ago).to_s).to be_eql(Time.zone.now.to_s)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return time 2 days from now' do
|
71
|
+
@period = Crono::Period.new(2.day)
|
72
|
+
expect(@period.next.to_s).to be_eql(2.days.from_now.to_s)
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should set time to 'at' time as a string" do
|
76
|
+
time = 10.minutes.ago
|
77
|
+
at = [time.hour, time.min].join(':')
|
78
|
+
@period = Crono::Period.new(2.days, at: at)
|
79
|
+
expect(@period.next.to_s).to be_eql(2.days.from_now.change(hour: time.hour, min: time.min).to_s)
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should set time to 'at' time as a hash" do
|
83
|
+
time = 10.minutes.ago
|
84
|
+
at = { hour: time.hour, min: time.min }
|
85
|
+
@period = Crono::Period.new(2.days, at: at)
|
86
|
+
expect(@period.next.to_s).to be_eql(2.days.from_now.change(at).to_s)
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should raise error when 'at' is wrong" do
|
90
|
+
expect {
|
91
|
+
Crono::Period.new(2.days, at: 1)
|
92
|
+
}.to raise_error("Unknown 'at' format")
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should raise error when period is less than 1 day' do
|
96
|
+
expect {
|
97
|
+
Crono::Period.new(5.hours, at: '15:30')
|
98
|
+
}.to raise_error("period should be at least 1 day to use 'at' with specified hour")
|
99
|
+
end
|
100
|
+
|
101
|
+
it 'should return time in relation to last time' do
|
102
|
+
@period = Crono::Period.new(2.days)
|
103
|
+
expect(@period.next(since: 1.day.ago).to_s).to be_eql(1.day.from_now.to_s)
|
104
|
+
end
|
105
|
+
|
106
|
+
it 'should return today time if it is first run and not too late' do
|
107
|
+
time = 10.minutes.from_now
|
108
|
+
at = { hour: time.hour, min: time.min }
|
109
|
+
@period = Crono::Period.new(2.days, at: at)
|
110
|
+
expect(@period.next.utc.to_s).to be_eql(Time.zone.now.change(at).utc.to_s)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
context 'in hourly basis' do
|
115
|
+
it 'should return next hour minutes if current hour minutes passed' do
|
116
|
+
Timecop.freeze(Time.zone.now.beginning_of_hour.advance(minutes: 20)) do
|
117
|
+
@period = Crono::Period.new(1.hour, at: { min: 15 })
|
118
|
+
expect(@period.next.utc.to_s).to be_eql 1.hour.from_now.beginning_of_hour.advance(minutes: 15).utc.to_s
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
it 'should return current hour minutes if current hour minutes not passed yet' do
|
123
|
+
Timecop.freeze(Time.zone.now.beginning_of_hour.advance(minutes: 10)) do
|
124
|
+
@period = Crono::Period.new(1.hour, at: { min: 15 })
|
125
|
+
expect(@period.next.utc.to_s).to be_eql Time.zone.now.beginning_of_hour.advance(minutes: 15).utc.to_s
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
it 'should return next hour minutes within the given interval' do
|
130
|
+
Timecop.freeze(Time.zone.now.change(hour: 16, min: 10)) do
|
131
|
+
@period = Crono::Period.new(1.hour, at: { min: 15 }, within: '08:00-16:00')
|
132
|
+
expect(@period.next.utc.to_s).to be_eql Time.zone.now.tomorrow.change(hour: 8, min: 15).utc.to_s
|
133
|
+
end
|
134
|
+
Timecop.freeze(Time.zone.now.change(hour: 16, min: 10)) do
|
135
|
+
@period = Crono::Period.new(1.hour, at: { min: 15 }, within: '23:00-07:00')
|
136
|
+
expect(@period.next.utc.to_s).to be_eql Time.zone.now.change(hour: 23, min: 15).utc.to_s
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
# This file is copied to spec/ when you run 'rails generate rspec:install'
|
2
|
+
require 'spec_helper'
|
3
|
+
ENV['RAILS_ENV'] ||= 'test'
|
4
|
+
require File.expand_path('../spec/internal/config/environment.rb', __dir__)
|
5
|
+
# Prevent database truncation if the environment is production
|
6
|
+
abort('The Rails environment is running in production mode!') if Rails.env.production?
|
7
|
+
require 'rspec/rails'
|
8
|
+
# Add additional requires below this line. Rails is not loaded until this point!
|
9
|
+
require 'bundler/setup'
|
10
|
+
Bundler.setup
|
11
|
+
|
12
|
+
$LOAD_PATH.unshift File.expand_path('../../lib', __dir__)
|
13
|
+
|
14
|
+
require 'timecop'
|
15
|
+
require 'byebug'
|
16
|
+
require 'crono'
|
17
|
+
|
18
|
+
# setting default time zone
|
19
|
+
# In Rails project, Time.zone_default equals "UTC"
|
20
|
+
Time.zone_default = Time.find_zone('UTC')
|
21
|
+
|
22
|
+
ActiveRecord::Base.logger = Logger.new($stdout)
|
23
|
+
|
24
|
+
ActiveRecord::Base.establish_connection(
|
25
|
+
adapter: 'sqlite3',
|
26
|
+
database: ':memory'
|
27
|
+
)
|
28
|
+
ActiveRecord::Schema.define do
|
29
|
+
require_relative '../lib/generators/crono/install/templates/migrations/create_crono_jobs.rb'
|
30
|
+
@migration_version = '[6.1]'
|
31
|
+
end
|
32
|
+
CreateCronoJobs.up
|
33
|
+
|
34
|
+
|
35
|
+
# Requires supporting ruby files with custom matchers and macros, etc, in
|
36
|
+
# spec/support/ and its subdirectories. Files matching `spec/**/*_spec.rb` are
|
37
|
+
# run as spec files by default. This means that files in spec/support that end
|
38
|
+
# in _spec.rb will both be required and run as specs, causing the specs to be
|
39
|
+
# run twice. It is recommended that you do not name files matching this glob to
|
40
|
+
# end with _spec.rb. You can configure this pattern with the --pattern
|
41
|
+
# option on the command line or in ~/.rspec, .rspec or `.rspec-local`.
|
42
|
+
#
|
43
|
+
# The following line is provided for convenience purposes. It has the downside
|
44
|
+
# of increasing the boot-up time by auto-requiring all files in the support
|
45
|
+
# directory. Alternatively, in the individual `*_spec.rb` files, manually
|
46
|
+
# require only the support files necessary.
|
47
|
+
#
|
48
|
+
# Dir[Rails.root.join('spec', 'support', '**', '*.rb')].sort.each { |f| require f }
|
49
|
+
|
50
|
+
# Checks for pending migrations and applies them before tests are run.
|
51
|
+
# If you are not using ActiveRecord, you can remove these lines.
|
52
|
+
begin
|
53
|
+
ActiveRecord::Migration.maintain_test_schema!
|
54
|
+
rescue ActiveRecord::PendingMigrationError => e
|
55
|
+
puts e.to_s.strip
|
56
|
+
exit 1
|
57
|
+
end
|
58
|
+
RSpec.configure do |config|
|
59
|
+
# Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
|
60
|
+
# config.fixture_path = "#{::Rails.root}/spec/fixtures"
|
61
|
+
|
62
|
+
# If you're not using ActiveRecord, or you'd prefer not to run each of your
|
63
|
+
# examples within a transaction, remove the following line or assign false
|
64
|
+
# instead of true.
|
65
|
+
# config.use_transactional_fixtures = true
|
66
|
+
|
67
|
+
# You can uncomment this line to turn off ActiveRecord support entirely.
|
68
|
+
# config.use_active_record = false
|
69
|
+
|
70
|
+
# RSpec Rails can automatically mix in different behaviours to your tests
|
71
|
+
# based on their file location, for example enabling you to call `get` and
|
72
|
+
# `post` in specs under `spec/controllers`.
|
73
|
+
#
|
74
|
+
# You can disable this behaviour by removing the line below, and instead
|
75
|
+
# explicitly tag your specs with their type, e.g.:
|
76
|
+
#
|
77
|
+
# RSpec.describe UsersController, type: :controller do
|
78
|
+
# # ...
|
79
|
+
# end
|
80
|
+
#
|
81
|
+
# The different available types are documented in the features, such as in
|
82
|
+
# https://relishapp.com/rspec/rspec-rails/docs
|
83
|
+
config.infer_spec_type_from_file_location!
|
84
|
+
|
85
|
+
# Filter lines from Rails gems in backtraces.
|
86
|
+
config.filter_rails_from_backtrace!
|
87
|
+
# arbitrary gems may also be filtered via:
|
88
|
+
# config.filter_gems_from_backtrace("gem name")
|
89
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
class TestJob
|
4
|
+
def perform
|
5
|
+
puts 'Test!'
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
describe Crono::Scheduler do
|
10
|
+
let(:scheduler) { Crono::Scheduler.new }
|
11
|
+
|
12
|
+
describe '#add_job' do
|
13
|
+
it 'should call Job#load on Job' do
|
14
|
+
@job = Crono::Job.new(TestJob, Crono::Period.new(10.day, at: '04:05'), [])
|
15
|
+
expect(@job).to receive(:load)
|
16
|
+
scheduler.add_job(@job)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#next_jobs' do
|
21
|
+
it 'should return next job in schedule' do
|
22
|
+
scheduler.jobs = jobs = [
|
23
|
+
Crono::Period.new(3.days, at: 10.minutes.from_now.strftime('%H:%M')),
|
24
|
+
Crono::Period.new(1.day, at: 20.minutes.from_now.strftime('%H:%M')),
|
25
|
+
Crono::Period.new(7.days, at: 40.minutes.from_now.strftime('%H:%M'))
|
26
|
+
].map { |period| Crono::Job.new(TestJob, period, []) }
|
27
|
+
|
28
|
+
time, jobs = scheduler.next_jobs
|
29
|
+
expect(jobs).to be_eql [jobs[0]]
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'should return an array of jobs scheduled at same time with `at`' do
|
33
|
+
time = 5.minutes.from_now
|
34
|
+
scheduler.jobs = jobs = [
|
35
|
+
Crono::Period.new(1.day, at: time.strftime('%H:%M')),
|
36
|
+
Crono::Period.new(1.day, at: time.strftime('%H:%M')),
|
37
|
+
Crono::Period.new(1.day, at: 10.minutes.from_now.strftime('%H:%M'))
|
38
|
+
].map { |period| Crono::Job.new(TestJob, period, []) }
|
39
|
+
|
40
|
+
time, jobs = scheduler.next_jobs
|
41
|
+
expect(jobs).to be_eql [jobs[0], jobs[1]]
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should handle a few jobs scheduled at same time without `at`' do
|
45
|
+
scheduler.jobs = jobs = [
|
46
|
+
Crono::Period.new(10.seconds),
|
47
|
+
Crono::Period.new(10.seconds),
|
48
|
+
Crono::Period.new(1.day, at: 10.minutes.from_now.strftime('%H:%M'))
|
49
|
+
].map { |period| Crono::Job.new(TestJob, period, []) }
|
50
|
+
|
51
|
+
_, next_jobs = scheduler.next_jobs
|
52
|
+
expect(next_jobs).to be_eql [jobs[0]]
|
53
|
+
|
54
|
+
Timecop.travel(4.seconds.from_now)
|
55
|
+
expect(Thread).to receive(:new)
|
56
|
+
jobs[0].perform
|
57
|
+
|
58
|
+
_, next_jobs = scheduler.next_jobs
|
59
|
+
expect(next_jobs).to be_eql [jobs[1]]
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'bundler'
|
2
|
+
|
3
|
+
Bundler.require :default, :development
|
4
|
+
|
5
|
+
# If you're using all parts of Rails:
|
6
|
+
Combustion.initialize! :all
|
7
|
+
# Or, load just what you need:
|
8
|
+
# Combustion.initialize! :active_record, :action_controller
|
9
|
+
|
10
|
+
require 'rspec/rails'
|
11
|
+
# If you're using Capybara:
|
12
|
+
# require 'capybara/rails'
|
13
|
+
|
14
|
+
RSpec.configure do |config|
|
15
|
+
config.use_transactional_fixtures = true
|
16
|
+
config.mock_with :rspec do |mocks|
|
17
|
+
mocks.allow_message_expectations_on_nil = true
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rake'
|
3
|
+
|
4
|
+
load 'tasks/crono_tasks.rake'
|
5
|
+
Rake::Task.define_task(:environment)
|
6
|
+
|
7
|
+
describe 'rake' do
|
8
|
+
describe 'crono:clean' do
|
9
|
+
it 'should clean unused tasks from DB' do
|
10
|
+
Crono::CronoJob.create!(job_id: 'used_job')
|
11
|
+
ENV['CRONOTAB'] = File.expand_path('../../assets/good_cronotab.rb', __FILE__)
|
12
|
+
Rake::Task['crono:clean'].invoke
|
13
|
+
expect(Crono::CronoJob.where(job_id: 'used_job')).not_to exist
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe 'crono:check' do
|
18
|
+
it 'should check cronotab syntax' do
|
19
|
+
ENV['CRONOTAB'] = File.expand_path('../../assets/bad_cronotab.rb', __FILE__)
|
20
|
+
expect { Rake::Task['crono:check'].invoke }.to raise_error
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/spec/web_spec.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'rack/test'
|
3
|
+
include Rack::Test::Methods
|
4
|
+
|
5
|
+
describe Crono::Engine do
|
6
|
+
let(:app) { Crono::Engine }
|
7
|
+
|
8
|
+
before do
|
9
|
+
Crono::CronoJob.destroy_all
|
10
|
+
@test_job_id = 'Perform TestJob every 5 seconds'
|
11
|
+
@test_job_log = 'All runs ok'
|
12
|
+
@test_job = Crono::CronoJob.create!(
|
13
|
+
job_id: @test_job_id,
|
14
|
+
log: @test_job_log
|
15
|
+
)
|
16
|
+
end
|
17
|
+
|
18
|
+
after { @test_job.destroy }
|
19
|
+
|
20
|
+
describe '/' do
|
21
|
+
it 'should show all jobs' do
|
22
|
+
get '/'
|
23
|
+
expect(last_response).to be_ok
|
24
|
+
expect(last_response.body).to include @test_job_id
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should show a error mark when a job is unhealthy' do
|
28
|
+
@test_job.update(healthy: false, last_performed_at: 10.minutes.ago)
|
29
|
+
get '/'
|
30
|
+
expect(last_response.body).to include 'Error'
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'should show a success mark when a job is healthy' do
|
34
|
+
@test_job.update(healthy: true, last_performed_at: 10.minutes.ago)
|
35
|
+
get '/'
|
36
|
+
expect(last_response.body).to include 'Success'
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'should show a pending mark when a job is pending' do
|
40
|
+
@test_job.update(healthy: nil)
|
41
|
+
get '/'
|
42
|
+
expect(last_response.body).to include 'Pending'
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
describe '/job/:id' do
|
47
|
+
it 'should show job log' do
|
48
|
+
get "/jobs/#{@test_job.id}"
|
49
|
+
expect(last_response).to be_ok
|
50
|
+
expect(last_response.body).to include @test_job_id
|
51
|
+
expect(last_response.body).to include @test_job_log
|
52
|
+
end
|
53
|
+
|
54
|
+
it 'should show a message about the unhealthy job' do
|
55
|
+
message = 'An error occurs during the last execution of this job'
|
56
|
+
@test_job.update(healthy: false)
|
57
|
+
get "/jobs/#{@test_job.id}"
|
58
|
+
expect(last_response.body).to include message
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|