shipit-engine 0.33.0 → 0.34.0

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +13 -2
  3. data/app/assets/stylesheets/_pages/_deploy.scss +0 -2
  4. data/app/controllers/shipit/api/ccmenu_controller.rb +1 -1
  5. data/app/controllers/shipit/api/deploys_controller.rb +2 -0
  6. data/app/controllers/shipit/api/rollbacks_controller.rb +2 -1
  7. data/app/controllers/shipit/api/stacks_controller.rb +1 -0
  8. data/app/controllers/shipit/deploys_controller.rb +1 -1
  9. data/app/controllers/shipit/stacks_controller.rb +2 -2
  10. data/app/controllers/shipit/tasks_controller.rb +2 -2
  11. data/app/controllers/shipit/webhooks_controller.rb +23 -4
  12. data/app/helpers/shipit/shipit_helper.rb +0 -1
  13. data/app/jobs/shipit/deliver_hook_job.rb +1 -1
  14. data/app/jobs/shipit/github_sync_job.rb +13 -9
  15. data/app/jobs/shipit/update_github_last_deployed_ref_job.rb +1 -1
  16. data/app/models/shipit/anonymous_user.rb +6 -2
  17. data/app/models/shipit/check_run.rb +36 -0
  18. data/app/models/shipit/commit.rb +20 -9
  19. data/app/models/shipit/commit_checks.rb +13 -13
  20. data/app/models/shipit/commit_deployment.rb +3 -3
  21. data/app/models/shipit/commit_deployment_status.rb +3 -3
  22. data/app/models/shipit/deploy.rb +16 -11
  23. data/app/models/shipit/deploy_spec/lerna_discovery.rb +12 -4
  24. data/app/models/shipit/duration.rb +2 -0
  25. data/app/models/shipit/hook.rb +26 -2
  26. data/app/models/shipit/merge_request.rb +9 -7
  27. data/app/models/shipit/pull_request.rb +1 -1
  28. data/app/models/shipit/release_status.rb +1 -1
  29. data/app/models/shipit/repository.rb +9 -3
  30. data/app/models/shipit/review_stack.rb +16 -2
  31. data/app/models/shipit/stack.rb +59 -25
  32. data/app/models/shipit/status/group.rb +1 -1
  33. data/app/models/shipit/task.rb +6 -2
  34. data/app/models/shipit/task_execution_strategy/default.rb +4 -5
  35. data/app/models/shipit/team.rb +4 -2
  36. data/app/models/shipit/user.rb +4 -0
  37. data/app/models/shipit/webhooks/handlers/pull_request/review_stack_adapter.rb +1 -1
  38. data/app/models/shipit/webhooks/handlers/push_handler.rb +4 -1
  39. data/app/serializers/shipit/merge_request_serializer.rb +1 -1
  40. data/app/validators/subset_validator.rb +1 -1
  41. data/app/views/layouts/merge_status.html.erb +1 -1
  42. data/app/views/shipit/stacks/_banners.html.erb +2 -1
  43. data/app/views/shipit/stacks/new.html.erb +1 -1
  44. data/config/secrets.development.example.yml +24 -0
  45. data/config/secrets.development.shopify.yml +20 -9
  46. data/db/migrate/20210325194053_remove_stacks_branch_default.rb +5 -0
  47. data/db/migrate/20210504200438_add_github_updated_at_to_check_runs.rb +5 -0
  48. data/lib/shipit.rb +39 -15
  49. data/lib/shipit/command.rb +7 -6
  50. data/lib/shipit/commands.rb +9 -2
  51. data/lib/shipit/engine.rb +2 -0
  52. data/lib/shipit/flock.rb +8 -1
  53. data/lib/shipit/github_app.rb +7 -5
  54. data/lib/shipit/octokit_iterator.rb +3 -3
  55. data/lib/shipit/simple_message_verifier.rb +2 -2
  56. data/lib/shipit/stack_commands.rb +28 -4
  57. data/lib/shipit/task_commands.rb +6 -0
  58. data/lib/shipit/version.rb +1 -1
  59. data/lib/snippets/publish-lerna-independent-packages +35 -34
  60. data/lib/snippets/publish-lerna-independent-packages-legacy +39 -0
  61. data/test/controllers/api/ccmenu_controller_test.rb +1 -1
  62. data/test/controllers/api/deploys_controller_test.rb +17 -0
  63. data/test/controllers/api/stacks_controller_test.rb +21 -7
  64. data/test/controllers/webhooks_controller_test.rb +26 -11
  65. data/test/dummy/app/assets/config/manifest.js +3 -0
  66. data/test/dummy/config/application.rb +1 -1
  67. data/test/dummy/config/database.yml +9 -0
  68. data/test/dummy/config/environments/development.rb +1 -1
  69. data/test/dummy/config/secrets_double_github_app.yml +79 -0
  70. data/test/dummy/db/schema.rb +5 -4
  71. data/test/dummy/db/seeds.rb +1 -0
  72. data/test/fixtures/payloads/check_suite_master.json +2 -30
  73. data/test/fixtures/payloads/push_master.json +1 -1
  74. data/test/fixtures/payloads/push_not_master.json +1 -1
  75. data/test/fixtures/shipit/commits.yml +2 -2
  76. data/test/fixtures/shipit/hooks.yml +1 -0
  77. data/test/fixtures/shipit/tasks.yml +1 -1
  78. data/test/helpers/json_helper.rb +5 -1
  79. data/test/jobs/github_sync_job_test.rb +2 -1
  80. data/test/models/commit_deployment_status_test.rb +3 -3
  81. data/test/models/commits_test.rb +2 -0
  82. data/test/models/deploy_spec_test.rb +7 -0
  83. data/test/models/deploys_test.rb +18 -0
  84. data/test/models/hook_test.rb +30 -1
  85. data/test/models/merge_request_test.rb +19 -4
  86. data/test/models/shipit/check_run_test.rb +124 -5
  87. data/test/models/shipit/review_stack_test.rb +38 -6
  88. data/test/models/shipit/stacks_test.rb +42 -4
  89. data/test/models/shipit/webhooks/handlers/pull_request/review_stack_adapter_test.rb +24 -0
  90. data/test/models/tasks_test.rb +22 -0
  91. data/test/test_helper.rb +15 -0
  92. data/test/unit/anonymous_user_serializer_test.rb +1 -1
  93. data/test/unit/command_test.rb +5 -0
  94. data/test/unit/commit_serializer_test.rb +1 -1
  95. data/test/unit/deploy_commands_test.rb +70 -14
  96. data/test/unit/deploy_serializer_test.rb +1 -1
  97. data/test/unit/github_app_test.rb +2 -3
  98. data/test/unit/github_apps_test.rb +416 -0
  99. data/test/unit/shipit_deployment_checks_test.rb +77 -0
  100. data/test/unit/shipit_test.rb +14 -0
  101. data/test/unit/user_serializer_test.rb +1 -1
  102. metadata +202 -191
