pact_broker 2.27.6 → 2.29.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 (68) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +4 -1
  3. data/.travis.yml +1 -2
  4. data/CHANGELOG.md +38 -0
  5. data/MATRIX.md +4 -0
  6. data/README.md +3 -2
  7. data/Rakefile +1 -2
  8. data/config/database.yml +5 -0
  9. data/lib/pact_broker/api/decorators/matrix_decorator.rb +7 -1
  10. data/lib/pact_broker/api/decorators/reason_decorator.rb +50 -0
  11. data/lib/pact_broker/api/resources/base_resource.rb +1 -1
  12. data/lib/pact_broker/api/resources/error_handler.rb +32 -9
  13. data/lib/pact_broker/app.rb +16 -1
  14. data/lib/pact_broker/domain/webhook_request.rb +1 -1
  15. data/lib/pact_broker/matrix/deployment_status_summary.rb +123 -44
  16. data/lib/pact_broker/matrix/head_row.rb +20 -0
  17. data/lib/pact_broker/matrix/integration.rb +49 -7
  18. data/lib/pact_broker/matrix/query_results.rb +3 -2
  19. data/lib/pact_broker/matrix/query_results_with_deployment_status_summary.rb +2 -2
  20. data/lib/pact_broker/matrix/reason.rb +74 -0
  21. data/lib/pact_broker/matrix/repository.rb +97 -61
  22. data/lib/pact_broker/matrix/resolved_selector.rb +126 -0
  23. data/lib/pact_broker/matrix/row.rb +8 -1
  24. data/lib/pact_broker/matrix/service.rb +2 -16
  25. data/lib/pact_broker/pacts/repository.rb +15 -5
  26. data/lib/pact_broker/repositories/helpers.rb +3 -2
  27. data/lib/pact_broker/ui/views/index/_navbar.haml +14 -0
  28. data/lib/pact_broker/ui/views/index/show-with-tags.haml +1 -12
  29. data/lib/pact_broker/ui/views/index/show.haml +1 -12
  30. data/lib/pact_broker/ui/views/layouts/main.haml +3 -0
  31. data/lib/pact_broker/version.rb +1 -1
  32. data/lib/pact_broker/webhooks/job.rb +11 -5
  33. data/lib/pact_broker/webhooks/service.rb +10 -2
  34. data/lib/pact_broker/webhooks/webhook.rb +4 -0
  35. data/lib/rack/pact_broker/database_transaction.rb +22 -0
  36. data/pact_broker.gemspec +25 -1
  37. data/script/restart.sh +18 -0
  38. data/script/watch.sh +7 -0
  39. data/spec/features/publish_verification_spec.rb +1 -1
  40. data/spec/integration/app_spec.rb +1 -1
  41. data/spec/integration/webhooks/certificate_spec.rb +10 -2
  42. data/spec/lib/pact_broker/api/decorators/matrix_decorator_spec.rb +2 -1
  43. data/spec/lib/pact_broker/api/decorators/reason_decorator_spec.rb +59 -0
  44. data/spec/lib/pact_broker/api/resources/error_handler_spec.rb +84 -21
  45. data/spec/lib/pact_broker/app_spec.rb +22 -0
  46. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +1 -1
  47. data/spec/lib/pact_broker/matrix/deployment_status_summary_spec.rb +116 -28
  48. data/spec/lib/pact_broker/matrix/head_row_spec.rb +23 -0
  49. data/spec/lib/pact_broker/matrix/integration_spec.rb +242 -0
  50. data/spec/lib/pact_broker/matrix/repository_dependency_spec.rb +58 -0
  51. data/spec/lib/pact_broker/matrix/repository_spec.rb +40 -7
  52. data/spec/lib/pact_broker/matrix/service_spec.rb +0 -50
  53. data/spec/lib/pact_broker/pacts/repository_spec.rb +20 -4
  54. data/spec/lib/pact_broker/webhooks/job_spec.rb +9 -9
  55. data/spec/lib/pact_broker/webhooks/service_spec.rb +3 -3
  56. data/spec/lib/pact_broker/webhooks/webhook_spec.rb +39 -0
  57. data/spec/lib/rack/pact_broker/database_transaction_spec.rb +40 -0
  58. data/spec/service_consumers/pact_helper.rb +2 -0
  59. data/spec/spec_helper.rb +2 -3
  60. data/spec/support/jobs.rb +12 -0
  61. data/spec/support/migration_helpers.rb +1 -1
  62. data/spec/support/simplecov.rb +10 -0
  63. data/tasks/audit.rake +2 -0
  64. data/tasks/pact.rake +5 -1
  65. data/tasks/rspec.rake +14 -0
  66. metadata +50 -5
  67. data/db/pact_broker_database.sqlite3 +0 -0
  68. data/spec/lib/pact_broker/matrix/repository_find_integrations_spec.rb +0 -51
