shipit-engine 0.15.0 → 0.16.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (111) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +34 -1
  3. data/app/assets/javascripts/shipit/page_updater.js.coffee +63 -0
  4. data/app/assets/javascripts/shipit/stacks.js.coffee +9 -21
  5. data/app/assets/stylesheets/_base/_base.scss +2 -2
  6. data/app/assets/stylesheets/_base/_colors.scss +0 -1
  7. data/app/assets/stylesheets/_base/_forms.scss +14 -0
  8. data/app/assets/stylesheets/_pages/_commits.scss +16 -6
  9. data/app/assets/stylesheets/_pages/_settings.scss +8 -0
  10. data/app/assets/stylesheets/_pages/_stacks.scss +1 -1
  11. data/app/controllers/shipit/api/base_controller.rb +7 -3
  12. data/app/controllers/shipit/api/ccmenu_controller.rb +33 -0
  13. data/app/controllers/shipit/api/pull_requests_controller.rb +36 -0
  14. data/app/controllers/shipit/api/stacks_controller.rb +1 -0
  15. data/app/controllers/shipit/ccmenu_url_controller.rb +22 -0
  16. data/app/controllers/shipit/pull_requests_controller.rb +30 -0
  17. data/app/controllers/shipit/stacks_controller.rb +7 -2
  18. data/app/controllers/shipit/webhooks_controller.rb +1 -2
  19. data/app/helpers/shipit/github_url_helper.rb +8 -2
  20. data/app/helpers/shipit/shipit_helper.rb +9 -0
  21. data/app/helpers/shipit/stacks_helper.rb +22 -7
  22. data/app/jobs/shipit/background_job/unique.rb +19 -1
  23. data/app/jobs/shipit/cache_deploy_spec_job.rb +1 -1
  24. data/app/jobs/shipit/merge_pull_requests_job.rb +26 -0
  25. data/app/jobs/shipit/perform_task_job.rb +1 -1
  26. data/app/jobs/shipit/refresh_pull_request_job.rb +8 -0
  27. data/app/models/concerns/shipit/deferred_touch.rb +6 -1
  28. data/app/models/shipit/anonymous_user.rb +4 -0
  29. data/app/models/shipit/application_record.rb +5 -0
  30. data/app/models/shipit/commit.rb +51 -49
  31. data/app/models/shipit/commit_message.rb +32 -0
  32. data/app/models/shipit/deploy.rb +5 -0
  33. data/app/models/shipit/deploy_spec.rb +26 -1
  34. data/app/models/shipit/deploy_spec/file_system.rb +6 -1
  35. data/app/models/shipit/deploy_spec/kubernetes_discovery.rb +10 -13
  36. data/app/models/shipit/deploy_spec/npm_discovery.rb +2 -1
  37. data/app/models/shipit/duration.rb +3 -1
  38. data/app/models/shipit/hook.rb +1 -0
  39. data/app/models/shipit/pull_request.rb +252 -0
  40. data/app/models/shipit/stack.rb +33 -17
  41. data/app/models/shipit/status.rb +1 -16
  42. data/app/models/shipit/status/common.rb +45 -0
  43. data/app/models/shipit/status/group.rb +82 -0
  44. data/app/models/shipit/status/missing.rb +30 -0
  45. data/app/models/shipit/status/unknown.rb +33 -0
  46. data/app/models/shipit/unlimited_api_client.rb +10 -0
  47. data/app/serializers/shipit/commit_serializer.rb +1 -1
  48. data/app/serializers/shipit/pull_request_serializer.rb +20 -0
  49. data/app/serializers/shipit/stack_serializer.rb +6 -2
  50. data/app/views/layouts/shipit.html.erb +41 -39
  51. data/app/views/shipit/ccmenu/project.xml.builder +13 -0
  52. data/app/views/shipit/commits/_commit.html.erb +1 -1
  53. data/app/views/shipit/deploys/_deploy.html.erb +1 -1
  54. data/app/views/shipit/pull_requests/_pull_request.html.erb +29 -0
  55. data/app/views/shipit/pull_requests/index.html.erb +20 -0
  56. data/app/views/shipit/shared/_author.html.erb +7 -0
  57. data/app/views/shipit/stacks/_header.html.erb +5 -0
  58. data/app/views/shipit/stacks/settings.html.erb +13 -0
  59. data/app/views/shipit/stacks/show.html.erb +3 -2
  60. data/app/views/shipit/statuses/_group.html.erb +1 -1
  61. data/app/views/shipit/tasks/_task.html.erb +1 -1
  62. data/config/initializers/inflections.rb +3 -0
  63. data/config/locales/en.yml +1 -3
  64. data/config/routes.rb +8 -0
  65. data/db/migrate/20170130113633_create_shipit_pull_requests.rb +25 -0
  66. data/db/migrate/20170208143657_add_pull_request_number_and_title_to_commits.rb +7 -0
  67. data/db/migrate/20170208154609_backfill_merge_commits.rb +13 -0
  68. data/db/migrate/20170209160355_add_branch_to_pull_requests.rb +5 -0
  69. data/db/migrate/20170215123538_add_merge_queue_enabled_to_stacks.rb +5 -0
  70. data/db/migrate/20170220152410_improve_users_indexing.rb +6 -0
  71. data/db/migrate/20170221102128_improve_tasks_indexing.rb +8 -0
  72. data/db/migrate/20170221130336_add_last_revalidated_at_on_pull_requests.rb +10 -0
  73. data/lib/shipit.rb +2 -0
  74. data/lib/shipit/version.rb +1 -1
  75. data/lib/tasks/cron.rake +1 -0
  76. data/test/controllers/api/ccmenu_controller_test.rb +57 -0
  77. data/test/controllers/api/commits_controller_test.rb +1 -1
  78. data/test/controllers/api/pull_requests_controller_test.rb +59 -0
  79. data/test/controllers/ccmenu_controller_test.rb +33 -0
  80. data/test/controllers/pull_requests_controller_test.rb +31 -0
  81. data/test/controllers/webhooks_controller_test.rb +3 -4
  82. data/test/dummy/config/environments/development.rb +3 -1
  83. data/test/dummy/data/stacks/shopify/junk/production/git/README.md +8 -0
  84. data/test/dummy/data/stacks/shopify/junk/production/git/circle.yml +4 -0
  85. data/test/dummy/data/stacks/shopify/junk/production/git/shipit.yml +4 -0
  86. data/test/dummy/db/development.sqlite3 +0 -0
  87. data/test/dummy/db/schema.rb +45 -11
  88. data/test/dummy/db/seeds.rb +33 -10
  89. data/test/dummy/db/test.sqlite3 +0 -0
  90. data/test/fixtures/shipit/commits.yml +14 -0
  91. data/test/fixtures/shipit/pull_requests.yml +56 -0
  92. data/test/fixtures/shipit/stacks.yml +5 -1
  93. data/test/fixtures/shipit/statuses.yml +8 -0
  94. data/test/helpers/json_helper.rb +16 -14
  95. data/test/jobs/merge_pull_requests_job_test.rb +59 -0
  96. data/test/models/commits_test.rb +104 -49
  97. data/test/{unit → models}/deploy_spec_test.rb +138 -12
  98. data/test/models/deploys_test.rb +10 -4
  99. data/test/models/pull_request_test.rb +197 -0
  100. data/test/models/stacks_test.rb +46 -53
  101. data/test/models/status/group_test.rb +44 -0
  102. data/test/models/status/missing_test.rb +23 -0
  103. data/test/models/status_test.rb +3 -6
  104. data/test/unit/csv_serializer_test.rb +10 -2
  105. metadata +57 -12
  106. data/app/models/shipit/missing_status.rb +0 -21
  107. data/app/models/shipit/status_group.rb +0 -35
  108. data/app/models/shipit/unknown_status.rb +0 -48
  109. data/app/views/shipit/commits/_commit_author.html.erb +0 -7
  110. data/test/models/missing_status_test.rb +0 -23
  111. data/test/models/status_group_test.rb +0 -26
