shipit-engine 0.32.0 → 0.35.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (235) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -2
  3. data/app/assets/images/magic-solid.svg +1 -0
  4. data/app/assets/javascripts/shipit/repositories_search.js.coffee +60 -0
  5. data/app/assets/javascripts/shipit/{search.js.coffee → stack_search.js.coffee} +0 -0
  6. data/app/assets/stylesheets/_pages/_deploy.scss +2 -3
  7. data/app/assets/stylesheets/_pages/_repositories.scss +148 -0
  8. data/app/assets/stylesheets/_pages/_stacks.scss +19 -0
  9. data/app/assets/stylesheets/shipit.scss +1 -0
  10. data/app/controllers/shipit/api/ccmenu_controller.rb +1 -1
  11. data/app/controllers/shipit/api/deploys_controller.rb +2 -0
  12. data/app/controllers/shipit/api/{pull_requests_controller.rb → merge_requests_controller.rb} +8 -8
  13. data/app/controllers/shipit/api/rollbacks_controller.rb +2 -1
  14. data/app/controllers/shipit/api/stacks_controller.rb +15 -1
  15. data/app/controllers/shipit/deploys_controller.rb +1 -1
  16. data/app/controllers/shipit/merge_requests_controller.rb +31 -0
  17. data/app/controllers/shipit/merge_status_controller.rb +15 -15
  18. data/app/controllers/shipit/repositories_controller.rb +74 -0
  19. data/app/controllers/shipit/stacks_controller.rb +2 -2
  20. data/app/controllers/shipit/tasks_controller.rb +2 -2
  21. data/app/controllers/shipit/webhooks_controller.rb +23 -4
  22. data/app/helpers/shipit/chunks_helper.rb +2 -2
  23. data/app/helpers/shipit/github_url_helper.rb +8 -0
  24. data/app/helpers/shipit/shipit_helper.rb +0 -1
  25. data/app/helpers/shipit/stacks_helper.rb +4 -0
  26. data/app/jobs/shipit/create_on_github_job.rb +1 -0
  27. data/app/jobs/shipit/deliver_hook_job.rb +1 -1
  28. data/app/jobs/shipit/destroy_repository_job.rb +24 -0
  29. data/app/jobs/shipit/destroy_stack_job.rb +2 -2
  30. data/app/jobs/shipit/github_sync_job.rb +13 -9
  31. data/app/jobs/shipit/perform_task_job.rb +4 -98
  32. data/app/jobs/shipit/process_merge_requests_job.rb +32 -0
  33. data/app/jobs/shipit/refresh_merge_request_job.rb +11 -0
  34. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +1 -1
  35. data/app/models/shipit/anonymous_user.rb +10 -2
  36. data/app/models/shipit/check_run.rb +38 -2
  37. data/app/models/shipit/command_line_user.rb +4 -0
  38. data/app/models/shipit/commit.rb +31 -20
  39. data/app/models/shipit/commit_checks.rb +14 -13
  40. data/app/models/shipit/commit_deployment.rb +3 -3
  41. data/app/models/shipit/commit_deployment_status.rb +3 -3
  42. data/app/models/shipit/deploy.rb +17 -11
  43. data/app/models/shipit/deploy_spec/file_system.rb +11 -5
  44. data/app/models/shipit/deploy_spec/lerna_discovery.rb +12 -4
  45. data/app/models/shipit/deploy_spec.rb +16 -4
  46. data/app/models/shipit/duration.rb +2 -0
  47. data/app/models/shipit/hook.rb +28 -2
  48. data/app/models/shipit/merge_request.rb +304 -0
  49. data/app/models/shipit/provisioning_handler/base.rb +30 -0
  50. data/app/models/shipit/provisioning_handler/unregistered_provisioning_handler.rb +35 -0
  51. data/app/models/shipit/provisioning_handler.rb +32 -0
  52. data/app/models/shipit/pull_request.rb +26 -265
  53. data/app/models/shipit/pull_request_assignment.rb +10 -0
  54. data/app/models/shipit/release_status.rb +1 -1
  55. data/app/models/shipit/repository.rb +63 -3
  56. data/app/models/shipit/review_stack.rb +130 -0
  57. data/app/models/shipit/review_stack_provisioning_queue.rb +39 -0
  58. data/app/models/shipit/rollback.rb +5 -0
  59. data/app/models/shipit/stack.rb +78 -30
  60. data/app/models/shipit/status/group.rb +1 -1
  61. data/app/models/shipit/task.rb +62 -9
  62. data/app/models/shipit/task_execution_strategy/base.rb +20 -0
  63. data/app/models/shipit/task_execution_strategy/default.rb +109 -0
  64. data/app/models/shipit/team.rb +4 -2
  65. data/app/models/shipit/user.rb +10 -1
  66. data/app/models/shipit/webhooks/handlers/pull_request/assigned_handler.rb +74 -0
  67. data/app/models/shipit/webhooks/handlers/pull_request/closed_handler.rb +68 -0
  68. data/app/models/shipit/webhooks/handlers/pull_request/edited_handler.rb +74 -0
  69. data/app/models/shipit/webhooks/handlers/pull_request/label_capturing_handler.rb +127 -0
  70. data/app/models/shipit/webhooks/handlers/pull_request/labeled_handler.rb +106 -0
  71. data/app/models/shipit/webhooks/handlers/pull_request/opened_handler.rb +83 -0
  72. data/app/models/shipit/webhooks/handlers/pull_request/reopened_handler.rb +88 -0
  73. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +103 -0
  74. data/app/models/shipit/webhooks/handlers/pull_request/unlabeled_handler.rb +107 -0
  75. data/app/models/shipit/webhooks/handlers/push_handler.rb +4 -1
  76. data/app/models/shipit/webhooks.rb +10 -0
  77. data/app/serializers/shipit/deploy_serializer.rb +6 -0
  78. data/app/serializers/shipit/merge_request_serializer.rb +21 -0
  79. data/app/serializers/shipit/pull_request_serializer.rb +5 -8
  80. data/app/serializers/shipit/review_stack_serializer.rb +7 -0
  81. data/app/serializers/shipit/stack_serializer.rb +7 -6
  82. data/app/serializers/shipit/tail_task_serializer.rb +10 -2
  83. data/app/serializers/shipit/task_serializer.rb +1 -1
  84. data/app/validators/subset_validator.rb +1 -1
  85. data/app/views/layouts/merge_status.html.erb +1 -1
  86. data/app/views/shipit/merge_requests/_merge_request.html.erb +29 -0
  87. data/app/views/shipit/{pull_requests → merge_requests}/index.html.erb +2 -2
  88. data/app/views/shipit/merge_requests/merge_requests/_pull_request.html.erb +29 -0
  89. data/app/views/shipit/merge_requests/merge_requests/index.html.erb +20 -0
  90. data/app/views/shipit/merge_status/_merge_queue_button.html.erb +3 -3
  91. data/app/views/shipit/merge_status/backlogged.html.erb +1 -1
  92. data/app/views/shipit/merge_status/failure.html.erb +1 -1
  93. data/app/views/shipit/merge_status/locked.html.erb +1 -1
  94. data/app/views/shipit/merge_status/success.html.erb +2 -2
  95. data/app/views/shipit/repositories/_header.html.erb +19 -0
  96. data/app/views/shipit/repositories/index.html.erb +31 -0
  97. data/app/views/shipit/repositories/new.html.erb +23 -0
  98. data/app/views/shipit/repositories/settings.html.erb +53 -0
  99. data/app/views/shipit/repositories/show.html.erb +30 -0
  100. data/app/views/shipit/stacks/_banners.html.erb +15 -1
  101. data/app/views/shipit/stacks/_header.html.erb +5 -2
  102. data/app/views/shipit/stacks/_stack.html.erb +8 -0
  103. data/app/views/shipit/stacks/index.html.erb +2 -1
  104. data/app/views/shipit/stacks/new.html.erb +1 -1
  105. data/app/views/shipit/stacks/settings.html.erb +5 -5
  106. data/app/views/shipit/stacks/show.html.erb +1 -1
  107. data/app/views/shipit/tasks/_task_output.html.erb +1 -1
  108. data/config/routes.rb +15 -5
  109. data/config/secrets.development.example.yml +24 -0
  110. data/config/secrets.development.shopify.yml +20 -9
  111. data/db/migrate/20200706145406_add_review_stacks.rb +12 -0
  112. data/db/migrate/20200804144639_rename_pull_request_to_merge_request.rb +7 -0
  113. data/db/migrate/20200804161512_rename_commits_pull_request_id_to_merge_request_id.rb +5 -0
  114. data/db/migrate/20200813134712_recreate_shipit_pull_requests.rb +22 -0
  115. data/db/migrate/20200813194056_create_pull_request_assignments.rb +8 -0
  116. data/db/migrate/20201001125502_add_provision_pr_stacks_flag_to_repositories.rb +7 -0
  117. data/db/migrate/20201008145809_add_retry_attempt_to_tasks.rb +5 -0
  118. data/db/migrate/20201008152744_add_max_retries_to_tasks.rb +5 -0
  119. data/db/migrate/20210325194053_remove_stacks_branch_default.rb +5 -0
  120. data/db/migrate/20210504200438_add_github_updated_at_to_check_runs.rb +5 -0
  121. data/db/migrate/20210823075617_change_check_runs_github_updated_at_default.rb +5 -0
  122. data/lib/shipit/command.rb +7 -6
  123. data/lib/shipit/commands.rb +18 -5
  124. data/lib/shipit/engine.rb +2 -0
  125. data/lib/shipit/flock.rb +8 -1
  126. data/lib/shipit/github_app.rb +8 -6
  127. data/lib/shipit/octokit_iterator.rb +3 -3
  128. data/lib/shipit/review_stack_commands.rb +8 -0
  129. data/lib/shipit/simple_message_verifier.rb +2 -2
  130. data/lib/shipit/stack_commands.rb +36 -7
  131. data/lib/shipit/task_commands.rb +8 -1
  132. data/lib/shipit/version.rb +1 -1
  133. data/lib/shipit.rb +50 -16
  134. data/lib/snippets/publish-lerna-independent-packages +35 -34
  135. data/lib/snippets/publish-lerna-independent-packages-legacy +39 -0
  136. data/lib/tasks/cron.rake +11 -2
  137. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  138. data/test/controllers/api/deploys_controller_test.rb +17 -0
  139. data/test/controllers/api/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +12 -12
  140. data/test/controllers/api/outputs_controller_test.rb +1 -0
  141. data/test/controllers/api/rollback_controller_test.rb +1 -1
  142. data/test/controllers/api/stacks_controller_test.rb +42 -8
  143. data/test/controllers/{pull_requests_controller_test.rb → merge_requests_controller_test.rb} +6 -6
  144. data/test/controllers/repositories_controller_test.rb +71 -0
  145. data/test/controllers/stacks_controller_test.rb +9 -1
  146. data/test/controllers/tasks_controller_test.rb +14 -2
  147. data/test/controllers/webhooks_controller_test.rb +27 -12
  148. data/test/dummy/app/assets/config/manifest.js +3 -0
  149. data/test/dummy/config/application.rb +7 -2
  150. data/test/dummy/config/database.yml +9 -0
  151. data/test/dummy/config/environments/development.rb +1 -4
  152. data/test/dummy/config/environments/test.rb +0 -5
  153. data/test/dummy/config/secrets_double_github_app.yml +79 -0
  154. data/test/dummy/db/schema.rb +56 -17
  155. data/test/dummy/db/seeds.rb +2 -1
  156. data/test/fixtures/payloads/check_suite_master.json +4 -32
  157. data/test/fixtures/payloads/invalid_pull_request.json +117 -0
  158. data/test/fixtures/payloads/provision_disabled_pull_request.json +454 -0
  159. data/test/fixtures/payloads/pull_request_assigned.json +480 -0
  160. data/test/fixtures/payloads/pull_request_closed.json +454 -0
  161. data/test/fixtures/payloads/pull_request_labeled.json +461 -0
  162. data/test/fixtures/payloads/pull_request_opened.json +454 -0
  163. data/test/fixtures/payloads/pull_request_reopened.json +454 -0
  164. data/test/fixtures/payloads/pull_request_unlabeled.json +454 -0
  165. data/test/fixtures/payloads/pull_request_with_no_repo.json +454 -0
  166. data/test/fixtures/payloads/push_master.json +1 -1
  167. data/test/fixtures/payloads/push_not_master.json +1 -1
  168. data/test/fixtures/shipit/commits.yml +17 -4
  169. data/test/fixtures/shipit/hooks.yml +1 -0
  170. data/test/fixtures/shipit/merge_requests.yml +141 -0
  171. data/test/fixtures/shipit/pull_request_assignments.yml +3 -0
  172. data/test/fixtures/shipit/pull_requests.yml +10 -131
  173. data/test/fixtures/shipit/repositories.yml +1 -0
  174. data/test/fixtures/shipit/stacks.yml +145 -0
  175. data/test/fixtures/shipit/statuses.yml +9 -0
  176. data/test/fixtures/shipit/tasks.yml +4 -1
  177. data/test/fixtures/shipit/users.yml +7 -0
  178. data/test/helpers/json_helper.rb +5 -1
  179. data/test/helpers/payloads_helper.rb +4 -0
  180. data/test/jobs/chunk_rollup_job_test.rb +15 -1
  181. data/test/jobs/destroy_repository_job_test.rb +27 -0
  182. data/test/jobs/github_sync_job_test.rb +2 -1
  183. data/test/jobs/perform_task_job_test.rb +8 -8
  184. data/test/jobs/{merge_pull_requests_job_test.rb → process_merge_requests_job_test.rb} +18 -18
  185. data/test/lib/shipit/deploy_commands_test.rb +16 -0
  186. data/test/lib/shipit/task_commands_test.rb +17 -0
  187. data/test/models/commit_deployment_status_test.rb +3 -3
  188. data/test/models/commits_test.rb +24 -13
  189. data/test/models/deploy_spec_test.rb +64 -24
  190. data/test/models/deploys_test.rb +188 -14
  191. data/test/models/hook_test.rb +30 -1
  192. data/test/models/{pull_request_test.rb → merge_request_test.rb} +49 -34
  193. data/test/models/pull_request_assignment_test.rb +16 -0
  194. data/test/models/shipit/check_run_test.rb +124 -5
  195. data/test/models/shipit/provisioning_handler/base_test.rb +33 -0
  196. data/test/models/shipit/provisioning_handler/unregistered_provisioning_handler_test.rb +49 -0
  197. data/test/models/shipit/provisioning_handler_test.rb +64 -0
  198. data/test/models/shipit/pull_request_test.rb +52 -0
  199. data/test/models/shipit/repository_test.rb +5 -1
  200. data/test/models/shipit/review_stack_provision_status_test.rb +77 -0
  201. data/test/models/shipit/review_stack_provisioning_queue_test.rb +63 -0
  202. data/test/models/shipit/review_stack_test.rb +91 -0
  203. data/test/models/{stacks_test.rb → shipit/stacks_test.rb} +52 -8
  204. data/test/models/shipit/webhooks/handlers/pull_request/assigned_handler_test.rb +45 -0
  205. data/test/models/shipit/webhooks/handlers/pull_request/closed_handler_test.rb +192 -0
  206. data/test/models/shipit/webhooks/handlers/pull_request/edited_handler_test.rb +47 -0
  207. data/test/models/shipit/webhooks/handlers/pull_request/label_capturing_handler_test.rb +209 -0
  208. data/test/models/shipit/webhooks/handlers/pull_request/labeled_handler_test.rb +332 -0
  209. data/test/models/shipit/webhooks/handlers/pull_request/opened_handler_test.rb +238 -0
  210. data/test/models/shipit/webhooks/handlers/pull_request/reopened_handler_test.rb +282 -0
  211. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +107 -0
  212. data/test/models/shipit/webhooks/handlers/pull_request/unlabeled_handler_test.rb +324 -0
  213. data/test/models/shipit/{wehbooks → webhooks}/handlers_test.rb +0 -0
  214. data/test/models/tasks_test.rb +66 -3
  215. data/test/serializers/shipit/pull_request_serializer_test.rb +29 -0
  216. data/test/test_helper.rb +15 -0
  217. data/test/unit/anonymous_user_serializer_test.rb +1 -1
  218. data/test/unit/command_test.rb +8 -3
  219. data/test/unit/commit_serializer_test.rb +1 -1
  220. data/test/unit/deploy_commands_test.rb +73 -17
  221. data/test/unit/deploy_serializer_test.rb +1 -1
  222. data/test/unit/github_app_test.rb +2 -3
  223. data/test/unit/github_apps_test.rb +416 -0
  224. data/test/unit/github_url_helper_test.rb +5 -0
  225. data/test/unit/shipit_deployment_checks_test.rb +77 -0
  226. data/test/unit/shipit_task_execution_strategy_test.rb +47 -0
  227. data/test/unit/shipit_test.rb +14 -0
  228. data/test/unit/user_serializer_test.rb +1 -1
  229. metadata +306 -188
  230. data/app/controllers/shipit/pull_requests_controller.rb +0 -31
  231. data/app/jobs/shipit/merge_pull_requests_job.rb +0 -32
  232. data/app/jobs/shipit/refresh_pull_request_job.rb +0 -11
  233. data/app/views/shipit/pull_requests/_pull_request.html.erb +0 -29
  234. data/test/fixtures/shipit/output_chunks.yml +0 -47
  235. data/test/models/output_chunk_test.rb +0 -21
