shipit-engine 0.32.0 → 0.35.1

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 (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