@@ -0,0 +1,20 @@
1
+ <% subscribe events_path(channels: ["stack.#{@stack.id}"]), '.pr-list', '.header' %>
2
+
3
+ <%= render partial: 'shipit/stacks/header', locals: { stack: @stack } %>
4
+
5
+ <div class="wrapper">
6
+ <section>
7
+ <header class="section-header">
8
+ <%= form_tag stack_pull_requests_path(@stack) do %>
9
+ <div class="field-wrapper inline">
10
+ <%= text_field_tag :number_or_url, '', placeholder: 'PR number or URL' %>
11
+ <%= submit_tag 'Request merge', class: 'btn' %>
12
+ </div>
13
+ <% end %>
14
+ </header>
15
+
16
+ <ul class="pr-list">
17
+ <%= render @pull_requests %>
18
+ </ul>
19
+ </section>
20
+ </div>
@@ -0,0 +1,7 @@
1
+ <a href="<%= github_user_url(author.login) %>" class="commit-author">
2
+ <%= github_avatar(author, size: 80, class: 'commit-author__avatar') %>
3
+ <div class="commit-author__name">
4
+ <span class="commit-author__name__real-name"><%= author.name %></span>
5
+ <span class="commit-author__name__username"><%= author.login %></span>
6
+ </div>
7
+ </a>
@@ -22,6 +22,11 @@
22
22
  <li class="nav__list__item">