@@ -34,9 +34,9 @@ module Shipit
34
34
 
35
35
  test "#chunk_output truncates output exceeding the storage limit" do
36
36
  task = shipit_tasks(:shipit)
37
- task.chunks.delete_all
38
- # Dont persist the chunk to the DB, as it may exceed the MySQL max packet size on CI
39
- task.chunks.build(text: 'a' * (Task::OUTPUT_SIZE_LIMIT * 1.1))
37
+ Shipit.redis.del(task.send(:output_key))
38
+
39
+ task.write('a' * (Task::OUTPUT_SIZE_LIMIT * 1.1))
40
40
 
41
41
  output = task.chunk_output
42
42
 
@@ -47,5 +47,68 @@ module Shipit
47
47
  "'#{Task::OUTPUT_TRUNCATED_MESSAGE.chomp}' was not present in the output",
48
48
  )
49
49
  end
50
+
51
+ test "#retry_if_necessary creates a duplicated task object with pending status and nil created_at and ended_at" do
52
+ task = shipit_tasks(:shipit)
53
+ task_stack = task.stack
54
+ task.retry_if_necessary
55
+
56
+ retried_task = task_stack.deploys.last
57
+
58
+ assert_not_equal task.id, retried_task.id
59
+ assert_nil retried_task.started_at
60
+ assert_nil retried_task.ended_at
61
+ assert_equal 'pending', retried_task.status
62
+ end
63
+
64
+ test "#retry_if_necessary does not create a new task object if max_retries is nil" do
65
+ task = shipit_tasks(:shipit2)
66
+
67
+ assert_no_difference 'Task.count', 'No new task should be created' do
68
+ task.retry_if_necessary
69
+ end
70
+ end
71
+
72
+ test "#retry_if_necessary does not create a new task object if the stack is locked" do
73
+ task = shipit_tasks(:shipit2)
74
+ task.stack.lock("test", task.user)
75
+
76
+ assert_no_difference 'Task.count', 'No new task should be created' do
77
+ task.retry_if_necessary
78
+ end
79
+ end
80
+
81
+ test "#retries_configured? returns true when max_retries is not nil and is greater than zero" do
82
+ task_with_three_retries = shipit_tasks(:shipit)
83
+ assert_predicate task_with_three_retries, :retries_configured?
84
+
85
+ task_with_nil_retries = shipit_tasks(:shipit2)
86
+ refute_predicate task_with_nil_retries, :retries_configured?
87
+
88
+ task_with_zero_retries = shipit_tasks(:shipit_restart)
89
+ refute_predicate task_with_zero_retries, :retries_configured?
90
+ end
91
+
92
+ test ".due_for_rollup includes tasks in successful terminal states" do
93
+ task = shipit_tasks(:shipit)
94
+ task.update(
95
+ rolled_up: false,
96
+ created_at: (60 + 1).minutes.ago.to_s(:db),
97
+ status: "success",
98
+ )
99
+
100
+ assert_includes Shipit::Task.due_for_rollup, task
101
+ end
102
+
103
+ test ".due_for_rollup includes tasks in unsuccessful terminal states" do
104
+ task = shipit_tasks(:shipit)
105
+ task.update(
106
+ rolled_up: false,
107
+ created_at: (60 + 1).minutes.ago.to_s(:db),
108
+ status: "error",
109
+ )
110
+
111
+ assert_includes Shipit::Task.due_for_rollup, task
112
+ end
50
113
  end