@@ -42,8 +42,9 @@ module Shipit
42
42
 
43
43
  attr_reader :oauth_teams, :domain, :bot_login
44
44
 
45
- def initialize(config)
45
+ def initialize(organization, config)
46
46
  super()
47
+ @organization = organization
47
48
  @config = (config || {}).with_indifferent_access
48
49
  @domain = @config[:domain] || DOMAIN
49
50
  @webhook_secret = @config[:webhook_secret].presence
@@ -92,10 +93,11 @@ module Shipit
92
93
  end
93
94
 
94
95
  def fetch_new_token
96
+ cache_key = @organization.nil? ? '' : "#{@organization.downcase}:"
95
97
  # Rails can add 5 minutes to the cache entry expiration time when any TTL is provided,
96
98
  # so our TTL setting can be lower, and TTL + expires_in should be lower than the GitHub token expiration.
97
99
  Rails.cache.fetch(
98
- 'github:integration:access-token',
100
+ "github:integration:#{cache_key}access-token",
99
101
  expires_in: GITHUB_TOKEN_RAILS_CACHE_LIFETIME,
100
102
  race_condition_ttl: 4.minutes,
101
103
  ) do
@@ -181,15 +183,15 @@ module Shipit
181
183
  end
182
184
 
183
185
  def app_id
