pact_broker 2.19.1 → 2.19.2

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +0 -1
  4. data/db/migrations/000002_create_versions_table.rb +1 -1
  5. data/db/migrations/20180311_optimise_head_matrix.rb +1 -0
  6. data/db/migrations/20180523_create_latest_verifications_for_consumer_version_tags.rb +50 -0
  7. data/db/migrations/20180524_create_latest_verifications_for_consumer_and_provider.rb +46 -0
  8. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +7 -3
  9. data/lib/pact_broker/configuration.rb +6 -1
  10. data/lib/pact_broker/domain/webhook_request.rb +12 -11
  11. data/lib/pact_broker/index/service.rb +14 -84
  12. data/lib/pact_broker/matrix/aggregated_row.rb +71 -0
  13. data/lib/pact_broker/matrix/head_row.rb +23 -0
  14. data/lib/pact_broker/matrix/repository.rb +0 -1
  15. data/lib/pact_broker/matrix/row.rb +15 -20
  16. data/lib/pact_broker/pacts/content.rb +66 -0
  17. data/lib/pact_broker/pacts/create_formatted_diff.rb +0 -1
  18. data/lib/pact_broker/pacts/diff.rb +3 -2
  19. data/lib/pact_broker/pacts/generate_sha.rb +24 -0
  20. data/lib/pact_broker/pacts/parse.rb +11 -0
  21. data/lib/pact_broker/pacts/repository.rb +4 -4
  22. data/lib/pact_broker/pacts/service.rb +4 -2
  23. data/lib/pact_broker/pacts/sort_content.rb +54 -0
  24. data/lib/pact_broker/verifications/latest_verification_for_consumer_and_provider.rb +26 -0
  25. data/lib/pact_broker/verifications/latest_verification_for_consumer_version_tag.rb +23 -0
  26. data/lib/pact_broker/version.rb +1 -1
  27. data/lib/pact_broker/versions/repository.rb +12 -3
  28. data/script/run-with-ssl.rb +44 -0
  29. data/spec/features/base_equality_only_on_content_that_affects_verification_results_spec.rb +34 -0
  30. data/spec/lib/pact_broker/api/decorators/webhook_execution_result_decorator_spec.rb +6 -2
  31. data/spec/lib/pact_broker/domain/{index_items_spec.rb → index_item_spec.rb} +0 -0
  32. data/spec/lib/pact_broker/domain/webhook_request_spec.rb +16 -5
  33. data/spec/lib/pact_broker/matrix/aggregated_row_spec.rb +79 -0
  34. data/spec/lib/pact_broker/matrix/head_row_spec.rb +55 -0
  35. data/spec/lib/pact_broker/matrix/row_spec.rb +22 -2
  36. data/spec/lib/pact_broker/pacts/content_spec.rb +166 -0
  37. data/spec/lib/pact_broker/pacts/create_formatted_diff_spec.rb +0 -3
  38. data/spec/lib/pact_broker/pacts/generate_sha_spec.rb +92 -0
  39. data/spec/lib/pact_broker/pacts/repository_spec.rb +47 -4
  40. data/spec/lib/pact_broker/pacts/sort_content_spec.rb +44 -0
  41. data/spec/lib/pact_broker/versions/repository_spec.rb +13 -5
  42. data/spec/spec_helper.rb +1 -0
  43. data/spec/support/foo-bar.json +34 -0
  44. data/spec/support/rspec_match_hash.rb +14 -17
  45. data/spec/support/test_data_builder.rb +5 -4
  46. metadata +26 -8
  47. data/lib/pact_broker/matrix/latest_row.rb +0 -10
  48. data/lib/pact_broker/pacts/sort_verifiable_content.rb +0 -41
  49. data/spec/lib/pact_broker/pacts/sort_verifiable_content_spec.rb +0 -25