51
114
  end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "test_helper"
4
+
5
+ module Shipit
6
+ class PulLRequestSerializerTest < ActiveSupport::TestCase
7
+ test "structure" do
8
+ pull_request = shipit_pull_requests(:review_stack_review)
9
+
10
+ serialized = serializer.new(pull_request).as_json
11
+
12
+ assert_includes serialized.keys, :id
13
+ assert_includes serialized.keys, :number
14
+ assert_includes serialized.keys, :title
15
+ assert_includes serialized.keys, :github_id
16
+ assert_includes serialized.keys, :additions
17
+ assert_includes serialized.keys, :deletions
18
+ assert_includes serialized.keys, :state
19
+ assert_includes serialized.keys, :html_url
20
+ assert_includes serialized.keys, :user
21
+ assert_includes serialized.keys, :assignees
22
+ assert_includes serialized.keys, :head
23
+ end
24
+
25
+ def serializer
26
+ Shipit::PullRequestSerializer
27
+ end
28
+ end
29
+ end
data/test/test_helper.rb CHANGED
@@ -1,6 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
  ENV["RAILS_ENV"] ||= "test"
3
3
 
4
+ if Warning.respond_to?(:[]=)
5
+ Warning[:deprecated] = true
6
+ end
7
+
4
8
  require 'simplecov'