184
- @app_id ||= @config.fetch(:app_id)
186
+ @config.fetch(:app_id)
185
187
  end
186
188
 
187
189
  def installation_id
188
- @installation_id ||= @config.fetch(:installation_id)
190
+ @config.fetch(:installation_id)
189
191
  end
190
192
 
191
193
  def private_key
192
- @private_key ||= @config.fetch(:private_key)
194
+ @config.fetch(:private_key)
193
195
  end
194
196
 
195
197
  def authentication_payload
@@ -3,12 +3,12 @@ module Shipit
3
3
  class OctokitIterator
4
4
  include Enumerable
5
5
 
6
- def initialize(relation = nil)
6
+ def initialize(relation = nil, github_api: nil)
7
7
  if relation
8
8
  @response = relation.get(per_page: 100)
9
9
  else
10
- data = yield Shipit.github.api
11
- @response = Shipit.github.api.last_response if data.present?
10
+ data = yield github_api
11
+ @response = github_api.last_response if data.present?
12
12
  end
13
13
  end
14
14
 
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
  module Shipit
3
3
  class SimpleMessageVerifier < ActiveSupport::MessageVerifier
4
- def initialize(secret, options = {})
4
+ def initialize(secret, **options)
5
5
  options[:serializer] ||= ToS
6
- super(secret, options)
6
+ super(secret, **options)
7
7
  end
8
8
 
9
9
  private
@@ -15,19 +15,25 @@ module Shipit
15
15
 
16
16
  def fetch
17
17
  create_directories
18
- if Dir.exist?(@stack.git_path)
18
+ if valid_git_repository?(@stack.git_path)
19
19
  git('fetch', 'origin', '--tags', @stack.branch, env: env, chdir: @stack.git_path)
20
20
  else
21
+ @stack.clear_git_cache!
21
22
  git_clone(@stack.repo_git_url, @stack.git_path, branch: @stack.branch, env: env, chdir: @stack.deploys_path)
22
23
  end
23
24
  end
24
25
 
25
26
  def fetched?(commit)
26
- git_dir = File.join(@stack.git_path, '.git')
27
- if Dir.exist?(git_dir)
27
+ if valid_git_repository?(@stack.git_path)
28
28
  git('rev-parse', '--quiet', '--verify', "#{commit.sha}^{commit}", env: env, chdir: @stack.git_path)
29
29
  else
30
- Command.new('test', '-d', git_dir, env: env, chdir: @stack.deploys_path)
30
+ # When the stack's git cache is not valid, the commit is
31
+ # NOT fetched. To keep the interface of this method
32
+ # consistent, we must return a Shipit::Command whose #success?
33
+ # method returns false - has a non-zero exit status. We utilize
34
+ # the POSIX 'test' command with no arguments which should
35
+ # always have an exit status of 1.
36
+ Command.new('test', env: env, chdir: @stack.deploys_path)
31
37
  end
32
38
  end
33
39
 
@@ -71,6 +77,18 @@ module Shipit
71
77
  end
72
78
  end
73
79
 
80
+ def valid_git_repository?(path)
81
+ path.exist? &&
82
+ !path.empty? &&
83
+ git_cmd_succeeds?(path)
84
+ end
85
+
86
+ def git_cmd_succeeds?(path)
87
+ git("rev-parse", "--git-dir", chdir: path)
88
+ .tap(&:run)
89
+ .success?
90
+ end
91
+
74
92
  def git_clone(url, path, branch: 'master', **kwargs)