@@ -5,6 +5,29 @@ module PactBroker
5
5
  describe HeadRow do
6
6
  let(:td) { TestDataBuilder.new }
7
7
 
8
+ describe "webhooks" do
9
+ let(:td) { TestDataBuilder.new }
10
+
11
+ before do
12
+ td.create_consumer("Foo")
13
+ .create_provider("Bar")
14
+ .create_consumer_version
15
+ .create_pact
16
+ .create_global_webhook
17
+ .create_consumer_webhook
18
+ .create_provider_webhook
19
+ .create_provider("Wiffle")
20
+ .create_provider_webhook
21
+ end
22
+
23
+ let(:row) { HeadRow.where(consumer_name: "Foo", provider_name: "Bar").single_record }
24
+
25
+ it "returns all the webhooks" do
26
+ rows = HeadRow.eager(:webhooks).all
27
+ expect(rows.first.webhooks.count).to eq 3
28
+ end
29
+ end
30
+
8
31
  describe "latest_verification_for_consumer_version_tag" do
9
32
  context "when the pact with a given tag has been verified" do
10
33
  before do
@@ -0,0 +1,242 @@
1
+ require 'pact_broker/matrix/service'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ describe Service do
6
+ let(:td) { TestDataBuilder.new }
7
+ describe "find" do
8
+ subject { Service.find(selectors, options) }
9
+
10
+ # Useful for eyeballing the messages to make sure they read nicely
11
+ # after do
12
+ # require 'pact_broker/api/decorators/reason_decorator'
13
+ # subject.deployment_status_summary.reasons.each do | reason |
14
+ # puts reason
15
+ # puts PactBroker::Api::Decorators::ReasonDecorator.new(reason).to_s
16
+ # end
17
+ # end
18
+
19
+ let(:options) { {} }
20
+
21
+ describe "find" do
22
+ let(:selectors) do
23
+ [ { pacticipant_name: "foo" } ]
24
+ end
25
+
26
+ let(:options) do
27
+ { latest: true, tag: "prod" }
28
+ end
29
+
30
+ before do
31
+ td.create_pact_with_hierarchy("foo", "1", "bar")
32
+ .create_verification(provider_version: "2", tag_names: ["prod"])
33
+ end
34
+
35
+ it "returns a QueryResultsWithDeploymentStatusSummary" do
36
+ expect(subject.rows).to be_a(Array)
37
+ expect(subject.selectors).to be selectors
38
+ expect(subject.options).to be options
39
+ expect(subject.resolved_selectors).to be_a(Array)
40
+ expect(subject.resolved_selectors.count).to eq 2
41
+ expect(subject.integrations.count).to eq 1
42
+ expect(subject.deployment_status_summary).to be_a(DeploymentStatusSummary)
43
+ end
44
+ end
45
+
46
+ describe "when deploying a version of a provider with multiple versions of a consumer in production that is missing a verification for the latest prod version" do
47
+ before do
48
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
49
+ .create_consumer_version_tag("prod")
50
+ .create_verification(provider_version: "10", tag_names: ["test"])
51
+ .create_consumer_version("2", tag_names: ["prod"])
52
+ .create_pact
53
+ end
54
+
55
+ let(:selectors) { [{ pacticipant_name: "Bar", latest: true, tag: "test" }]}
56
+ let(:options) { { tag: "prod" } }
57
+
58
+ it "does not allow the consumer to be deployed" do
59
+ expect(subject.deployment_status_summary.deployable?).to_not be true
60
+ end
61
+ end
62
+
63
+ describe "when deploying a consumer that has not been verified by any providers" do
64
+ before do
65
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
66
+ .create_provider("Baz")
67
+ .create_pact
68
+ end
69
+ let(:selectors) do
70
+ [ { pacticipant_name: "Foo", pacticipant_version_number: "1" } ]
71
+ end
72
+
73
+ it "returns 2 integrations" do
74
+ expect(subject.integrations.size).to eq 2
75
+ end
76
+
77
+ it "but cannot resolve selectors for the providers" do
78
+ expect(subject.resolved_selectors.size).to eq 1
79
+ end
80
+
81
+ it "does not allow the consumer to be deployed" do
82
+ expect(subject.deployment_status_summary.deployable?).to_not be true
83
+ end
84
+ end
85
+
86
+ describe "when deploying a consumer that has two providers in prod, but it is not verified by one of the prod provider versions, pact_broker-client issue #33" do
87
+ before do
88
+ td.create_pact_with_hierarchy("Foo", "3.0.0", "Bar")
89
+ .create_verification(provider_version: "10.0.0", tag_names: ["prod"])
90
+ .create_provider("Baz")
91
+ .create_pact
92
+ .create_verification(provider_version: "20", tag_names:["prod"])
93
+ .create_consumer_version("2.0.0")
94
+ .use_provider("Bar")
95
+ .create_pact
96
+ .create_verification(provider_version: "11.0.0", tag_names: ["prod"])
97
+ end
98
+
99
+ let(:selectors) do
100
+ [ { pacticipant_name: "Foo", pacticipant_version_number: "3.0.0" } ]
101
+ end
102
+
103
+ let(:options) { {latest: true, tag: "prod"} }
104
+
105
+ it "returns 2 integrations" do
106
+ expect(subject.integrations.size).to eq 2
107
+ end
108
+
109
+ it "returns 1 row" do
110
+ expect(subject.rows.size).to eq 1
111
+ end
112
+
113
+ it "does not allow the consumer to be deployed" do
114
+ expect(subject.deployment_status_summary.deployable?).to_not be true
115
+ end
116
+ end
117
+
118
+ describe "when deploying an old version of a consumer that has added a new provider since that version" do
119
+ before do
120
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
121
+ .create_verification(provider_version: "2")
122
+ .create_consumer_version("2")
123
+ .create_pact
124
+ .create_verification(provider_version: "3")
125
+ .create_provider("Wiffle")
126
+ .create_pact
127
+ .create_verification(provider_version: "10")
128
+ end
129
+
130
+ let(:selectors) do
131
+ [ { pacticipant_name: "Foo", pacticipant_version_number: "1" } ]
132
+ end
133
+
134
+ it "allows the old version of the consumer to be deployed" do
135
+ expect(subject.deployment_status_summary.deployable?).to be true
136
+ end
137
+ end
138
+
139
+ describe "when the specified version does not exist" do
140
+ before do
141
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
142
+ end
143
+
144
+ let(:selectors) do
145
+ [ { pacticipant_name: "Bar", pacticipant_version_number: "5" } ]
146
+ end
147
+
148
+ it "does not allow the provider to be deployed" do
149
+ expect(subject.deployment_status_summary.deployable?).to_not be true
150
+ end
151
+ end
152
+
153
+ describe "when deploying a provider to prod for the first time and the consumer is not yet deployed" do
154
+ before do
155
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
156
+ .create_verification(provider_version: "2")
157
+ end
158
+
159
+ let(:selectors) do
160
+ [ { pacticipant_name: "Bar", pacticipant_version_number: "2" } ]
161
+ end
162
+
163
+ let(:options) do
164
+ { latest: true, tag: "prod" }
165
+ end
166
+
167
+ subject { Service.find(selectors, options) }
168
+
169
+ it "allows the provider to be deployed" do
170
+ expect(subject.deployment_status_summary.deployable?).to be true
171
+ end
172
+ end
173
+
174
+ describe "when deploying a consumer to prod for the first time and the provider is not yet deployed" do
175
+ before do
176
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
177
+ .create_verification(provider_version: "2")
178
+ end
179
+
180
+ let(:selectors) do
181
+ [ { pacticipant_name: "Foo", pacticipant_version_number: "1" } ]
182
+ end
183
+
184
+ let(:options) do
185
+ { latest: true, tag: "prod" }
186
+ end
187
+
188
+ it "does not allow the consumer to be deployed" do
189
+ expect(subject.deployment_status_summary.deployable?).to_not be true
190
+ end
191
+ end
192
+
193
+ describe "when deploying an app that is both a consumer and a provider to prod for the first time and the downstream provider is not yet deployed" do
194
+ before do
195
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
196
+ .create_verification(provider_version: "2")
197
+ .use_consumer("Bar")
198
+ .use_consumer_version("2")
199
+ .create_provider("Baz")
200
+ .create_pact
201
+ end
202
+
203
+ let(:selectors) do
204
+ [ { pacticipant_name: "Bar", pacticipant_version_number: "2" } ]
205
+ end
206
+
207
+ let(:options) do
208
+ { latest: true, tag: "prod" }
209
+ end
210
+
211
+ it "does not allow the app to be deployed" do
212
+ expect(subject.deployment_status_summary.deployable?).to_not be true
213
+ end
214
+ end
215
+
216
+ describe "when deploying an app that is both a consumer and a provider to prod for the first time and the downstream provider has been deployed" do
217
+ before do
218
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
219
+ .create_verification(provider_version: "2")
220
+ .use_consumer("Bar")
221
+ .use_consumer_version("2")
222
+ .create_provider("Baz")
223
+ .create_pact
224
+ .create_verification(provider_version: "4", tag_names: "prod")
225
+ end
226
+
227
+ let(:selectors) do
228
+ [ { pacticipant_name: "Bar", pacticipant_version_number: "2" } ]
229
+ end
230
+
231
+ let(:options) do
232
+ { latest: true, tag: "prod" }
233
+ end
234
+
235
+ it "allows the app to be deployed" do
236
+ expect(subject.deployment_status_summary.deployable?).to be true
237
+ end
238
+ end
239
+ end
240
+ end
241
+ end
242
+ end
@@ -0,0 +1,58 @@
1
+ require 'pact_broker/matrix/repository'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ describe Repository do
6
+ let(:td) { TestDataBuilder.new}
7
+
8
+ def build_selectors(hash)
9
+ hash.collect do | key, value |
10
+ { pacticipant_name: key, pacticipant_version_number: value }
11
+ end
12
+ end
13
+
14
+ def shorten_row row
15
+ "#{row[:consumer_name]}#{row[:consumer_version_number]} #{row[:provider_name]}#{row[:provider_version_number] || '?'} n#{row[:verification_number] || '?'}"
16
+ end
17
+
18
+ def shorten_rows rows
19
+ rows.collect{ |r| shorten_row(r) }
20
+ end
21
+
22
+ describe "find when deploying a provider to prod for the first time and the consumer has not been deployed yet" do
23
+ before do
24
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
25
+ .create_verification(provider_version: "2")
26
+ end
27
+
28
+ let(:selectors) { [ { pacticipant_name: "Bar", pacticipant_version_number: "2" } ] }
29
+ let(:options) { { latest: true, tag: "prod"} }
30
+
31
+ subject { shorten_rows(rows) }
32
+ let(:rows) { Repository.new.find(selectors, options) }
33
+
34
+ it "returns an empty array" do
35
+ expect(rows).to eq []
36
+ end
37
+ end
38
+
39
+ describe "find when deploying a consumer to prod for the first time and the provider has not been deployed yet" do
40
+ before do
41
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
42
+ .create_verification(provider_version: "2")
43
+ end
44
+
45
+ let(:selectors) { [ { pacticipant_name: "Foo", pacticipant_version_number: "1" } ] }
46
+ let(:options) { { latest: true, tag: "prod"} }
47
+
48
+ subject { shorten_rows(rows) }
49
+ let(:results) { Repository.new.find(selectors, options) }
50
+
51
+ it "returns an empty array" do
52
+ expect(results).to eq []
53
+ expect(results.resolved_selectors.find{ |s | s[:pacticipant_name] == "Bar"}[:pacticipant_version_id]).to eq -1
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
@@ -956,24 +956,27 @@ module PactBroker
956
956
  context "when the latest tag is specified" do