5
9
  SimpleCov.start('rails') do
6
10
  enable_coverage :branch
@@ -32,6 +36,17 @@ begin
32
36
  rescue LoadError
33
37
  end
34
38
 
39
+ # FIXME: We need to get rid of active_model_serializers
40
+ # This is a monkey patch for Ruby 2.7+ compatibility
41
+ module ActionController
42
+ module SerializationAssertions
43
+ def process(*, **)
44
+ @serializers = Hash.new(0)
45
+ super
46
+ end
47
+ end
48
+ end
49
+
35
50
  module ActiveSupport
36
51
  class TestCase
37
52
  include PayloadsHelper
@@ -8,7 +8,7 @@ module Shipit
8
8
  serializer = ActiveModel::Serializer.serializer_for(user)
9
9
  assert_equal AnonymousUserSerializer, serializer
10
10
  serialized = serializer.new(user).to_json
11
- assert_json("anonymous", true, document: serialized)
11
+ assert_json_document(serialized, "anonymous", true)
12
12
  end
13
13
  end
14
14
  end
@@ -34,6 +34,11 @@ module Shipit
34
34
  ENV['SHIPIT_TEST'] = previous
35
35
  end
36
36
 
37
+ test "#env cast to strings except for `nil`" do
38
+ command = Command.new('echo foo', env: { 'SOME_PATH' => Pathname.new('/foo'), 'HOST' => nil }, chdir: '.')
39
+ assert_equal({ 'SOME_PATH' => '/foo', 'HOST' => nil }, command.env)
40
+ end
41
+
37
42
  test "#timeout is 5 minutes by default" do
38
43
  command = Command.new('cap $LANG deploy', env: { 'ENVIRONMENT' => 'production' }, chdir: '.')
39
44
  assert_equal 5.minutes.to_i, command.timeout
@@ -59,14 +64,14 @@ module Shipit
59
64
  end
60
65
 
61
66
  test "command not found" do
62
- error = assert_raises Command::NotFound do
67
+ error = assert_raises(Command::NotFound) do
63
68
  Command.new('does-not-exist foo bar', env: {}, chdir: '.').run
64
69
  end
65
70
  assert_equal 'does-not-exist: command not found', error.message
66
71
  end
67
72
 
68
73
  test "permission denied" do
69
- error = assert_raises Command::Denied do
74
+ error = assert_raises(Command::Denied) do
70
75
  Command.new('/etc/passwd foo bar', env: {}, chdir: '.').run
71
76
  end
72
77
  assert_equal '/etc/passwd: Permission denied', error.message
@@ -121,7 +126,7 @@ module Shipit
121
126
  signalled = true
122
127
  break
123
128
  end
124
- sleep 0.1
129
+ sleep(0.1)
125
130
  end
126
131
  signalled
127
132
  end
@@ -10,7 +10,7 @@ module Shipit
10
10
  assert_equal CommitSerializer, serializer
11
11
  serialized = serializer.new(commit).to_json
12
12
 
13
- assert_json("author.name", commit.author.name, document: serialized)
13
+ assert_json_document(serialized, "author.name", commit.author.name)
14
14
  end