@@ -0,0 +1,44 @@
1
+ if __FILE__ == $0
2
+ require 'pact_broker'
3
+
4
+ DATABASE_CREDENTIALS = {adapter: "sqlite", database: "pact_broker_ssl_database.sqlite3", :encoding => 'utf8'}
5
+
6
+ app = PactBroker::App.new do | config |
7
+ config.logger = ::Logger.new($stdout)
8
+ config.logger.level = ::Logger::DEBUG
9
+ config.database_connection = Sequel.connect(DATABASE_CREDENTIALS)
10
+ end
11
+
12
+ SSL_KEY = 'spec/fixtures/certificates/key.pem'
13
+ SSL_CERT = 'spec/fixtures/certificates/cert.pem'
14
+
15
+ trap(:INT) do
16
+ @server.shutdown
17
+ exit
18
+ end
19
+
20
+ def webrick_opts port
21
+ certificate = OpenSSL::X509::Certificate.new(File.read(SSL_CERT))
22
+ cert_name = certificate.subject.to_a.collect{|a| a[0..1] }
23
+ {
24
+ Port: port,
25
+ Host: "0.0.0.0",
26
+ AccessLog: [],
27
+ SSLCertificate: certificate,
28
+ SSLPrivateKey: OpenSSL::PKey::RSA.new(File.read(SSL_KEY)),
29
+ SSLEnable: true,
30
+ SSLCertName: cert_name
31
+ }
32
+ end
33
+
34
+ require 'webrick'
35
+ require 'webrick/https'
36
+ require 'rack'
37
+ require 'rack/handler/webrick'
38
+
39
+ opts = webrick_opts(4444)
40
+
41
+ Rack::Handler::WEBrick.run(app, opts) do |server|
42
+ @server = server
43
+ end
44
+ end
@@ -0,0 +1,34 @@
1
+ RSpec.describe "base_equality_only_on_content_that_affects_verification_results" do
2
+ let(:td) { TestDataBuilder.new }
3
+ let(:json_content_1) { load_fixture('foo-bar.json') }
4
+ let(:json_content_2) do
5
+ pact_hash = load_json_fixture('foo-bar.json')
6
+ pact_hash['interactions'] = pact_hash['interactions'].reverse
7
+ pact_hash.to_json
8
+ end
9
+ let(:base_equality_only_on_content_that_affects_verification_results) { true }
10
+
11
+ before do
12
+ PactBroker.configuration.base_equality_only_on_content_that_affects_verification_results = base_equality_only_on_content_that_affects_verification_results
13
+ td.create_pact_with_hierarchy("Foo", "1", "Bar", json_content_1)
14
+ .create_verification(provider_version: "5")
15
+ .create_consumer_version("2")
16
+ .create_pact(json_content: json_content_2)
17
+ end
18
+
19
+ context "when a pact is published with a different order of interactions to a previous version, but which is otherwise the same" do
20
+ context "when base_equality_only_on_content_that_affects_verification_results is true" do
21
+ it "applies the verifications from the previous version" do
22
+ expect(PactBroker::Matrix::Row.all).to contain_hash(consumer_version_number: "2", provider_version_number: "5")
23
+ end
24
+ end
25
+
26
+ context "when base_equality_only_on_content_that_affects_verification_results is false" do
27
+ let(:base_equality_only_on_content_that_affects_verification_results) { false }
28
+
29
+ it "does not apply the verifications from the previous version" do
30
+ expect(PactBroker::Matrix::Row.all).to_not contain_hash(consumer_version_number: "2", provider_version_number: "5")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -30,7 +30,11 @@ module PactBroker
30
30
  expect(subject[:_links][:webhook][:href]).to eq 'http://example.org/webhooks/some-uuid'
31
31
  end
32
32
 
33
- context "when there is an error" do
33
+ it "includes a message about the response being redacted" do
34
+ expect(subject[:message]).to match /redacted/
35
+ end
36
+
37
+ context "when there is an error", pending: "temporarily disabled" do
34
38
  let(:error) { double('error', message: 'message', backtrace: ['blah','blah']) }
35
39
 
36
40
  it "includes the message" do
@@ -42,7 +46,7 @@ module PactBroker
42
46
  end
43
47
  end
44
48
 
45
- context "when there is a response" do
49
+ context "when there is a response", pending: "temporarily disabled" do
46
50
  it "includes the response code" do
47
51
  expect(subject[:response][:status]).to eq 200
48
52
  end
@@ -8,6 +8,9 @@ module PactBroker
8
8
  before do
9
9
  allow(PactBroker::Api::PactBrokerUrls).to receive(:pact_url).and_return('http://example.org/pact-url')
10
10
  allow(PactBroker.configuration).to receive(:base_url).and_return('http://example.org')
11
+ allow(PactBroker.logger).to receive(:info).and_call_original
12
+ allow(PactBroker.logger).to receive(:debug).and_call_original
13
+ allow(PactBroker.logger).to receive(:warn).and_call_original
11
14
  end
12
15
 
13
16
  let(:username) { nil }
@@ -121,6 +124,14 @@ module PactBroker
121
124
  subject.execute(pact, options)
122
125
  end
123
126
 
127
+ it "does not write the response body to the exeuction log for security purposes" do
128
+ expect(logs).to_not include "An error"
129
+ end
130
+
131
+ it "logs a message about why there is no response information" do
132
+ expect(logs).to include "Webhook response has been redacted temporarily for security purposes"
133
+ end
134
+
124
135
  describe "execution logs" do