957
957
  let(:selectors) { [{ pacticipant_name: 'D', latest: true, tag: 'dev' } ] }
958
958
 
959
- it "raises an error" do
960
- expect { subject }.to raise_error Error, /No version of D found/
959
+ it "returns an empty array" do
960
+ expect(subject).to eq []
961
+ expect(subject.resolved_selectors.find{ |s | s[:pacticipant_name] == "D"}[:pacticipant_version_id]).to eq -1
961
962
  end
962
963
  end
963
964
 
964
965
  context "when all tags are specified" do
965
966
  let(:selectors) { [{ pacticipant_name: 'D', tag: 'dev' } ] }
966
967
 
967
- it "raises an error" do
968
- expect { subject }.to raise_error Error, /No version of D found/
968
+ it "returns an empty array" do
969
+ expect(subject).to eq []
970
+ expect(subject.resolved_selectors.find{ |s | s[:pacticipant_name] == "D"}[:pacticipant_version_id]).to eq -1
969
971
  end
970
972
  end
971
973
 
972
974
  context "when no tags are specified" do
973
975
  let(:selectors) { [{ pacticipant_name: 'E', latest: true } ] }
974
976
 
975
- it "raises an error" do
976
- expect { subject }.to raise_error Error, /No version of E found/
977
+ it "returns an empty array" do
978
+ expect(subject).to eq []
979
+ expect(subject.resolved_selectors.find{ |s | s[:pacticipant_name] == "E"}[:pacticipant_version_id]).to eq -1
977
980
  end