23
23
  <%= link_to 'Timeline', index_stack_tasks_path(stack) %>
24
24
  </li>
25
+ <% if stack.merge_queue_enabled? %>
26
+ <li class="nav__list__item">
27
+ <%= link_to "Pull Requests (#{stack.pull_requests.queued.count})", stack_pull_requests_path(stack) %>
28
+ </li>
29
+ <% end %>
25
30
  </ul>
26
31
  <ul class="nav__list nav__list--secondary">
27
32
  <% if stack.links.present? %>
@@ -23,6 +23,11 @@
23
23
  <%= f.label :continuous_deployment, 'Enable continuous deployment' %>
24
24
  </div>
25
25
 
26
+ <div class="field-wrapper">
27
+ <%= f.check_box :merge_queue_enabled %>
28
+ <%= f.label :merge_queue_enabled, 'Enable merge queue' %>
29
+ </div>
30
+
26
31
  <div class="field-wrapper">
27
32
  <%= f.check_box :ignore_ci %>
28
33
  <%= f.label :ignore_ci, "Don't require CI to deploy" %>
@@ -66,6 +71,14 @@
66
71
  </table>
67
72
  </div>
68
73
 
74
+ <div class="setting-section setting-ccmenu">
75
+ <h5>Miscellaneous</h5>
76
+ <div class="field-wrapper">
77
+ <label>CCMenu URL</label>
78
+ <input id="ccmenu-url" class="hidden" type="text" disabled />
79
+ </div>
80
+ <%= button_to "Fetch URL", "", class: 'btn', data: {remote: ccmenu_url_url(stack_id: @stack.to_param)} %>
81
+ </div>
69
82
 
70
83
  <div class="setting-section">
71
84
  <h5>Delete this stack</h5>
@@ -1,3 +1,5 @@
1
+ <% subscribe events_path(channels: ["stack.#{@stack.id}"]), '#layout-content' %>
2
+
1
3
  <%= render partial: 'shipit/stacks/header', locals: { stack: @stack } %>
2
4
 
3
5
  <% if !@stack.ignore_ci && !@stack.ci_enabled? %>
@@ -63,8 +65,7 @@
63
65
  </div>
64
66
  <% end %>
65
67
 
66
- <div class="wrapper" data-event-stream="<%= events_path(channels: ["stack.#{@stack.id}"]) %>">
67
-
68
+ <div class="wrapper">
68
69
  <section>
69
70
  <header class="section-header">
70
71
  <h2>Undeployed Commits</h2>
@@ -7,7 +7,7 @@
7
7
  <strong class="status-item__service"><%= group.description %></strong>
8
8
  </div>
9
9
  <% group.statuses.each do |status| %>
10
- <div class="status-item status-item--<%= status.state %> <%= :ignored if status.ignored? %>">
10
+ <div class="status-item status-item--<%= status.state %> <%= :ignored if status.allowed_to_fail? %>">
11
11
  <i class="status-item__icon"></i>
12
12
  <a href="<%= status.target_url %>" target="_blank">
13
13
  <strong class="status-item__service"><%= status.context %></strong>
@@ -1,7 +1,7 @@
1
1
  <%- read_only ||= false -%>
2
2
 
3
3
  <li class="task" id="task-<%= task.id %>" data-status="<%= task.status %>">
4
- <%= render 'shipit/commits/commit_author', commit: task %>
4
+ <%= render 'shipit/shared/author', author: task.author %>
5
5
  <a href="<%= stack_task_path(@stack, task) %>" class="status status--<%= task.status %>" data-tooltip="<%= task.status.capitalize %>">
6
6
  <i class="status__icon"></i>
7
7
  <span class="visually-hidden"><%= task.status %></span>
@@ -0,0 +1,3 @@
1
+ ActiveSupport::Inflector.inflections(:en) do |inflect|
2
+ inflect.acronym 'CCMenu'
3
+ end
@@ -43,9 +43,7 @@ en:
43
43
  rollback: Impossible to detect how to rollback this application. Please define `rollback.override` in your shipit.yml