125
136
 
126
137
  it "logs the request method and path" do
@@ -143,12 +154,12 @@ module PactBroker
143
154
  expect(logs).to include "HTTP/1.0 200"
144
155
  end
145
156
 
146
- it "logs the response headers" do
147
- expect(logs).to include "Content-Type: text/foo, blah"
157
+ it "does not log the response headers" do
158
+ expect(logs).to_not include "Content-Type: text/foo, blah"
148
159
  end
149
160
 
150
- it "logs the response body" do
151
- expect(logs).to include "respbod"
161
+ it "does not log the response body" do
162
+ expect(logs).to_not include "respbod"
152
163
  end
153
164
 
154
165
  context "when the response code is a success" do
@@ -280,7 +291,7 @@ module PactBroker
280
291
  end
281
292
  end
282
293
 
283
- context "when the response body contains a non UTF-8 character" do
294
+ context "when the response body contains a non UTF-8 character", pending: "execution logs disabled temporarily for security purposes" do
284
295
  let!(:http_request) do
285
296
  stub_request(:post, "http://example.org/hook").
286
297
  to_return(:status => 200, :body => "This has some \xC2 invalid chars")
@@ -0,0 +1,79 @@
1
+ require 'pact_broker/matrix/aggregated_row'
2
+
3
+ module PactBroker
4
+ module Matrix
5
+ describe AggregatedRow do
6
+ describe "latest_verification" do
7
+ let(:row_1) do
8
+ instance_double('PactBroker::Matrix::HeadRow',
9
+ consumer_name: "Foo",
10
+ provider_name: "Bar",
11
+ verification: verification_1,
12
+ latest_verification_for_consumer_version_tag: tag_verification_1,
13
+ consumer_version_tag_name: consumer_version_tag_name_1)
14
+ end
15
+ let(:row_2) do
16
+ instance_double('PactBroker::Matrix::HeadRow',
17
+ verification: verification_2,
18
+ latest_verification_for_consumer_version_tag: tag_verification_2,
19
+ consumer_version_tag_name: consumer_version_tag_name_2)
20
+ end
21
+ let(:verification_1) { instance_double('PactBroker::Domain::Verification', id: 1) }
22
+ let(:verification_2) { instance_double('PactBroker::Domain::Verification', id: 2) }
23
+ let(:tag_verification_1) { instance_double('PactBroker::Domain::Verification', id: 3) }
24
+ let(:tag_verification_2) { instance_double('PactBroker::Domain::Verification', id: 4) }
25
+ let(:consumer_version_tag_name_1) { 'master' }
26
+ let(:consumer_version_tag_name_2) { 'prod' }
27
+ let(:rows) { [row_1, row_2] }
28
+ let(:aggregated_row) { AggregatedRow.new(rows) }
29
+
30
+ subject { aggregated_row.latest_verification }
31
+
32
+ context "when the rows have verifications" do
33
+ it "returns the verification with the largest id" do
34
+ expect(subject).to be verification_2
35
+ end
36
+ end
37
+
38
+ context "when the rows do not have verifications, but there are a previous verifications for a pacts with the same tag" do
39
+ let(:verification_1) { nil }
40
+ let(:verification_2) { nil }
41
+
42
+ it "returns the verification for the previous pact that has the largest id" do
43
+ expect(subject).to be tag_verification_2
44
+ end
45
+ end
46
+
47
+ context "when there is no verification for any of the rows or any of the pacts with the same tag" do
48
+ let(:verification_1) { nil }
49
+ let(:verification_2) { nil }
50
+ let(:tag_verification_1) { nil }
51
+ let(:tag_verification_2) { nil }
52
+
53
+ context "when one of the rows is the overall latest" do
54
+ let(:consumer_version_tag_name_1) { nil }
55
+ let(:overall_latest_verification) { instance_double('PactBroker::Domain::Verification', id: 1) }
56
+ before do
57
+ allow(row_1).to receive(:latest_verification_for_consumer_and_provider).and_return(overall_latest_verification)
58
+ end
59
+
60
+ it "looks up the overall latest verification" do
61
+ expect(row_1).to receive(:latest_verification_for_consumer_and_provider)
62
+ subject
63
+ end
64
+
65
+ it "returns the overall latest verification" do
66
+ expect(subject).to be overall_latest_verification
67
+ end
68
+ end
69
+
70
+ context "when none of the rows is not the overall latest (they are all the latest with a tag)" do
71
+ it "returns nil" do
72
+ expect(subject).to be nil
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -3,6 +3,61 @@ require 'pact_broker/matrix/head_row'
3
3
  module PactBroker