978
981
  end
979
982
  end
@@ -988,6 +991,8 @@ module PactBroker
988
991
  .create_pact
989
992
  .create_verification(provider_version: "10.0.0", tag_names: ["prod"])
990
993
  .create_provider("baz")
994
+ .create_pact
995
+ .create_verification(provider_version: "9.0.0")
991
996
  .create_consumer_version("2.0.0")
992
997
  .create_pact
993
998
  .create_verification(provider_version: "20.0.0", tag_names: ["prod"])
@@ -995,12 +1000,40 @@ module PactBroker
995
1000
 
996
1001
  let(:selectors) { [{ pacticipant_name: "foo", pacticipant_version_number: "1.0.0" }] }
997
1002
  let(:options) { {latestby: "cvp", latest: true, tag: "prod"} }
1003
+ let(:results) { Repository.new.find(selectors, options) }
998
1004
 
999
- subject { shorten_rows(Repository.new.find(selectors, options)) }
1005
+ subject { shorten_rows(results) }
1000
1006
 
1001
1007
  it "only returns a row for the foo pact version that has been verified by the current production version of bar" do
1002
1008
  expect(subject).to eq ["foo1.0.0 bar10.0.0 n1"]
1003
1009
  end
1010
+
1011
+ it "returns 2 integrations" do
1012
+ expect(results.integrations.size).to eq 2
1013
+ end
1014
+ end
1015
+
1016
+ describe "when deploying a provider and the version of the consumer is not specified and multiple consumer versions have been verified by this provider version" do
1017
+ before do
1018
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
1019
+ .create_verification(provider_version: "5")
1020
+ .create_consumer_version("2")
1021
+ .create_pact
1022
+ .create_verification(provider_version: "5")
1023
+ end
1024
+
1025
+ subject { Repository.new.find(selectors, options) }
1026
+
1027
+ let(:options) { { latestby: 'cvp' } }
1028
+
1029
+ let(:selectors) do
1030
+ [ { pacticipant_name: "Bar", pacticipant_version_number: "5" } ]
1031
+ end
1032
+
1033
+ it "returns only the latest consumer version row", pending: "Can't work out what this should do" do
1034
+ expect(subject.rows.size).to eq 1
1035
+ expect(subject.rows.first.consumer_version_number).to eq "2"
1036
+ end
1004
1037
  end