44
44
  fetch: Impossible to detect how to fetch the deployed revision for this application. Please define `fetch` in your shipit.yml
45
45
  missing_status:
46
- description:
47
- one: "%{missing_statuses} is required for deploy but was not sent"
48
- other: "%{missing_statuses} are required for deploy but were not sent"
46
+ description: "%{context} is required for deploy but was not sent yet."
49
47
  deploys:
50
48
  description: "deploy of %{sha}"
51
49
  rollbacks:
@@ -30,12 +30,14 @@ Shipit::Engine.routes.draw do
30
30
  end
31
31
 
32
32
  scope '/stacks/*stack_id', stack_id: stack_id_format, as: :stack do
33
+ get '/ccmenu' => 'ccmenu#show', as: :ccmenu
33
34
  resource :lock, only: %i(create update destroy)
34
35
  resources :tasks, only: %i(index show) do
35
36
  resource :output, only: :show
36
37
  end
37
38
  resources :deploys, only: %i(create)
38
39
  resources :commits, only: %i(index)
40
+ resources :pull_requests, only: %i(index show update destroy)
39
41
  post '/task/:task_name' => 'tasks#trigger', as: :trigger_task
40
42
  resources :hooks, only: %i(index create show update destroy)
41
43
  end
@@ -43,6 +45,10 @@ Shipit::Engine.routes.draw do
43
45
  resources :hooks, only: %i(index create show update destroy)
44
46
  end
45
47
 
48
+ scope '/ccmenu/*stack_id', stack_id: stack_id_format, as: :ccmenu_url do
49
+ get '/' => 'ccmenu_url#fetch'
50
+ end
51
+
46
52
  # Humans
47
53
  scope '/github/auth/github', as: :github_authentication, controller: :github_authentication do
48
54
  get '/', action: :request
@@ -87,6 +93,8 @@ Shipit::Engine.routes.draw do
87
93
  get :revert
88
94
  end
89
95
  end
96
+
97
+ resources :pull_requests, only: %i(index destroy create)
90
98
  end
91
99
  get '/stacks/:id' => 'stacks#lookup'
92
100
  end
@@ -0,0 +1,25 @@
1
+ class CreateShipitPullRequests < ActiveRecord::Migration[5.0]
2
+ def change
3
+ create_table :pull_requests do |t|
4
+ t.references :stack, foreign_key: true, null: false
5
+ t.integer :number, null: false
6
+ t.string :title, limit: 256
7
+ t.integer :github_id, limit: 8
8
+ t.string :api_url, limit: 1024
9
+ t.string :state
10
+ t.references :head, foreign_key: {to_table: :commits}
11
+ t.boolean :mergeable, null: true
12
+ t.integer :additions, null: false, default: 0
13
+ t.integer :deletions, null: false, default: 0
14
+ t.string :merge_status, null: false, limit: 30
15
+ t.string :rejection_reason, null: true
16
+ t.datetime :merge_requested_at, null: false
17
+ t.references :merge_requested_by, foreign_key: {to_table: :users}
18
+ t.timestamps
19
+
20
+ t.index [:stack_id, :number], unique: true
21
+ t.index [:stack_id, :github_id], unique: true
22
+ t.index [:stack_id, :merge_status]
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,7 @@
1
+ class AddPullRequestNumberAndTitleToCommits < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :commits, :pull_request_number, :integer, null: true
4
+ add_column :commits, :pull_request_title, :string, limit: 1024, null: true
5
+ add_column :commits, :pull_request_id, :integer, null: true, index: true
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ class BackfillMergeCommits < ActiveRecord::Migration[5.0]
2
+ def change
3
+ ActiveRecord::Base.no_touching do
4
+ Shipit::Commit.find_in_batches do |commits|
5
+ commits.each do |commit|
6
+ commit.identify_pull_request
7
+ commit.save!
8
+ end
9
+ print '.'
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ class AddBranchToPullRequests < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :pull_requests, :branch, :string, null: true
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddMergeQueueEnabledToStacks < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_column :stacks, :merge_queue_enabled, :boolean, default: false, null: false
4
+ end
5
+ end
@@ -0,0 +1,6 @@
1
+ class ImproveUsersIndexing < ActiveRecord::Migration[5.0]
2
+ def change
3
+ add_index :users, :updated_at
4
+ add_index :users, :github_id
5
+ end
6
+ end
@@ -0,0 +1,8 @@
1
+ class ImproveTasksIndexing < ActiveRecord::Migration[5.0]
2
+ def change
3
+ # index_active_tasks should superseed this, but for some reason
4
+ # MySQL tend to chose the wrong index. This one while wasting a bit of memory
5
+ # makes it do a better choice.
6
+ add_index :tasks, %i(stack_id allow_concurrency)
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ class AddLastRevalidatedAtOnPullRequests < ActiveRecord::Migration[5.0]
2
+ def up
3
+ add_column :pull_requests, :revalidated_at, :datetime
4
+ Shipit::PullRequest.update_all('revalidated_at = merge_requested_at')
5
+ end
6
+
7
+ def down
8
+ remove_column :pull_requests, :revalidated_at
9
+ end
10
+ end
@@ -56,6 +56,8 @@ module Shipit
56
56
 