4
4
  module Matrix
5
5
  describe HeadRow do
6
+ let(:td) { TestDataBuilder.new }
7
+
8
+ describe "latest_verification_for_consumer_version_tag" do
9
+ context "when the pact with a given tag has been verified" do
10
+ before do
11
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
12
+ .create_consumer_version_tag("prod")
13
+ .create_verification(provider_version: "10")
14
+ .create_consumer_version("2", comment: "latest prod version for Foo")
15
+ .create_consumer_version_tag("prod")
16
+ .create_pact
17
+ .create_verification(provider_version: "11")
18
+ .create_verification(provider_version: "12", number: 2)
19
+ .create_consumer("Wiffle")
20
+ .create_consumer_version("30", comment: "latest prod version for Wiffle")
21
+ .create_consumer_version_tag("prod")
22
+ .create_pact
23
+ .create_verification(provider_version: "12")
24
+ .create_provider("Meep")
25
+ .create_pact
26
+ .create_verification(provider_version: "40")
27
+ end
28
+
29
+ subject { HeadRow.eager(:consumer_version_tags).eager(:latest_verification_for_consumer_version_tag).order(:pact_publication_id, :verification_id).exclude(consumer_version_tag_name: nil).all }
30
+
31
+ it "returns its most recent verification" do
32
+ cols = [:consumer_name, :consumer_version_number, :consumer_version_tag_name, :provider_name, :provider_version_number]
33
+ rows = subject.collect{ | row | cols.collect{ | col | row[col]} }
34
+
35
+ expect(subject.size).to eq 3
36
+ expect(rows).to include ["Foo", "2", "prod", "Bar", "12"]
37
+ expect(rows).to include ["Wiffle", "30", "prod", "Bar", "12"]
38
+ expect(rows).to include ["Wiffle", "30", "prod", "Meep", "40"]
39
+ end
40
+ end
41
+
42
+ context "when the most recent pact with a given tag has not been verified, but a previous version with the same tag has" do
43
+ before do
44
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
45
+ .create_consumer_version_tag("prod")
46
+ .create_verification(provider_version: "10")
47
+ .create_verification(provider_version: "11", number: 2, comment: "this is the latest verification for a pact with cv tag prod")
48
+ .create_consumer_version("2")
49
+ .create_consumer_version_tag("prod")
50
+ .create_pact
51
+ end
52
+
53
+ subject { HeadRow.eager(:consumer_version_tags).eager(:latest_verification_for_consumer_version_tag).order(:pact_publication_id).all }
54
+
55
+ it "returns the most recent verification for the previous version with the same tag" do
56
+ expect(subject.last.verification_id).to be nil # this pact version has not been verified directly
57
+ expect(subject.last.latest_verification_for_consumer_version_tag.provider_version.number).to eq "11"
58
+ end
59
+ end
60
+ end
6
61
  describe "refresh", migration: true do
7
62
  before do
8
63
  PactBroker::Database.migrate
@@ -3,14 +3,34 @@ require 'pact_broker/matrix/row'
3
3
  module PactBroker
4
4
  module Matrix
5
5
  describe Row do
6
- describe "refresh", migration: true do
6
+ let(:td) { TestDataBuilder.new }
7
+
8
+ describe "latest_verification_for_consumer_and_provider" do
7
9
  before do
8
- PactBroker::Database.migrate
10
+ td.create_pact_with_hierarchy("Foo", "1", "Bar")
11
+ .create_verification(provider_version: "9")
12
+ .create_consumer_version("2")
13
+ .create_consumer_version_tag("prod")
14
+ .create_pact
15
+ .create_verification(provider_version: "10")
16
+ .create_consumer("Wiffle")
17
+ .create_consumer_version("4")
18
+ .create_pact
19
+ .create_verification(provider_version: "11")
20
+ end
21
+
22
+ subject { Row.where(consumer_name: "Foo", provider_name: "Bar").all.collect(&:latest_verification_for_consumer_and_provider) }
23
+
24
+ it "returns the latest verification for the consumer and provider" do
25
+ expect(subject.collect(&:provider_version_number)).to eq ["10", "10"]
9
26
  end
27
+ end
10
28
 
29
+ describe "refresh", migration: true do
11
30
  let(:td) { TestDataBuilder.new(auto_refresh_matrix: false) }
12
31
 
13
32
  before do
33
+ PactBroker::Database.migrate
14
34
  td.create_pact_with_hierarchy("Foo", "1", "Bar")
15
35
  end
16
36
 