15
15
  end
16
16
  end
@@ -4,8 +4,9 @@ require 'test_helper'
4
4
  module Shipit
5
5
  class DeployCommandsTest < ActiveSupport::TestCase
6
6
  def setup
7
- @stack = shipit_stacks(:shipit)
8
7
  @deploy = shipit_deploys(:shipit_pending)
8
+ @stack = @deploy.stack
9
+ @stack.stubs(:clear_git_cache!)
9
10
  @commands = DeployCommands.new(@deploy)
10
11
  @deploy_spec = stub(
11
12
  dependencies_steps!: ['bundle install --some-args'],
@@ -21,42 +22,97 @@ module Shipit
21
22
  end
22
23
 
23
24
  test "#fetch calls git fetch if repository cache already exist" do
24
- Dir.expects(:exist?).with(@stack.git_path).returns(true)
25
+ @stack.git_path.stubs(:exist?).returns(true)
26
+ @stack.git_path.stubs(:empty?).returns(false)
27
+
25
28
  command = @commands.fetch
29
+
26
30
  assert_equal %w(git fetch origin --tags master), command.args
27
31
  end
28
32
 
29
33
  test "#fetch calls git fetch in git_path directory if repository cache already exist" do
30
- Dir.expects(:exist?).with(@stack.git_path).returns(true)
34
+ @stack.git_path.stubs(:exist?).returns(true)
35
+ @stack.git_path.stubs(:empty?).returns(false)
36
+
31
37
  command = @commands.fetch
32
- assert_equal @stack.git_path, command.chdir
38
+
39
+ assert_equal @stack.git_path.to_s, command.chdir
33
40
  end
34
41
 
35
42
  test "#fetch calls git clone if repository cache do not exist" do
36
- Dir.expects(:exist?).with(@stack.git_path).returns(false)
43
+ @stack.git_path.stubs(:exist?).returns(false)
44
+
45
+ command = @commands.fetch
46
+
47
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
48
+ assert_equal expected, command.args.map(&:to_s)
49
+ end
50
+
51
+ test "#fetch calls git clone if repository cache is empty" do
52
+ @stack.git_path.stubs(:exist?).returns(true)
53
+ @stack.git_path.stubs(:empty?).returns(true)
54
+
37
55
  command = @commands.fetch
38
- expected = %W(git clone --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
56
+
57
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
58
+ assert_equal expected, command.args
59
+ end
60
+
61
+ test "#fetch calls git clone if repository cache corrupt" do
62
+ @stack.git_path.stubs(:exist?).returns(true)
63
+ @stack.git_path.stubs(:empty?).returns(false)
64
+ StackCommands.any_instance.expects(:git_cmd_succeeds?)
65
+ .with(@stack.git_path)
66
+ .returns(false)
67
+
68
+ command = @commands.fetch
69
+
70
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
71
+ assert_equal expected, command.args
72
+ end
73
+
74
+ test "#fetch clears a corrupted git stash before cloning" do
75
+ @stack.expects(:clear_git_cache!)
76
+ @stack.git_path.stubs(:exist?).returns(true)
77
+ @stack.git_path.stubs(:empty?).returns(false)
78
+ StackCommands.any_instance.expects(:git_cmd_succeeds?)
79
+ .with(@stack.git_path)
80
+ .returns(false)
81
+
82
+ command = @commands.fetch
83
+
84
+ expected = %W(git clone --quiet --single-branch --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
39
85
  assert_equal expected, command.args
40
86
  end
41
87
 
42
88
  test "#fetch does not use --single-branch if git is outdated" do
43
- Dir.expects(:exist?).with(@stack.git_path).returns(false)
89
+ @stack.git_path.stubs(:exist?).returns(false)
44
90
  StackCommands.stubs(git_version: Gem::Version.new('1.7.2.30'))
91
+
45
92
  command = @commands.fetch
46
- expected = %W(git clone --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
47
- assert_equal expected, command.args
93
+
94
+ expected = %W(git clone --quiet --recursive --branch master #{@stack.repo_git_url} #{@stack.git_path})
95
+ assert_equal expected, command.args.map(&:to_s)
48
96
  end
49
97
 
50
98
  test "#fetch calls git fetch in base_path directory if repository cache do not exist" do
51
- Dir.expects(:exist?).with(@stack.git_path).returns(false)
99
+ @stack.git_path.stubs(:exist?).returns(false)
100
+
52
101
  command = @commands.fetch
53
- assert_equal @stack.deploys_path, command.chdir
102
+
103
+ assert_equal @stack.deploys_path.to_s, command.chdir
54
104
  end
55
105
 
56
106
  test "#fetch merges Shipit.env in ENVIRONMENT" do
57
107
  Shipit.stubs(:env).returns("SPECIFIC_CONFIG" => 5)
58
108
  command = @commands.fetch
59
- assert_equal 5, command.env["SPECIFIC_CONFIG"]
109
+ assert_equal '5', command.env["SPECIFIC_CONFIG"]
110
+ end
111
+
112
+ test "#env uses the correct Github token for a stack" do
113
+ Shipit.github(organization: 'shopify').stubs(:token).returns('aS3cr3Tt0kEn')
114
+ command = @commands.fetch
115
+ assert_equal 'aS3cr3Tt0kEn', command.env["GITHUB_TOKEN"]
60
116
  end
61
117
 
62
118
  test "#clone clones the repository cache into the working directory" do
@@ -65,20 +121,20 @@ module Shipit
65
121
  clone_args = [
66
122
  'git', 'clone', '--quiet',
67
123
  '--local', '--origin', 'cache',
68
- @stack.git_path, @deploy.working_directory
124
+ @stack.git_path.to_s, @deploy.working_directory,
69
125
  ]
70
126
  assert_equal clone_args, commands.first.args
71
- assert_equal ['git', 'remote', 'add', 'origin', @stack.repo_git_url], commands.second.args
127
+ assert_equal ['git', 'remote', 'add', 'origin', @stack.repo_git_url.to_s], commands.second.args
72
128
  end
73
129
 
74
130
  test "#clone clones the repository cache from the deploys_path" do
75
131
  commands = @commands.clone
76
- assert_equal @stack.deploys_path, commands.first.chdir
132
+ assert_equal @stack.deploys_path.to_s, commands.first.chdir
77
133
  end
78
134
 
79
135
  test "#checkout checks out the deployed commit" do
80
136
  command = @commands.checkout(@deploy.until_commit)
81
- assert_equal ['git', 'checkout', @deploy.until_commit.sha], command.args
137
+ assert_equal ['git', '-c', 'advice.detachedHead=false', 'checkout', @deploy.until_commit.sha], command.args
82
138
  end
83
139
 
84
140
  test "#checkout checks out the deployed commit from the working directory" do
@@ -182,7 +238,7 @@ module Shipit
182
238
  test "#install_dependencies merges Shipit.env in ENVIRONMENT" do
183
239
  Shipit.stubs(:env).returns("SPECIFIC_CONFIG" => 5)
184
240
  command = @commands.install_dependencies.first
185
- assert_equal 5, command.env["SPECIFIC_CONFIG"]
241
+ assert_equal '5', command.env["SPECIFIC_CONFIG"]
186
242
  end
187
243
 
188
244
  test "#install_dependencies merges machine_env in ENVIRONMENT" do
@@ -11,7 +11,7 @@ module Shipit
11
11
  assert_equal DeploySerializer, serializer
12
12
  serialized = serializer.new(deploy).to_json
13
13
 
14
- assert_json("commits.0.author.name", first_commit_author.name, document: serialized)
14
+ assert_json_document(serialized, "commits.0.author.name", first_commit_author.name)
15
15
  end
16
16
  end
17
17
  end
@@ -18,7 +18,7 @@ module Shipit
18
18
 
19
19
  test "#initialize doesn't raise if given an empty config" do
20
20
  assert_nothing_raised do
21
- GitHubApp.new({})
21
+ GitHubApp.new(nil, {})
22
22
  end
23
23
  end
24
24
 
@@ -183,7 +183,6 @@ module Shipit
183
183
  .any_instance
184
184
  .expects(:create_app_installation_access_token).with(config[:installation_id], anything)
185
185
  .returns(second_token)
186
-
187
186
  first_token = valid_app.token
188
187
 
189
188
  first_cached_token = Rails.cache.fetch(@token_cache_key)
@@ -199,7 +198,7 @@ module Shipit
199
198
  private
200
199
 
201
200
  def app(extra_config = {})
202
- GitHubApp.new(default_config.deep_merge(extra_config))
201
+ GitHubApp.new(nil, default_config.deep_merge(extra_config))
203
202
  end
204
203
 
205
204
  def default_config
@@ -0,0 +1,416 @@
1
+ # frozen_string_literal: true
2
+ require 'test_helper'
3
+
4
+ module Shipit
5
+ class GitHubAppsTestOrgOne < ActiveSupport::TestCase
6
+ setup do
7
+ @organization = "OrgOne"
8
+ @github = app(@organization)
9
+ @enterprise = app(@organization, domain: 'github.example.com')
10
+ @rails_env = Rails.env
11
+ @token_cache_key = "github:integration:#{@organization.downcase}:access-token"
12
+ Rails.cache.delete(@token_cache_key)
13
+ end
14
+
15
+ teardown do
16
+ Rails.env = @rails_env
17
+ Rails.cache.delete(@token_cache_key)
18
+ end
19
+
20
+ test "#domain defaults to github.com" do
21
+ assert_equal 'github.com', @github.domain
22
+ end
23
+
24
+ test "#url returns the HTTPS url to the github installation" do
25
+ assert_equal 'https://github.example.com', @enterprise.url
26
+ assert_equal 'https://github.example.com/foo/bar', @enterprise.url('/foo/bar')
27
+ assert_equal 'https://github.example.com/foo/bar/baz', @enterprise.url('foo/bar', 'baz')
28
+ end
29
+
30
+ test "#new_client retruns an Octokit::Client configured to use the github installation" do
31
+ assert_equal 'https://github.example.com/', @enterprise.new_client.web_endpoint
32
+ assert_equal 'https://github.example.com/api/v3/', @enterprise.new_client.api_endpoint
33
+ end
34
+
35
+ test "#oauth_config.last[:client_options] is nil if domain is not overriden" do
36
+ assert_nil @github.oauth_config.last[:client_options][:site]
37
+ end
38
+
39
+ test "#oauth_config.last[:client_options] returns Enterprise endpoint if domain is overriden" do
40
+ assert_equal 'https://github.example.com/api/v3/', @enterprise.oauth_config.last[:client_options][:site]
41
+ end
42
+
43
+ test "#initialize doesn't raise if given an empty config" do
44
+ assert_nothing_raised do
45
+ GitHubApp.new(@organization, {})
46
+ end
47
+ end
48
+
49
+ test "#api_status" do
50
+ stub_request(:get, "https://www.githubstatus.com/api/v2/components.json").to_return(
51
+ status: 200,
52
+ body: %(
53
+ {
54
+ "page":{},
55
+ "components":[
56
+ {
57
+ "id":"brv1bkgrwx7q",
58
+ "name":"API Requests",
59
+ "status":"operational",
60
+ "created_at":"2017-01-31T20:01:46.621Z",
61
+ "updated_at":"2019-07-23T18:41:18.197Z",
62
+ "position":2,
63
+ "description":"Requests for GitHub APIs",
64
+ "showcase":false,
65
+ "group_id":null,
66
+ "page_id":"kctbh9vrtdwd",
67
+ "group":false,
68
+ "only_show_if_degraded":false
69
+ }
70
+ ]
71
+ }
72
+ ),
73
+ )
74
+ assert_equal "operational", app(@organization).api_status[:status]
75
+ end
76
+
77
+ test "#github token is refreshed after expiration" do
78
+ Rails.env = 'not_test'
79
+ config = {
80
+ app_id: "test_id",
81
+ installation_id: "test_installation_id",
82
+ private_key: "test_private_key",
83
+ }
84
+ initial_token = OpenStruct.new(
85
+ token: "some_initial_github_token",
86
+ expires_at: Time.now.utc + 60.minutes,
87
+ )
88
+ second_token = OpenStruct.new(
89
+ token: "some_new_github_token",
90
+ expires_at: initial_token.expires_at + 60.minutes,
91
+ )
92
+ auth_payload = "test_auth_payload"
93
+
94
+ GitHubApp.any_instance.expects(:authentication_payload).twice.returns(auth_payload)
95
+ valid_app = app(@organization, config)
96
+
97
+ freeze_time do
98
+ Octokit::Client
99
+ .any_instance
100
+ .expects(:create_app_installation_access_token).twice.with(config[:installation_id], anything)
101
+ .returns(initial_token, second_token)
102
+
103
+ initial_token = valid_app.token
104
+ initial_cached_token = Rails.cache.fetch(@token_cache_key)
105
+ assert_equal initial_token, initial_cached_token.to_s
106
+
107
+ travel 5.minutes
108
+ assert_equal initial_token, valid_app.token
109
+
110
+ travel_to initial_cached_token.expires_at + 5.minutes
111
+ assert_equal second_token.token, valid_app.token
112
+ end
113
+ end
114
+
115
+ test "#github token is refreshed in refresh window before expiry" do
116
+ Rails.env = 'not_test'
117
+ config = {
118
+ app_id: "test_id",
119
+ installation_id: "test_installation_id",
120
+ private_key: "test_private_key",
121
+ }
122
+ initial_token = OpenStruct.new(
123
+ token: "some_initial_github_token",
124
+ expires_at: Time.now.utc + 60.minutes,
125
+ )
126
+ second_token = OpenStruct.new(
127
+ token: "some_new_github_token",
128
+ expires_at: initial_token.expires_at + 60.minutes,
129
+ )
130
+ auth_payload = "test_auth_payload"
131
+
132
+ GitHubApp.any_instance.expects(:authentication_payload).twice.returns(auth_payload)
133
+ valid_app = app(@organization, config)
134
+
135
+ freeze_time do
136
+ Octokit::Client
137
+ .any_instance
138
+ .expects(:create_app_installation_access_token).twice.with(config[:installation_id], anything)
139
+ .returns(initial_token, second_token)
140
+
141
+ initial_token = valid_app.token
142
+ initial_cached_token = Rails.cache.fetch(@token_cache_key)
143
+ assert_equal initial_token, initial_cached_token.to_s
144
+
145
+ # Travel forward, but before the token is refreshed, so the cached value should be the same.
146
+ travel 40.minutes
147
+ assert_equal initial_token, valid_app.token
148
+
149
+ # Travel to when the token should refresh, but is not expired, which should result in our cache.fetch update block.
150
+ travel 15.minutes
151
+ updated_token = valid_app.token
152
+ assert_not_equal initial_token, updated_token
153
+
154
+ cached_token = Rails.cache.fetch(@token_cache_key)
155
+ assert_operator cached_token.expires_at, :>, initial_cached_token.expires_at
156
+ end
157
+ end
158
+
159
+ test "#github token is missing refresh_at field" do
160
+ # $debugging = true
161
+ Rails.env = 'not_test'
162
+ config = {
163
+ app_id: "test_id",
164
+ installation_id: "test_installation_id",
165
+ private_key: "test_private_key",
166
+ }
167
+ initial_cached_token = Shipit::GitHubApp::Token.new("some_initial_github_token", Time.now.utc - 1.minute)
168
+ initial_cached_token.instance_variable_set(:@refresh_at, nil)
169
+
170
+ second_token = OpenStruct.new(
171
+ token: "some_new_github_token",
172
+ expires_at: initial_cached_token.expires_at + 60.minutes,
173
+ )
174
+ auth_payload = "test_auth_payload"
175
+
176
+ GitHubApp.any_instance.expects(:authentication_payload).returns(auth_payload)
177
+ valid_app = app(@organization, config)
178
+
179
+ freeze_time do
180
+ valid_app.instance_variable_set(:@token, initial_cached_token)
181
+ Rails.cache.write(@token_cache_key, initial_cached_token, expires_in: 1.minute)
182
+
183
+ Octokit::Client
184
+ .any_instance
185
+ .expects(:create_app_installation_access_token).with(config[:installation_id], anything)
186
+ .returns(second_token)
187
+
188
+ first_token = valid_app.token
189
+
190
+ first_cached_token = Rails.cache.fetch(@token_cache_key)
191
+ assert_equal first_token, first_cached_token.to_s
192
+
193
+ travel_to first_cached_token.expires_at + 5.minutes
194
+ new_token = valid_app.token
195
+
196
+ assert_equal second_token.token, new_token
197
+ end
198
+ end
199
+
200
+ private
201
+
202
+ def app(organization, extra_config = {})
203
+ GitHubApp.new(organization, double_github_app_config.deep_merge(extra_config))
204
+ end
205
+
206
+ def double_github_app_config
207
+ YAML.load_file('test/dummy/config/secrets_double_github_app.yml')
208
+ end
209
+ end
210
+
211
+ class GitHubAppsTestOrgTwo < ActiveSupport::TestCase
212
+ setup do
213
+ @organization = "OrgTwo"
214
+ @github = app(@organization)
215
+ @enterprise = app(@organization, domain: 'github.example.com')
216
+ @rails_env = Rails.env
217
+ @token_cache_key = "github:integration:#{@organization.downcase}:access-token"
218
+ Rails.cache.delete(@token_cache_key)
219
+ end
220
+
221
+ teardown do
222
+ Rails.env = @rails_env
223
+ Rails.cache.delete(@token_cache_key)
224
+ end
225
+
226
+ test "#domain defaults to github.com" do
227
+ assert_equal 'github.com', @github.domain
228
+ end
229
+
230
+ test "#url returns the HTTPS url to the github installation" do
231
+ assert_equal 'https://github.example.com', @enterprise.url
232
+ assert_equal 'https://github.example.com/foo/bar', @enterprise.url('/foo/bar')
233
+ assert_equal 'https://github.example.com/foo/bar/baz', @enterprise.url('foo/bar', 'baz')
234
+ end
235
+
236
+ test "#new_client retruns an Octokit::Client configured to use the github installation" do
237
+ assert_equal 'https://github.example.com/', @enterprise.new_client.web_endpoint
238
+ assert_equal 'https://github.example.com/api/v3/', @enterprise.new_client.api_endpoint
239
+ end
240
+
241
+ test "#oauth_config.last[:client_options] is nil if domain is not overriden" do
242
+ assert_nil @github.oauth_config.last[:client_options][:site]
243
+ end
244
+
245
+ test "#oauth_config.last[:client_options] returns Enterprise endpoint if domain is overriden" do
246
+ assert_equal 'https://github.example.com/api/v3/', @enterprise.oauth_config.last[:client_options][:site]
247
+ end
248
+
249
+ test "#initialize doesn't raise if given an empty config" do
250
+ assert_nothing_raised do
251
+ GitHubApp.new(@organization, {})
252
+ end
253
+ end
254
+
255
+ test "#api_status" do
256
+ stub_request(:get, "https://www.githubstatus.com/api/v2/components.json").to_return(
257
+ status: 200,
258
+ body: %(
259
+ {
260
+ "page":{},
261
+ "components":[
262
+ {
263
+ "id":"brv1bkgrwx7q",
264
+ "name":"API Requests",
265
+ "status":"operational",
266
+ "created_at":"2017-01-31T20:01:46.621Z",
267
+ "updated_at":"2019-07-23T18:41:18.197Z",
268
+ "position":2,
269
+ "description":"Requests for GitHub APIs",
270
+ "showcase":false,
271
+ "group_id":null,
272
+ "page_id":"kctbh9vrtdwd",
273
+ "group":false,
274
+ "only_show_if_degraded":false
275
+ }
276
+ ]
277
+ }
278
+ ),
279
+ )
280
+ assert_equal "operational", app(@organization).api_status[:status]
281
+ end
282
+
283
+ test "#github token is refreshed after expiration" do
284
+ Rails.env = 'not_test'
285
+ config = {
286
+ app_id: "test_id",
287
+ installation_id: "test_installation_id",
288
+ private_key: "test_private_key",
289
+ }
290
+ initial_token = OpenStruct.new(
291
+ token: "some_initial_github_token",
292
+ expires_at: Time.now.utc + 60.minutes,
293
+ )
294
+ second_token = OpenStruct.new(
295
+ token: "some_new_github_token",
296
+ expires_at: initial_token.expires_at + 60.minutes,
297
+ )
298
+ auth_payload = "test_auth_payload"
299
+
300
+ GitHubApp.any_instance.expects(:authentication_payload).twice.returns(auth_payload)
301
+ valid_app = app(@organization, config)
302
+
303
+ freeze_time do
304
+ Octokit::Client
305
+ .any_instance
306
+ .expects(:create_app_installation_access_token).twice.with(config[:installation_id], anything)
307
+ .returns(initial_token, second_token)
308
+
309
+ initial_token = valid_app.token
310
+ initial_cached_token = Rails.cache.fetch(@token_cache_key)
311
+ assert_equal initial_token, initial_cached_token.to_s
312
+
313
+ travel 5.minutes
314
+ assert_equal initial_token, valid_app.token
315
+
316
+ travel_to initial_cached_token.expires_at + 5.minutes
317
+ assert_equal second_token.token, valid_app.token
318
+ end
319
+ end
320
+
321
+ test "#github token is refreshed in refresh window before expiry" do
322
+ Rails.env = 'not_test'
323
+ config = {
324
+ app_id: "test_id",
325
+ installation_id: "test_installation_id",
326
+ private_key: "test_private_key",
327
+ }
328
+ initial_token = OpenStruct.new(
329
+ token: "some_initial_github_token",
330
+ expires_at: Time.now.utc + 60.minutes,
331
+ )
332
+ second_token = OpenStruct.new(
333
+ token: "some_new_github_token",
334
+ expires_at: initial_token.expires_at + 60.minutes,
335
+ )
336
+ auth_payload = "test_auth_payload"
337
+
338
+ GitHubApp.any_instance.expects(:authentication_payload).twice.returns(auth_payload)
339
+ valid_app = app(@organization, config)
340
+
341
+ freeze_time do
342
+ Octokit::Client
343
+ .any_instance
344
+ .expects(:create_app_installation_access_token).twice.with(config[:installation_id], anything)
345
+ .returns(initial_token, second_token)
346
+
347
+ initial_token = valid_app.token
348
+ initial_cached_token = Rails.cache.fetch(@token_cache_key)
349
+ assert_equal initial_token, initial_cached_token.to_s
350
+
351
+ # Travel forward, but before the token is refreshed, so the cached value should be the same.
352
+ travel 40.minutes
353
+ assert_equal initial_token, valid_app.token
354
+
355
+ # Travel to when the token should refresh, but is not expired, which should result in our cache.fetch update block.
356
+ travel 15.minutes
357
+ updated_token = valid_app.token
358
+ assert_not_equal initial_token, updated_token
359
+
360
+ cached_token = Rails.cache.fetch(@token_cache_key)
361
+ assert_operator cached_token.expires_at, :>, initial_cached_token.expires_at
362
+ end
363
+ end
364
+
365
+ test "#github token is missing refresh_at field" do
366
+ # $debugging = true
367
+ Rails.env = 'not_test'
368
+ config = {
369
+ app_id: "test_id",
370
+ installation_id: "test_installation_id",
371
+ private_key: "test_private_key",
372
+ }
373
+ initial_cached_token = Shipit::GitHubApp::Token.new("some_initial_github_token", Time.now.utc - 1.minute)
374
+ initial_cached_token.instance_variable_set(:@refresh_at, nil)
375
+
376
+ second_token = OpenStruct.new(
377
+ token: "some_new_github_token",
378
+ expires_at: initial_cached_token.expires_at + 60.minutes,
379
+ )
380
+ auth_payload = "test_auth_payload"
381
+
382
+ GitHubApp.any_instance.expects(:authentication_payload).returns(auth_payload)
383
+ valid_app = app(@organization, config)
384
+
385
+ freeze_time do
386
+ valid_app.instance_variable_set(:@token, initial_cached_token)
387
+ Rails.cache.write(@token_cache_key, initial_cached_token, expires_in: 1.minute)
388
+
389
+ Octokit::Client
390
+ .any_instance
391
+ .expects(:create_app_installation_access_token).with(config[:installation_id], anything)
392
+ .returns(second_token)
393
+
394
+ first_token = valid_app.token
395
+
396
+ first_cached_token = Rails.cache.fetch(@token_cache_key)
397
+ assert_equal first_token, first_cached_token.to_s
398
+
399
+ travel_to first_cached_token.expires_at + 5.minutes
400
+ new_token = valid_app.token
401
+
402
+ assert_equal second_token.token, new_token
403
+ end
404
+ end
405
+
406
+ private
407
+
408
+ def app(organization, extra_config = {})
409
+ GitHubApp.new(organization, double_github_app_config.deep_merge(extra_config))
410
+ end
411
+
412
+ def double_github_app_config
413
+ YAML.load_file('test/dummy/config/secrets_double_github_app.yml')
414
+ end
415
+ end
416
+ end