57
57
  delegate :table_name_prefix, to: :secrets
58
58
 
59
+ attr_accessor :disable_api_authentication
60
+
59
61
  def app_name
60
62
  @app_name ||= secrets.app_name || Rails.application.class.name.split(':').first || 'Shipit'
61
63
  end
@@ -1,3 +1,3 @@
1
1
  module Shipit
2
- VERSION = '0.15.0'.freeze
2
+ VERSION = '0.16.0'.freeze
3
3
  end
@@ -4,6 +4,7 @@ namespace :cron do
4
4
  Shipit::Stack.refresh_deployed_revisions
5
5
  Shipit::Stack.schedule_continuous_delivery
6
6
  Shipit::GithubStatus.refresh_status
7
+ Shipit::PullRequest.schedule_merges
7
8
  end
8
9
 
9
10
  task hourly: [:rollup, :purge_deliveries, :refresh_users]
@@ -0,0 +1,57 @@
1
+ require 'test_helper'
2
+
3
+ module Shipit
4
+ module Api
5
+ class CCMenuControllerTest < ActionController::TestCase
6
+ setup do
7
+ authenticate!
8
+ @stack = shipit_stacks(:shipit)
9
+ end
10
+
11
+ test "a request with insufficient permissions will render a 403" do
12
+ @client.update!(permissions: [])
13
+ get :show, params: {stack_id: @stack.to_param}
14
+ assert_response :forbidden
15
+ assert_json 'message', 'This operation requires the `read:stack` permission'
16
+ end
17
+
18
+ test "#show renders the xml" do
19
+ get :show, params: {stack_id: @stack.to_param}
20
+ assert_response :ok
21
+ assert_payload 'name', @stack.to_param
22
+ end
23
+
24
+ test "can authenticate with query string token" do
25
+ request.headers['Authorization'] = 'bleh'
26
+ get :show, params: {stack_id: @stack.to_param, token: @client.authentication_token}
27
+ assert_response :ok
28
+ assert_payload 'name', @stack.to_param
29
+ end
30
+
31
+ test "xml contains required attributes" do
32
+ get :show, params: {stack_id: @stack.to_param}
33
+ project = get_project_from_xml(response.body)
34
+ %w(name activity lastBuildStatus lastBuildLabel lastBuildTime webUrl).each do |attribute|
35
+ assert_includes project, attribute, "Response missing required attribute: #{attribute}"
36
+ end
37
+ end
38
+
39
+ test "locked stacks show as failed" do
40
+ @stack.lock('test', @user)
41
+ get :show, params: {stack_id: @stack.to_param}
42
+ assert_payload 'lastBuildStatus', 'Failure'
43
+ end
44
+
45
+ private
46
+
47
+ def get_project_from_xml(xml)
48
+ Hash.from_xml(xml)['Projects']['Project']
49
+ end
50
+
51
+ def assert_payload(k, v)
52
+ @project ||= get_project_from_xml(response.body)
53
+ assert_equal v, @project[k]
54
+ end
55
+ end
56
+ end
57
+ end
@@ -9,7 +9,7 @@ module Shipit
9
9
  end
10
10
 
11
11
  test "#index returns a list of commits" do
12
- commit = @stack.commits.last
12
+ commit = @stack.commits.reachable.last
13
13
 
14
14
  get :index, params: {stack_id: @stack.to_param}
15
15
  assert_response :ok