75
93
  git('clone', *modern_git_args, '--recursive', '--branch', branch, url, path, **kwargs)
76
94
  end
@@ -83,5 +101,11 @@ module Shipit
83
101
  def create_directories
84
102
  FileUtils.mkdir_p(@stack.deploys_path)
85
103
  end
104
+
105
+ private
106
+
107
+ def github
108
+ Shipit.github(organization: @stack.repository.owner)
109
+ end
86
110
  end
87
111
  end
@@ -88,5 +88,11 @@ module Shipit
88
88
  @task.working_directory
89
89
  end
90
90
  end
91
+
92
+ private
93
+
94
+ def github
95
+ Shipit.github(organization: @stack.repository.owner)
96
+ end
91
97
  end
92
98
  end
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
  module Shipit
3
- VERSION = '0.33.0'
3
+ VERSION = '0.34.0'
4
4
  end
@@ -1,39 +1,40 @@
1
1
  #!/usr/bin/env node
2
- const {resolve} = require('path');
3
- const {execSync} = require('child_process')
4
- const Repository = require(resolve('.', 'node_modules', 'lerna', 'lib', 'Repository'));
5
- const PackageUtilities = require(resolve('.', 'node_modules', 'lerna', 'lib', 'PackageUtilities'));
6
-
7
- function npmTag(version) {
8
- const isNext = ['-beta', '-alpha', '-rc', '-next'].some(distributionType =>
9
- version.includes(distributionType),
10
- );
11
- return isNext ? 'next' : 'latest';
12
- }
2
+ const { execSync } = require("child_process");
13
3
 
14
- const taggedPackages = execSync('git tag --points-at HEAD')
4
+ const taggedPackages = execSync("git tag --points-at HEAD")
15
5
  .toString()
16
6
  .trim()
