rocketjob_mission_control 0.9.2

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.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/Rakefile +26 -0
  4. data/app/assets/javascripts/rocket_job_mission_control/application.js +18 -0
  5. data/app/assets/javascripts/rocket_job_mission_control/base.js.coffee +24 -0
  6. data/app/assets/javascripts/rocket_job_mission_control/jobs.js.coffee +11 -0
  7. data/app/assets/stylesheets/rocket_job_mission_control/application.scss +18 -0
  8. data/app/assets/stylesheets/rocket_job_mission_control/base.scss +468 -0
  9. data/app/assets/stylesheets/rocket_job_mission_control/bootstrap_and_overrides.scss +6 -0
  10. data/app/assets/stylesheets/rocket_job_mission_control/callout.scss +40 -0
  11. data/app/assets/stylesheets/rocket_job_mission_control/jobs.scss +16 -0
  12. data/app/assets/stylesheets/rocket_job_mission_control/jquery.bootstrap-touchspin.scss +45 -0
  13. data/app/controllers/rocket_job_mission_control/application_controller.rb +15 -0
  14. data/app/controllers/rocket_job_mission_control/jobs/inputs_controller.rb +9 -0
  15. data/app/controllers/rocket_job_mission_control/jobs/slices_controller.rb +10 -0
  16. data/app/controllers/rocket_job_mission_control/jobs_controller.rb +97 -0
  17. data/app/controllers/rocket_job_mission_control/workers_controller.rb +79 -0
  18. data/app/helpers/rocket_job_mission_control/application_helper.rb +16 -0
  19. data/app/helpers/rocket_job_mission_control/jobs_helper.rb +57 -0
  20. data/app/helpers/rocket_job_mission_control/workers_helper.rb +14 -0
  21. data/app/views/layouts/rocket_job_mission_control/application.html.haml +43 -0
  22. data/app/views/layouts/rocket_job_mission_control/partials/_header.html.haml +16 -0
  23. data/app/views/layouts/rocket_job_mission_control/partials/_sidebar.html.haml +20 -0
  24. data/app/views/rocket_job_mission_control/jobs/_list.html.haml +33 -0
  25. data/app/views/rocket_job_mission_control/jobs/_status.html.haml +39 -0
  26. data/app/views/rocket_job_mission_control/jobs/index.html.haml +8 -0
  27. data/app/views/rocket_job_mission_control/jobs/running.html.haml +37 -0
  28. data/app/views/rocket_job_mission_control/jobs/show.html.haml +50 -0
  29. data/app/views/rocket_job_mission_control/workers/_actions.html.haml +9 -0
  30. data/app/views/rocket_job_mission_control/workers/index.html.haml +51 -0
  31. data/config/locales/en.yml +57 -0
  32. data/config/routes.rb +28 -0
  33. data/lib/rocket_job_mission_control/engine.rb +15 -0
  34. data/lib/rocket_job_mission_control/version.rb +3 -0
  35. data/lib/rocketjob_mission_control.rb +4 -0
  36. data/lib/tasks/rocket_job_mission_control_tasks.rake +4 -0
  37. data/spec/controllers/application_controller_spec.rb +47 -0
  38. data/spec/controllers/jobs_controller_spec.rb +217 -0
  39. data/spec/controllers/workers_controller_spec.rb +125 -0
  40. data/spec/dummy/README.rdoc +28 -0
  41. data/spec/dummy/Rakefile +6 -0
  42. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  43. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  44. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  45. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  46. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  47. data/spec/dummy/bin/bundle +3 -0
  48. data/spec/dummy/bin/rails +4 -0
  49. data/spec/dummy/bin/rake +4 -0
  50. data/spec/dummy/config.ru +4 -0
  51. data/spec/dummy/config/application.rb +25 -0
  52. data/spec/dummy/config/boot.rb +5 -0
  53. data/spec/dummy/config/database.yml +25 -0
  54. data/spec/dummy/config/environment.rb +5 -0
  55. data/spec/dummy/config/environments/development.rb +34 -0
  56. data/spec/dummy/config/environments/test.rb +39 -0
  57. data/spec/dummy/config/initializers/assets.rb +8 -0
  58. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  59. data/spec/dummy/config/initializers/cookies_serializer.rb +3 -0
  60. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  61. data/spec/dummy/config/initializers/inflections.rb +16 -0
  62. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  63. data/spec/dummy/config/initializers/session_store.rb +3 -0
  64. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  65. data/spec/dummy/config/locales/en.yml +23 -0
  66. data/spec/dummy/config/routes.rb +4 -0
  67. data/spec/dummy/config/secrets.yml +22 -0
  68. data/spec/dummy/log/test.log +2459 -0
  69. data/spec/dummy/public/404.html +67 -0
  70. data/spec/dummy/public/422.html +67 -0
  71. data/spec/dummy/public/500.html +66 -0
  72. data/spec/dummy/public/favicon.ico +0 -0
  73. data/spec/helpers/jobs_helper_spec.rb +100 -0
  74. data/spec/rails_helper.rb +47 -0
  75. data/spec/spec_helper.rb +81 -0
  76. data/spec/views/workers/index.html.haml_spec.rb +17 -0
  77. data/vendor/assets/javascripts/jquery.bootstrap-touchspin.js +686 -0
  78. data/vendor/assets/javascripts/prism.js +6 -0
  79. data/vendor/assets/stylesheets/jquery.bootstrap-touchspin.css +45 -0
  80. data/vendor/assets/stylesheets/prism.scss +160 -0
  81. metadata +319 -0