1005
1038
  end
1006
1039
  end
@@ -5,32 +5,6 @@ module PactBroker
5
5
  describe Service do
6
6
  let(:td) { TestDataBuilder.new }
7
7
 
8
- describe "find integration test" do
9
- let(:selectors) do
10
- [ { pacticipant_name: "foo" } ]
11
- end
12
-
13
- let(:options) do
14
- { latest: true, tag: "prod" }
15
- end
16
-
17
- before do
18
- td.create_pact_with_hierarchy("foo", "1", "bar")
19
- .create_verification(provider_version: "2", tag_names: ["prod"])
20
- end
21
-
22
- subject { Service.find(selectors, options) }
23
-
24
- it "returns a QueryResultsWithDeploymentStatusSummary" do
25
- expect(subject.rows).to be_a(Array)
26
- expect(subject.selectors).to be selectors
27
- expect(subject.options).to be options
28
- expect(subject.resolved_selectors).to be_a(Array)
29
- expect(subject.resolved_selectors.count).to eq 2
30
- expect(subject.deployment_status_summary).to be_a(DeploymentStatusSummary)
31
- end
32
- end
33
-
34
8
  describe "validate_selectors" do
35
9
 
36
10
  subject { Service.validate_selectors(selectors) }
@@ -43,21 +17,6 @@ module PactBroker
43
17
  end