@@ -0,0 +1,166 @@
1
+ require 'pact_broker/pacts/content'
2
+
3
+ module PactBroker
4
+ module Pacts
5
+ describe Content do
6
+ describe "content_that_affects_verification_results" do
7
+
8
+ subject { Content.from_hash(pact_hash).content_that_affects_verification_results }
9
+
10
+ context "with messages" do
11
+ let(:pact_hash) do
12
+ {
13
+ 'ignored' => 'foo',
14
+ 'messages' => [1],
15
+ 'metadata' => {
16
+ 'pactSpecification' => {
17
+ 'version' => '1'
18
+ }
19
+ }
20
+ }
21
+ end
22
+
23
+ let(:expected_content) do
24
+ {
25
+ 'messages' => [1],
26
+ 'pact_specification_version' => '1'
27
+ }
28
+ end
29
+
30
+ it "extracts the messages and pact_specification_version" do
31
+ expect(subject).to eq expected_content
32
+ end
33
+ end
34
+
35
+ context "with interactions" do
36
+ let(:pact_hash) do
37
+ {
38
+ 'ignored' => 'foo',
39
+ 'interactions' => [1],
40
+ 'metadata' => {
41
+ 'pactSpecification' => {
42
+ 'version' => '1'
43
+ }
44
+ }
45
+ }
46
+ end
47
+
48
+ let(:expected_content) do
49
+ {
50
+ 'interactions' => [1],
51
+ 'pact_specification_version' => '1'
52
+ }
53
+ end
54
+
55
+ it "extracts the interactions and pact_specification_version" do
56
+ expect(subject).to eq expected_content
57
+ end
58
+ end
59
+
60
+ context "with both messages and interactions, even though this should never happen" do
61
+ let(:pact_hash) do
62
+ {
63
+ 'ignored' => 'foo',
64
+ 'interactions' => [1],
65
+ 'messages' => [2],
66
+ 'metadata' => {
67
+ 'pactSpecification' => {
68
+ 'version' => '1'
69
+ }
70
+ }
71
+ }
72
+ end
73
+
74
+ let(:expected_content) do
75
+ {
76
+ 'interactions' => [1],
77
+ 'messages' => [2],
78
+ 'pact_specification_version' => '1'
79
+ }
80
+ end
81
+
82
+ it "extracts the interactions and pact_specification_version" do
83
+ expect(subject).to eq expected_content
84
+ end
85
+ end
86
+
87
+ context "with neither messages nor interactions" do
88
+ let(:pact_hash) do
89
+ {
90
+ 'ignored' => 'foo',
91
+ 'foo' => [1],
92
+ 'metadata' => {
93
+ 'pactSpecification' => {
94
+ 'version' => '1'
95
+ }
96
+ }
97
+ }
98
+ end
99
+
100
+ it "returns the entire hash" do
101
+ expect(subject).to eq pact_hash
102
+ end
103
+ end
104
+
105
+ context "when somebody publishes an array as the top level element" do
106
+ let(:pact_hash) do
107
+ [{ "foo" => "bar" }]
108
+ end
109
+
110
+ it "returns the entire document" do
111
+ expect(subject).to eq pact_hash
112
+ end
113
+ end
114
+ end
115
+
116
+ describe "#pact_specification_version" do
117
+ subject { Content.from_hash(json) }
118
+ context 'with pactSpecification.version' do
119
+ let(:json) do
120
+ {
121
+ 'metadata' => {
122
+ 'pactSpecification' => {
123
+ 'version' => '1'
124
+ }
125
+ }
126
+ }
127
+ end
128
+
129
+ its(:pact_specification_version) { is_expected.to eq '1' }
130
+ end
131
+
132
+ context 'with pact-specification.version' do
133
+ let(:json) do
134
+ {
135
+ 'metadata' => {
136
+ 'pact-specification' => {
137
+ 'version' => '1'
138
+ }
139
+ }
140
+ }
141
+ end
142
+
143
+ its(:pact_specification_version) { is_expected.to eq '1' }
144
+ end
145
+
146
+ context 'with pactSpecificationVersion' do
147
+ let(:json) do
148
+ {
149
+ 'metadata' => {
150
+ 'pactSpecificationVersion' => '1'
151
+ }
152
+ }
153
+ end
154
+
155
+ its(:pact_specification_version) { is_expected.to eq '1' }
156
+ end
157
+
158
+ context 'with an array for content' do
159
+ let(:json) { [] }
160
+
161
+ its(:pact_specification_version) { is_expected.to eq nil }
162
+ end
163
+ end
164
+ end
165
+ end
166
+ end