@@ -0,0 +1,16 @@
1
+ #header
2
+ %a#menu-toggle.btn.btn-default.btn-blank.first{href: ""}
3
+ %i.fa.fa-bars
4
+ Menu
5
+
6
+ = link_to main_app.root_path, class: 'btn btn-default btn-blank last' do
7
+ %i.fa.fa-reply
8
+ Back Home
9
+
10
+ .clearfix
11
+
12
+ .brand
13
+ = link_to root_path, class: 'text-center' do
14
+ %i.fa.fa-rocket
15
+ rocket
16
+ .color job
@@ -0,0 +1,20 @@
1
+ #sidebar-wrapper
2
+ %ul.sidebar-nav
3
+ %li.sidebar-brand
4
+ %a#menu-close.menu-close
5
+ %i.fa.fa-times
6
+
7
+ .clearfix
8
+
9
+ %li
10
+ = link_to jobs_path, class: active_page(jobs_path) do
11
+ %i.fa.fa-home
12
+ Jobs
13
+ %li
14
+ = link_to workers_path, class: active_page(workers_path) do
15
+ %i.fa.fa-database
16
+ Workers
17
+ %li
18
+ = link_to running_jobs_path, class: active_page(running_jobs_path) do
19
+ %i.fa.fa-circle-o-notch
20
+ Running
@@ -0,0 +1,33 @@
1
+ .col-md-4.job-list
2
+ .filter.btn-group.btn-group-justified{ style: 'padding: 1px 1px 1px 1px;', data: { toggle: 'buttons' } }
3
+ - %w( queued running failed completed ).each do |state|
4
+ %label.btn.btn-default.state-toggle{ class: ('active' if @states.include?(state.to_s) || @states.empty?), title: state.capitalize}
5
+ = check_box_tag(state, state, (@states.include?(state.to_s) || @states.empty?))
6
+ %i.fa{class: job_state_icon(state.to_sym)}
7
+ .clearfix
8
+ .list
9
+ - if jobs.empty?
10
+ .card.inner
11
+ .title
12
+ .lead.text-center No Jobs Found.
13
+ - else
14
+ - jobs.each do |job|
15
+ = link_to job_path(id: job.id, states: @states), class: job_card_class(job) do
16
+ .inner
17
+ .title
18
+ .lead{class: job_selected_class(job, @job)}
19
+ %i.fa{class: job_state_icon(job.state), style: 'font-size: 75%;', title: job.state}
20
+ = job_title(job)
21
+
22
+ .description= job.description.try(:truncate, 50)
23
+ .info
24
+ .duration.pull-left
25
+ - if job.failed?
26
+ %i.fa.fa-bomb
27
+ failed
28
+ - else
29
+ %i.fa.fa-clock-o
30
+ = job.duration
31
+ - if job.running?
32
+ .progress.pull-right
33
+ .progress-bar{ style: "width: #{job.percent_complete}%;", title: "#{job.percent_complete}% percent complete."}
@@ -0,0 +1,39 @@
1
+ - status = job.status(Time.zone)
2
+ .id
3
+ %label ID:
4
+ = status.delete('id')
5
+ .status-message
6
+ %label Description:
7
+ = status.delete('description')
8
+ .status-message
9
+ %label Duration:
10
+ = status.delete('duration')
11
+ .status-message
12
+ %label Record Count:
13
+ = status.delete('record_count')
14
+ .created_at
15
+ %label Created At:
16
+ = status.delete('created_at')
17
+ .status-message
18
+ %label Started At:
19
+ = status.delete('started_at')
20
+ .status-message
21
+ %label % Complete:
22
+ = status.delete('percent_complete')
23
+
24
+ - remaining = {}
25
+ - status.each_pair do |key, value|
26
+ .status-message
27
+ - if ['state', '_type'].include?(key)
28
+ - elsif value.kind_of?(Hash) || value.kind_of?(Array)
29
+ - remaining[key] = value
30
+ - else
31
+ %label= "#{key.to_s.titleize}:"
32
+ = value
33
+ .clearfix
34
+
35
+ - remaining.each_pair do |key, value|
36
+ .status-message
37
+ %label= "#{key.to_s.titleize}:"
38
+ %pre
39
+ %code= pretty_print_array_or_hash(value)
@@ -0,0 +1,8 @@
1
+ #queue
2
+ = render partial: 'list', locals: { jobs: @jobs }
3
+
4
+ .col-md-8.job-status
5
+ #job
6
+ .welcome_screen
7
+ %h1 Welcome to RocketJob!
8
+ %p High volume, priority based, Enterprise Batch Processing solution for Ruby
@@ -0,0 +1,37 @@
1
+ .col-md-10.col-md-offset-1
2
+ .jumbotron
3
+ %h3 There are currently #{@jobs.count} jobs running.
4
+ #accordion.panel-group
5
+ - @jobs.each do |job|
6
+ .panel.panel-default
7
+ .panel-heading
8
+ %h4.panel-title
9
+ %a.collapsed{href: "##{job.id}", data: {toggle: 'collapse', parent: '#accordion'} }
10
+ %i.fa.fa-plus-square-o{ style: 'font-size: 80%;'}
11
+ = job_title(job)
12
+ .panel-collapse.collapse{ id: job.id }
13
+ .panel-body
14
+ .info
15
+ .progress
16
+ .progress-bar{ style: "width: #{job.percent_complete}%; min-width: 1em;", title: "#{job.percent_complete}% percent complete."}
17
+ - if job.kind_of?(RocketJob::SlicedJob)
18
+ .slice_count
19
+ %label Slices Running:
20
+ = job.input.find_all { |s| s.state == :running }.count
21
+ - else
22
+ .worker
23
+ %label Worker:
24
+ = job.worker_name
25
+
26
+ = render partial: 'status', locals: { job: job }
27
+ - if job.kind_of?(RocketJob::SlicedJob)
28
+ %table.table
29
+ %tr
30
+ %th worker
31
+ %th parameters
32
+ - job.input.each('state' => 'running') do |slice|
33
+ %tr
34
+ %td= slice.worker_name
35
+ %td
36
+ %pre
37
+ %code= pretty_print_array_or_hash(slice.to_a)
@@ -0,0 +1,50 @@
1
+ #queue
2
+ = render partial: 'list', locals: { jobs: @jobs }
3
+
4
+ .col-md-8.job-status
5
+ #job
6
+ .lead= job_title(@job)
7
+
8
+ .job-state.inline-block
9
+ .left state
10
+ .right{ class: job_class(@job) }
11
+ = @job.state
12
+
13
+ .priority.pull-right
14
+ = form_for(@job, as: :job, url: job_path(@job), html: {method: :patch}) do |f|
15
+
16
+ .input-group.spinner{ "data-id" => @job.id }
17
+ = f.text_field(:priority, class: 'form-control')
18
+
19
+ .input-group-btn-vertical
20
+ %button#increase_priority.btn.btn-default.up{ type: 'button' }
21
+ %i.fa.fa-caret-up
22
+
23
+ %button#decrease_priority.btn.btn-default.down{ type: 'button' }
24
+ %i.fa.fa-caret-down
25
+
26
+ = submit_tag "set", class: "btn btn-sm btn-primary priority-set"
27
+
28
+ .job-retry.btn-group.pull-right{ style: 'margin-right: 1em;' }
29
+ - valid_events = @job.aasm.events.collect{ |e| e.name }
30
+ - if valid_events.include?(:abort)
31
+ = link_to "abort", abort_job_path(@job), method: :patch, class: 'btn btn-default'
32
+
33
+ - if @job.completed? || @job.aborted?
34
+ = link_to "destroy", job_path(@job), method: :delete, class: 'btn btn-default'
35
+
36
+ - if valid_events.include?(:fail)
37
+ = link_to "fail", fail_job_path(@job), method: :patch, class: 'btn btn-default'
38
+
39
+ - if valid_events.include?(:pause)
40
+ = link_to "pause", pause_job_path(@job), method: :patch, class: 'btn btn-default'
41
+
42
+ - if valid_events.include?(:resume)
43
+ = link_to "resume", resume_job_path(@job), method: :patch, class: 'btn btn-default'
44
+
45
+ - if valid_events.include?(:retry)
46
+ = link_to "retry", retry_job_path(@job), method: :patch, class: 'btn btn-default'
47
+
48
+ .clearfix
49
+
50
+ = render partial: 'status', locals: { job: @job }
@@ -0,0 +1,9 @@
1
+ - if worker.stopping?
2
+ %p Worker is stopping...
3
+ = link_to "destroy", worker_path(worker), method: :delete, class: 'btn btn-danger'
4
+ -else
5
+ = link_to "stop", stop_worker_path(worker), method: :patch, class: 'btn btn-danger'
6
+ - if worker.paused?
7
+ = link_to "resume", resume_worker_path(worker), method: :patch, class: 'btn btn-default'
8
+ - else
9
+ = link_to "pause", pause_worker_path(worker), method: :patch, class: 'btn btn-default'
@@ -0,0 +1,51 @@
1
+ #workers
2
+ - if @workers.present?
3
+ .col-lg-1
4
+ .dropdown
5
+ %button.btn.btn-primary.dropdown-toggle{ data: {toggle: 'dropdown'} }
6
+ Actions
7
+ %span.caret
8
+ %ul.dropdown-menu
9
+ %li= link_to("Stop All", update_all_workers_path(worker_action: :stop), method: :patch)
10
+ %li= link_to("Pause All", update_all_workers_path(worker_action: :pause), method: :patch)
11
+ %li= link_to("Resume All", update_all_workers_path(worker_action: :resume), method: :patch)
12
+
13
+ .grid
14
+ - @workers.each do |worker|
15
+ .card.col-xs-12.col-sm-6.col-md-3.col-lg-3
16
+ .inner.callout-top{ class: worker_card_class(worker) }
17
+ .icon
18
+ %i.fa.fa-database
19
+
20
+ .title
21
+ %h3
22
+ = worker.name
23
+
24
+ .state
25
+ %b State:
26
+ = worker.state
27
+
28
+ .threads.max_threads
29
+ %b Max Threads:
30
+ = worker.max_threads
31
+
32
+ .threads.current_threads
33
+ %b Current Threads:
34
+ = worker.heartbeat.current_threads.present? ? worker.heartbeat.current_threads : 0
35
+
36
+ .time
37
+ %b Started:
38
+ = worker.started_at.strftime("%b, %d %Y at %l:%M %p")
39
+
40
+ .actions
41
+ = render partial: 'actions', locals: { worker: worker }
42
+
43
+ - else
44
+ .no-workers
45
+ There are currently no workers.
46
+ %br
47
+ You can start a worker with the following command:
48
+
49
+ .code-block.col-md-6.col-md-offset-3
50
+ %pre
51
+ %code.language-bash bin/rocketjob
@@ -0,0 +1,57 @@
1
+ en:
2
+ # Used in distance_of_time_in_words(), distance_of_time_in_words_to_now(), time_ago_in_words()
3
+ datetime:
4
+ distance_in_words:
5
+ half_a_minute: "half a minute"
6
+ less_than_x_seconds:
7
+ one: "1 second" # default was: "less than 1 second"
8
+ other: "%{count} seconds" # default was: "less than %{count} seconds"
9
+ x_seconds:
10
+ one: "1 second"
11
+ other: "%{count} seconds"
12
+ less_than_x_minutes:
13
+ one: "a minute" # default was: "less than a minute"
14
+ other: "%{count} minutes" # default was: "less than %{count} minutes"
15
+ x_minutes:
16
+ one: "1 minute"
17
+ other: "%{count} minutes"
18
+ about_x_hours:
19
+ one: "1 hour" # default was: "about 1 hour"
20
+ other: "%{count} hours" # default was: "about %{count} hours"
21
+ x_days:
22
+ one: "1 day"
23
+ other: "%{count} days"
24
+ about_x_months:
25
+ one: "1 month" # default was: "about 1 month"
26
+ other: "%{count} months" # default was: "about %{count} months"
27
+ x_months:
28
+ one: "1 month"
29
+ other: "%{count} months"
30
+ about_x_years:
31
+ one: "1 year" # default was: "about 1 year"
32
+ other: "%{count} years" # default was: "about %{count} years"
33
+ over_x_years:
34
+ one: "1 year" # default was: "over 1 year"
35
+ other: "%{count} years" # default was: "over %{count} years"
36
+ almost_x_years:
37
+ one: "1 year" # default was: "almost 1 year"
38
+ other: "%{count} years" # default was: "almost %{count} years"
39
+ worker:
40
+ stop:
41
+ success: "Worker successfully stopped."
42
+ failure: "Error attempting to stop worker."
43
+ destroy:
44
+ success: "Worker successfully destroyed."
45
+ failure: "Error attempting to destroy worker."
46
+ pause:
47
+ success: "Worker successfully paused."
48
+ failure: "Error attempting to pause worker."
49
+ resume:
50
+ success: "Worker successfully resumed."
51
+ failure: "Error attempting to resume worker."
52
+ update_all:
53
+ success: "Workers have been %{worker_action}."
54
+ invalid: "Action not allowed."
55
+ job:
56
+ find:
57
+ :failure: "Could not find job with id: %{id}!"
@@ -0,0 +1,28 @@
1
+ RocketJobMissionControl::Engine.routes.draw do
2
+
3
+ resources :jobs, only: [:index, :show, :update, :destroy] do
4
+ member do
5
+ patch :abort
6
+ patch :fail
7
+ patch :pause
8
+ patch :resume
9
+ patch :retry
10
+ end
11
+ collection do
12
+ get :running
13
+ end
14
+ end
15
+
16
+ resources :workers, only: [:index, :destroy] do
17
+ member do
18
+ patch :stop
19
+ patch :pause
20
+ patch :resume
21
+ end
22
+ collection do
23
+ patch :update_all
24
+ end
25
+ end
26
+
27
+ root to: "jobs#index"
28
+ end
@@ -0,0 +1,15 @@
1
+ module RocketJobMissionControl
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace RocketJobMissionControl
4
+
5
+ require 'rocketjob'
6
+ require 'jquery-rails'
7
+ require 'haml'
8
+ require 'mongo'
9
+ require 'mongo_mapper'
10
+ require 'mongo_ha'
11
+ require 'bootstrap-sass'
12
+ require 'sass-rails'
13
+ require 'coffee-rails'
14
+ end
15
+ end
@@ -0,0 +1,3 @@
1
+ module RocketJobMissionControl
2
+ VERSION = "0.9.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ require "rocket_job_mission_control/engine"
2
+
3
+ module RocketJobMissionControl
4
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rocket_job_mission_control do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,47 @@
1
+ require 'rails_helper'
2
+
3
+ module RocketJobMissionControl
4
+ RSpec.describe ApplicationController, type: :controller do
5
+ controller do
6
+ def index
7
+ @time_zone = Time.zone
8
+
9
+ render text: 'Time Zoned'
10
+ end
11
+ end
12
+
13
+ describe "#with_time_zone" do
14
+ let(:session_params) { {} }
15
+ let(:expected_time_zone) { 'UTC' }
16
+
17
+ before do
18
+ get :index, {}, session_params
19
+ end
20
+
21
+ context "with a session present" do
22
+ context "that contains a time zone" do
23
+ let(:expected_time_zone) { 'America/Los_Angeles' }
24
+ let(:session_params) { {'time_zone' => expected_time_zone} }
25
+
26
+ it "sets the time zone correctly" do
27
+ expect(assigns(:time_zone).name).to eq(expected_time_zone)
28
+ end
29
+ end
30
+
31
+ context 'that does not contain a time zone' do
32
+ let(:session_params) { { 'user_id' => '42' } }
33
+
34
+ it 'sets the time zone correctly' do
35
+ expect(assigns(:time_zone).name).to eq(expected_time_zone)
36
+ end
37
+ end
38
+ end
39
+
40
+ context "with no session present" do
41
+ it "sets the time zone correctly" do
42
+ expect(assigns(:time_zone).name).to eq(expected_time_zone)
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,217 @@
1
+ require 'rails_helper'
2
+
3
+ module RocketJobMissionControl
4
+ RSpec.shared_examples "a jobs update controller" do
5
+ describe "with an invalid job id" do
6
+ before do
7
+ allow(RocketJob::Job).to receive(:find).and_return(nil)
8
+ do_action
9
+ end
10
+
11
+ it "redirects" do
12
+ expect(response).to redirect_to(jobs_path)
13
+ end
14
+
15
+ it "adds a flash alert message" do
16
+ expect(flash[:alert]).to eq(I18n.t(:failure, scope: [:job, :find], id: 42))
17
+ end
18
+ end
19
+ end
20
+
21
+ RSpec.describe JobsController do
22
+ routes { Engine.routes }
23
+
24
+ [:pause, :resume, :abort, :retry, :fail].each do |state|
25
+ describe "PATCH ##{state}" do
26
+ it_behaves_like "a jobs update controller" do
27
+ let(:do_action) { patch state, id: 42, job: {id: 42, priority: 12} }
28
+ end
29
+
30
+ describe "with a valid job id" do
31
+ let(:job) { spy(id: 42, to_param: 42) }
32
+
33
+ before do
34
+ allow(RocketJob::Job).to receive(:find).and_return(job)
35
+ patch state, id: 42, job: {id: 42, priority: 12}
36
+ end
37
+
38
+ it "redirects to the job" do
39
+ expect(response).to redirect_to(job_path(42))
40
+ end
41
+
42
+ it "transitions the job" do
43
+ expect(job).to have_received("#{state}!".to_sym)
44
+ end
45
+ end
46
+ end
47
+ end
48
+
49
+ describe "PATCH #update" do
50
+ it_behaves_like "a jobs update controller" do
51
+ let(:do_action) { patch :update, id: 42, job: {id: 42, priority: 12} }
52
+ end
53
+
54
+ describe "with a valid job id" do
55
+ let(:job) { spy(id: 42, to_param: 42) }
56
+
57
+ before do
58
+ allow(RocketJob::Job).to receive(:find).and_return(job)
59
+ patch :update, id: 42, job: {id: 42, priority: 12}
60
+ end
61
+
62
+ it "redirects to the job" do
63
+ expect(response).to redirect_to(job_path(42))
64
+ end
65
+
66
+ it "updates the job correctly" do
67
+ expect(job).to have_received(:update_attributes!).with('priority' => '12')
68
+ end
69
+ end
70
+ end
71
+
72
+ describe "GET #show" do
73
+ let(:result) { spy(sort: []) }
74
+
75
+ before do
76
+ allow(RocketJob::Job).to receive(:limit).and_return(result)
77
+ end
78
+
79
+ describe "with an invalid job id" do
80
+ before do
81
+ allow(RocketJob::Job).to receive(:find).and_return(nil)
82
+ get :show, id: 42
83
+ end
84
+
85
+ it "redirects" do
86
+ expect(response).to redirect_to(jobs_path)
87
+ end
88
+
89
+ it "adds a flash alert message" do
90
+ expect(flash[:alert]).to eq(I18n.t(:failure, scope: [:job, :find], id: 42))
91
+ end
92
+ end
93
+
94
+ describe "with a valid job id" do
95
+ before do
96
+ allow(RocketJob::Job).to receive(:find).and_return('job')
97
+ get :show, id: 42
98
+ end
99
+
100
+ it "succeeds" do
101
+ expect(response.status).to be(200)
102
+ end
103
+
104
+ it "assigns the job" do
105
+ expect(assigns(:job)).to be_present
106
+ end
107
+
108
+ it "assigns the jobs" do
109
+ expect(assigns(:jobs)).to eq([])
110
+ end
111
+
112
+ it "grabs a sorted list of rocket jobs" do
113
+ expect(result).to have_received(:sort).with(created_at: :desc)
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "GET #running" do
119
+ before do
120
+ allow(RocketJob::Job).to receive(:where).and_return([])
121
+ get :running
122
+ end
123
+
124
+ it { expect(response.status).to be(200) }
125
+
126
+ it "queries for running jobs" do
127
+ expect(RocketJob::Job).to have_received(:where).with(state: 'running')
128
+ end
129
+
130
+ it "returns expected jobs" do
131
+ expect(assigns[:jobs]).to eq([])
132
+ end
133
+ end
134
+
135
+ describe "GET #index" do
136
+ describe "with no jobs" do
137
+ let(:result) { spy(sort: []) }
138
+
139
+ before do
140
+ allow(RocketJob::Job).to receive(:limit).and_return(result)
141
+ get :index
142
+ end
143
+
144
+ it "succeeds" do
145
+ expect(response.status).to be(200)
146
+ end
147
+
148
+ it "grabs a sorted list of rocket jobs" do
149
+ expect(result).to have_received(:sort).with(created_at: :desc)
150
+ end
151
+
152
+ it "returns no jobs" do
153
+ expect(assigns(:jobs)).to eq([])
154
+ end
155
+ end
156
+
157
+ describe "with jobs" do
158
+ let(:result) { spy(sort: jobs) }
159
+ let(:jobs) { ['fake_job1', 'fake_job2'] }
160
+
161
+ before do
162
+ allow(RocketJob::Job).to receive(:limit).and_return(result)
163
+ end
164
+
165
+ describe "with no parameters" do
166
+ before { get :index }
167
+
168
+ it "succeeds" do
169
+ expect(response.status).to be(200)
170
+ end
171
+
172
+ it "grabs a sorted list of rocket jobs" do
173
+ expect(result).to have_received(:sort).with(created_at: :desc)
174
+ end
175
+
176
+ it "returns the jobs" do
177
+ expect(assigns(:jobs)).to match_array(jobs)
178
+ end
179
+ end
180
+
181
+ describe "with a state filter" do
182
+ before { get :index, states: states}
183
+
184
+ context "that is empty" do
185
+ let(:states) { [] }
186
+
187
+ it { expect(response.status).to be(200) }
188
+
189
+ it "grabs a sorted list of rocket jobs" do
190
+ expect(result).to have_received(:sort).with(created_at: :desc)
191
+ end
192
+
193
+ it "returns the jobs" do
194
+ expect(assigns(:jobs)).to match_array(jobs)
195
+ end
196
+ end
197
+
198
+ context "with a state" do
199
+ let(:query_spy) { spy(where: jobs) }
200
+ let(:result) { spy(sort: query_spy) }
201
+ let(:states) { ['completed', 'running'] }
202
+
203
+ it { expect(response.status).to be(200) }
204
+
205
+ it "grabs a filtered list of rocket jobs" do
206
+ expect(query_spy).to have_received(:where).with(state: states)
207
+ end
208
+
209
+ it "returns the jobs" do
210
+ expect(assigns(:jobs)).to match_array(jobs)
211
+ end
212
+ end
213
+ end
214
+ end
215
+ end
216
+ end
217
+ end