44
18
  end
45
19
 
46
- context "when one or more of the selectors does not match any known version" do
47
- before do
48
- td.create_pacticipant("Foo")
49
- .create_version("1")
50
- .create_pacticipant("Bar")
51
- .create_version("2")
52
- end
53
-
54
- let(:selectors) { [{ pacticipant_name: "Foo", pacticipant_version_number: "1" }, { pacticipant_name: "Bar", pacticipant_version_number: "1" }] }
55
-
56
- it "returns error messages" do
57
- expect(subject).to eq ["No pact or verification found for Bar version 1"]
58
- end
59
- end
60
-
61
20
  context "when the pacticipant does not exist" do
62
21
  let(:selectors) { [{ pacticipant_name: "Foo", pacticipant_version_number: "1" }] }
63
22
 
@@ -105,15 +64,6 @@ module PactBroker
105
64
  expect(subject).to eq []
106
65
  end
107
66
  end
108
-
109
- context "when there is not a version for the tag" do
110
-
111
- let(:selectors) { [{ pacticipant_name: "Foo", latest: true, tag: "wiffle" }, { pacticipant_name: "Bar", pacticipant_version_number: "2" }] }
112
-
113
- it "returns an error message" do
114
- expect(subject).to eq ["No version of Foo found with tag wiffle"]
115
- end
116
- end
117
67
  end
118
68
 
119
69
  context "when the latest is used as well as a version" do