17
- .split('\n')
18
- .map(tag => {
19
- if (tag.startsWith('@')) {
20
- return `@${tag.split('@')[1]}`;
21
- }
22
- return tag.split('@')[0];
23
- });
24
-
25
- const repository = new Repository('.');
26
- const unsortedPackages = PackageUtilities.getPackages({rootPath: '.', packageConfigs: repository.packageConfigs});
27
- const packages = PackageUtilities.topologicallyBatchPackages(unsortedPackages, true)
28
- .reduce((acc, packageGroup) => [...acc, ...packageGroup], [])
29
- .filter(({name}) => taggedPackages.includes(name));
30
-
31
- packages.forEach(({name, version}) => {
32
- const command = `node_modules/.bin/lerna publish --yes --npm-client=npm --skip-npm=false --skip-git --force-publish=${name} --repo-version=${version} --scope=${name} --npm-tag=${npmTag(
33
- version,
34
- )}`;
35
-
36
- // eslint-disable-next-line no-console
37
- console.log(command);
38
- require('child_process').execSync(command);
7
+ .split("\n");
8
+
9
+ // anything that matches `-` after MAJOR.MINOR.PATCH
10
+ const validPattern = new RegExp(/\d+\.\d+\.\d+-(\w+)/);
11
+
12
+ // Ensure that all releases use the same prerelease suffix.
13
+ // We want to only release all final versions, or all betas etc
14
+ const allPrereleaseSuffixes = taggedPackages.map((version) => {
15
+ const result = validPattern.exec(version);
16
+ return result ? result[1] : '';
39
17
  });
18
+ if ((new Set(allPrereleaseSuffixes)).size > 1) {
19
+ throw Error("All packages must be of the same type of release. Versions cannot be mixed.");
20
+ }
21
+
22
+ // set this using machine.environment.SHIPIT_LERNA_PUBLISH_MECHANISM in your shipit.yml
23
+ const publishMechanism = process.env.SHIPIT_LERNA_PUBLISH_MECHANISM || 'from-git';
24
+
25
+ if (!['from-package', 'from-git'].includes(publishMechanism)) {
26
+ throw Error("SHIPIT_LERNA_PUBLISH_MECHANISM must be 'from-package' or 'from-git'.");
27
+ }
28
+
29
+ const command = [
30
+ "node_modules/.bin/lerna publish",
31
+ publishMechanism,
32
+ "--yes",
33
+ "--dist-tag latest",
34
+ "--pre-dist-tag next",
35
+ ];
36
+
37
+ const commandString = command.join(" ");
38
+
39
+ console.log(`${commandString}`);
40
+ execSync(commandString);
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env node
2
+ const {resolve} = require('path');
3
+ const {execSync} = require('child_process')
4
+ const Repository = require(resolve('.', 'node_modules', 'lerna', 'lib', 'Repository'));
5
+ const PackageUtilities = require(resolve('.', 'node_modules', 'lerna', 'lib', 'PackageUtilities'));
6
+
7
+ function npmTag(version) {
8
+ const isNext = ['-beta', '-alpha', '-rc', '-next'].some(distributionType =>
9
+ version.includes(distributionType),
10
+ );
11
+ return isNext ? 'next' : 'latest';
12
+ }
13
+
14
+ const taggedPackages = execSync('git tag --points-at HEAD')
15
+ .toString()
16
+ .trim()
17
+ .split('\n')
18
+ .map(tag => {
19
+ if (tag.startsWith('@')) {
20
+ return `@${tag.split('@')[1]}`;
21
+ }
22
+ return tag.split('@')[0];
23
+ });
24
+
25
+ const repository = new Repository('.');
26
+ const unsortedPackages = PackageUtilities.getPackages({rootPath: '.', packageConfigs: repository.packageConfigs});
27
+ const packages = PackageUtilities.topologicallyBatchPackages(unsortedPackages, true)
28
+ .reduce((acc, packageGroup) => [...acc, ...packageGroup], [])
29
+ .filter(({name}) => taggedPackages.includes(name));
30
+
31
+ packages.forEach(({name, version}) => {
32
+ const command = `node_modules/.bin/lerna publish --yes --npm-client=npm --skip-npm=false --skip-git --force-publish=${name} --repo-version=${version} --scope=${name} --npm-tag=${npmTag(
33
+ version,
34
+ )}`;
35
+
36
+ // eslint-disable-next-line no-console
37
+ console.log(command);
38
+ require('child_process').execSync(command);
39
+ });
@@ -44,7 +44,7 @@ module Shipit
44
44
  end
45
45
 
46
46
  test "stacks with no deploys render correctly" do
47
- stack = Stack.create!(repository: Repository.new(owner: "foo", name: "bar"))
47
+ stack = Stack.create!(repository: Repository.new(owner: "foo", name: "bar"), branch: 'main')
48
48
  get :show, params: { stack_id: stack.to_param }
49
49
  assert_payload 'lastBuildStatus', 'Success'
50
50
  end
@@ -83,6 +83,23 @@ module Shipit
83
83
  assert_response :conflict
84
84
  end
85
85
 
86
+ test "#create refuses to deploy unsuccessful commits if the require_ci flag is passed" do
87
+ Commit.any_instance.expects(:deployable?).returns(false)
88
+
89
+ assert_no_difference -> { @stack.deploys.count } do
90
+ post :create, params: { stack_id: @stack.to_param, sha: @commit.sha, require_ci: true }
91
+ end
92
+ assert_response :unprocessable_entity
93
+ assert_json 'errors.require_ci', ["Commit is not deployable"]
94
+ end
95
+
96
+ test "#create deploys failing commits if the require_ci flag is not passed" do
97
+ Commit.any_instance.expects(:deployable?).returns(false)
98
+
99
+ post :create, params: { stack_id: @stack.to_param, sha: @commit.sha }
100
+ assert_response :accepted
101
+ end
102
+
86
103
  test "#create refuses to deploy locked stacks" do
87
104
  @stack.update!(lock_reason: 'Something broken')
88
105
 
@@ -23,7 +23,7 @@ module Shipit
23
23
 
24
24
  test "#create fails with invalid stack" do
25
25
  assert_no_difference "Stack.count" do
26
- post :create, params: { repo_owner: 'some', repo_name: 'owner/path' }
26
+ post :create, params: { repo_owner: 'some', repo_name: 'owner/path', branch: 'main' }
27
27
  end
28
28
  assert_response :unprocessable_entity
29
29
  assert_json 'errors', 'repository' => ['is invalid']
@@ -48,12 +48,12 @@ module Shipit
48
48
 
49
49
  assert_no_difference -> { Stack.count } do
50
50
  post :create,
51
- params: {
52
- repo_name: existing_stack.repo_name,
53
- repo_owner: existing_stack.repo_owner,
54
- environment: existing_stack.environment,
55
- branch: existing_stack.branch,
56
- }
51
+ params: {
52
+ repo_name: existing_stack.repo_name,
53
+ repo_owner: existing_stack.repo_owner,
54
+ environment: existing_stack.environment,
55
+ branch: existing_stack.branch,
56
+ }
57
57
  end
58
58
 
59
59
  assert_response :unprocessable_entity
@@ -82,6 +82,20 @@ module Shipit
82
82
  assert_equal true, @stack.continuous_deployment
83
83
  end
84
84
 
85
+ test "#update allows changing the branch name" do
86
+ assert_equal 'master', @stack.branch
87
+
88
+ patch :update, params: {
89
+ id: @stack.to_param,
90
+ branch: 'test',
91
+ }
92
+
93
+ assert_response :ok
94
+ @stack.reload
95
+
96
+ assert_equal 'test', @stack.branch
97
+ end
98
+
85
99
  test "#index returns a list of stacks" do
86
100
  stack = Stack.last
87
101
  get :index
@@ -22,14 +22,15 @@ module Shipit
22
22
  test ":push with the target branch queues a GithubSyncJob" do
23
23
  request.headers['X-Github-Event'] = 'push'
24
24
 
25
+ body = JSON.parse(payload(:push_master)).to_json
25
26
  assert_enqueued_with(job: GithubSyncJob, args: [stack_id: @stack.id]) do
26
- post :create, body: payload(:push_master), as: :json
27
+ post :create, body: body, as: :json
27
28
  end
28
29
  end
29
30
 
30
31
  test ":push does not enqueue a job if not the target branch" do
31
32
  request.headers['X-Github-Event'] = 'push'
32
- params = payload(:push_not_master)
33
+ params = JSON.parse(payload(:push_not_master)).to_json
33
34
  assert_no_enqueued_jobs do
34
35
  post :create, body: params, as: :json
35
36
  end
@@ -40,8 +41,9 @@ module Shipit
40
41
 
41
42
  commit = shipit_commits(:first)
42
43
 
44
+ body = JSON.parse(payload(:status_master)).merge(repository_params).to_json
43
45
  assert_difference 'commit.statuses.count', 1 do
44
- post :create, body: payload(:status_master), as: :json
46
+ post :create, body: body, as: :json
45
47
  end
46
48
 
47
49
  status = commit.statuses.last
@@ -55,14 +57,14 @@ module Shipit
55
57
 
56
58
  test ":state with a unexisting commit respond with 200 OK" do
57
59
  request.headers['X-Github-Event'] = 'status'
58
- params = { 'sha' => 'notarealcommit', 'state' => 'pending', 'branches' => [{ 'name' => 'master' }] }.to_json
60
+ params = { 'sha' => 'notarealcommit', 'state' => 'pending', 'branches' => [{ 'name' => 'master' }] }.merge(repository_params).to_json
59
61
  post :create, body: params, as: :json
60
62
  assert_response :ok
61
63
  end
62
64
 
63
65
  test ":state in an untracked branche bails out" do
64
66
  request.headers['X-Github-Event'] = 'status'
65
- params = { 'sha' => 'notarealcommit', 'state' => 'pending', 'branches' => [] }.to_json
67
+ params = { 'sha' => 'notarealcommit', 'state' => 'pending', 'branches' => [] }.merge(repository_params).to_json
66
68
  post :create, body: params, as: :json
67
69
  assert_response :ok
68
70
  end
@@ -70,8 +72,9 @@ module Shipit
70
72
  test ":check_suite with the target branch queues a RefreshCheckRunsJob" do
71
73
  request.headers['X-Github-Event'] = 'check_suite'
72
74
 
75
+ body = JSON.parse(payload(:check_suite_master)).to_json
73
76
  assert_enqueued_with(job: RefreshCheckRunsJob) do
74
- post :create, body: payload(:check_suite_master), as: :json
77
+ post :create, body: body, as: :json
75
78
  assert_response :ok
76
79
  end
77
80
  end
@@ -88,13 +91,13 @@ module Shipit
88
91
  test "verifies webhook signature" do
89
92
  commit = shipit_commits(:first)
90
93
 
91
- payload = { "sha" => commit.sha, "state" => "pending", "target_url" => "https://ci.example.com/1000/output" }.to_json
94
+ payload = { "sha" => commit.sha, "state" => "pending", "target_url" => "https://ci.example.com/1000/output" }.merge(repository_params).to_json
92
95
  signature = 'sha1=4848deb1c9642cd938e8caa578d201ca359a8249'
93
96
 
94
97
  @request.headers['X-Github-Event'] = 'push'
95
98
  @request.headers['X-Hub-Signature'] = signature
96
99
 
97
- Shipit.github.expects(:verify_webhook_signature).with(signature, payload).returns(false)
100
+ Shipit.github(organization: 'shopify').expects(:verify_webhook_signature).with(signature, payload).returns(false)
98
101
 
99
102
  post :create, body: payload, as: :json
100
103
  assert_response :unprocessable_entity
@@ -157,7 +160,7 @@ module Shipit
157
160
  test "other events trigger custom handlers" do
158
161
  event = 'pull_request'
159
162
  mock_handler = mock
160
- mock_handler.expects(:call).with(pull_request_params.stringify_keys).once
163
+ mock_handler.expects(:call).with(pull_request_params.deep_stringify_keys).once
161
164
  Shipit::Webhooks.handlers["pull_request"] = [mock_handler]
162
165
 
163
166
  @request.headers['X-Github-Event'] = event
@@ -165,20 +168,32 @@ module Shipit
165
168
  assert_response :ok
166
169
  end
167
170
 
171
+ test "events with no handler are dropped" do
172
+ event = 'not_a_real_event'
173
+
174
+ @request.headers['X-Github-Event'] = event
175
+ post :create, body: pull_request_params.to_json, as: :json
176
+ assert_response 204
177
+ end
178
+
168
179
  private
169
180
 
170
181
  def pull_request_params
171
- { action: 'opened', number: 2, pull_request: 'foobar' }
182
+ { action: 'opened', number: 2, pull_request: 'foobar' }.merge(repository_params)
172
183
  end
173
184
 
174
185
  def membership_params
175
- { action: 'added', team: team_params, organization: { login: 'shopify' }, member: { login: 'walrus' } }
186
+ { action: 'added', team: team_params, organization: { login: 'shopify' }, member: { login: 'walrus' } }.merge(repository_params)
176
187
  end
177
188
 
178
189
  def team_params
179
190
  { id: shipit_teams(:shopify_developers).id, slug: 'developers', name: 'Developers', url: 'http://example.com' }
180
191
  end
181
192
 
193
+ def repository_params
194
+ { repository: { owner: { login: 'shopify' } } }
195
+ end
196
+
182
197
  def george
183
198
  stub(
184
199
  id: 42,