@@ -0,0 +1,59 @@
1
+ require 'test_helper'
2
+
3
+ module Shipit
4
+ module Api
5
+ class PullRequestsControllerTest < ActionController::TestCase
6
+ setup do
7
+ @stack = shipit_stacks(:shipit)
8
+ @pull_request = shipit_pull_requests(:shipit_pending)
9
+ authenticate!
10
+ end
11
+
12
+ test "#index returns a list of pull requests" do
13
+ pull_request = @stack.pull_requests.last
14
+
15
+ get :index, params: {stack_id: @stack.to_param}
16
+ assert_response :ok
17
+ assert_json '0.id', pull_request.id
18
+ end
19
+
20
+ test "#show returns a single pull requests" do
21
+ get :show, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
22
+ assert_response :ok
23
+ assert_json 'id', @pull_request.id
24
+ end
25
+
26
+ test "#update responds with Accepted if the pull request was queued" do
27
+ assert_enqueued_with(job: RefreshPullRequestJob) do
28
+ put :update, params: {stack_id: @stack.to_param, id: '64'}
29
+ end
30
+ assert_response :accepted
31
+ end
32
+
33
+ test "#update responds with Accepted if the pull request was already queued" do
34
+ assert_enqueued_with(job: RefreshPullRequestJob) do
35
+ put :update, params: {stack_id: @stack.to_param, id: '65'}
36
+ end
37
+ assert_response :accepted
38
+ end
39
+
40
+ test "#update responds with method not allowed if the pull request was already merged" do
41
+ @pull_request.complete!
42
+ put :update, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
43
+ assert_response :method_not_allowed
44
+ assert_json 'message', 'This pull request was already merged.'
45
+ end
46
+
47
+ test "#destroy cancels the merge if the pull request was waiting" do
48
+ delete :destroy, params: {stack_id: @stack.to_param, id: @pull_request.number.to_s}
49
+ assert_response :no_content
50
+ assert_predicate @pull_request.reload, :canceled?
51
+ end
52
+
53
+ test "#destroy silently fail if the pull request was unknown" do
54
+ delete :destroy, params: {stack_id: @stack.to_param, id: '83453489'}
55
+ assert_response :no_content
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,33 @@
1
+ require 'uri'
2
+ require 'test_helper'
3
+
4
+ module Shipit
5
+ class CCMenuUrlControllerTest < ActionController::TestCase
6
+ setup do
7
+ @stack = shipit_stacks(:shipit)
8
+ @user = shipit_users(:walrus)
9
+ session[:user_id] = @user.id
10
+ end
11
+
12
+ test ":fetch returns ok with json" do
13
+ get :fetch, params: {stack_id: @stack.to_param}
14
+ assert_response :ok
15
+ data = JSON.parse(response.body)
16
+ assert_includes data, 'ccmenu_url'
17
+ end
18
+
19
+ test ":fetch creates a read only api client" do
20
+ assert_difference 'ApiClient.count' do
21
+ get :fetch, params: {stack_id: @stack.to_param}
22
+ end
23
+ end
24
+
25
+ test ":fetch url includes api token on query string" do
26
+ get :fetch, params: {stack_id: @stack.to_param}
27
+ data = JSON.parse(response.body)
28
+ client = ApiClient.last
29
+ query = Rack::Utils.parse_nested_query(URI(data['ccmenu_url']).query)
30
+ assert_equal client.authentication_token, query['token']
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,31 @@
1
+ require 'test_helper'
2
+
3
+ module Shipit
4
+ class PullRequestsControllerTest < ActionController::TestCase
5
+ setup do
6
+ @stack = shipit_stacks(:shipit)
7
+ @pr = shipit_pull_requests(:shipit_pending)
8
+ session[:user_id] = shipit_users(:walrus).id
9
+ end
10
+
11
+ test "#index shows pending pull requests" do
12
+ get :index, params: {stack_id: @stack.to_param}
13
+ assert_response :success
14
+ assert_select '.pr-list .pr', @stack.pull_requests.pending.count
15
+ end
16
+
17
+ test "#add can enqueue a pull request" do
18
+ assert_difference -> { PullRequest.count }, +1 do
19
+ post :create, params: {stack_id: @stack.to_param, number_or_url: '#5'}
20
+ end
21
+ assert_redirected_to stack_pull_requests_path(@stack)
22
+ end
23
+
24
+ test "#destroy can cancel a pending pull request" do
25
+ assert_predicate @pr, :pending?
26
+ delete :destroy, params: {stack_id: @stack.to_param, id: @pr.id}
27
+ assert_redirected_to stack_pull_requests_path(@stack)
28
+ assert_predicate @pr.reload, :canceled?
29
+ end
30
+ end
31
+ end