pact_broker 1.1.0 → 1.2.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 (70) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +18 -0
  3. data/README.md +8 -11
  4. data/lib/pact_broker/api.rb +2 -0
  5. data/lib/pact_broker/api/decorators/decorator_context.rb +8 -1
  6. data/lib/pact_broker/api/decorators/pact_decorator.rb +18 -3
  7. data/lib/pact_broker/api/decorators/pact_version_decorator.rb +39 -0
  8. data/lib/pact_broker/api/decorators/pact_versions_decorator.rb +48 -0
  9. data/lib/pact_broker/api/decorators/pacticipant_decorator.rb +3 -3
  10. data/lib/pact_broker/api/decorators/webhook_decorator.rb +1 -1
  11. data/lib/pact_broker/api/decorators/webhook_execution_result_decorator.rb +1 -1
  12. data/lib/pact_broker/api/decorators/webhooks_decorator.rb +1 -1
  13. data/lib/pact_broker/api/pact_broker_urls.rb +16 -6
  14. data/lib/pact_broker/api/renderers/html_pact_renderer.rb +35 -6
  15. data/lib/pact_broker/api/resources/base_resource.rb +80 -33
  16. data/lib/pact_broker/api/resources/group.rb +26 -24
  17. data/lib/pact_broker/api/resources/index.rb +52 -51
  18. data/lib/pact_broker/api/resources/latest_pact.rb +22 -22
  19. data/lib/pact_broker/api/resources/latest_pacts.rb +18 -17
  20. data/lib/pact_broker/api/resources/pact.rb +34 -39
  21. data/lib/pact_broker/api/resources/pact_versions.rb +35 -0
  22. data/lib/pact_broker/api/resources/pact_webhooks.rb +54 -61
  23. data/lib/pact_broker/api/resources/pacticipant.rb +40 -39
  24. data/lib/pact_broker/api/resources/pacticipant_resource_methods.rb +19 -0
  25. data/lib/pact_broker/api/resources/pacticipants.rb +52 -17
  26. data/lib/pact_broker/api/resources/relationships.rb +18 -17
  27. data/lib/pact_broker/api/resources/tag.rb +30 -29
  28. data/lib/pact_broker/api/resources/webhook.rb +29 -28
  29. data/lib/pact_broker/api/resources/webhook_execution.rb +0 -1
  30. data/lib/pact_broker/api/resources/webhook_resource_methods.rb +24 -0
  31. data/lib/pact_broker/api/resources/webhooks.rb +18 -17
  32. data/lib/pact_broker/app.rb +1 -0
  33. data/lib/pact_broker/configuration.rb +2 -2
  34. data/lib/pact_broker/doc/views/webhooks.markdown +1 -1
  35. data/lib/pact_broker/functions/find_potential_duplicate_pacticipant_names.rb +43 -0
  36. data/lib/pact_broker/locale/en.yml +7 -0
  37. data/lib/pact_broker/messages.rb +20 -1
  38. data/lib/pact_broker/models/pact.rb +8 -0
  39. data/lib/pact_broker/models/pacticipant.rb +9 -0
  40. data/lib/pact_broker/models/version.rb +1 -0
  41. data/lib/pact_broker/repositories/pact_repository.rb +6 -0
  42. data/lib/pact_broker/repositories/pacticipant_repository.rb +4 -0
  43. data/lib/pact_broker/repositories/webhook_repository.rb +2 -3
  44. data/lib/pact_broker/services/pact_service.rb +20 -0
  45. data/lib/pact_broker/services/pacticipant_service.rb +28 -0
  46. data/lib/pact_broker/services/webhook_service.rb +7 -2
  47. data/lib/pact_broker/version.rb +1 -1
  48. data/pact_broker.gemspec +1 -1
  49. data/public/stylesheets/github.css +1 -1
  50. data/public/stylesheets/pact.css +12 -0
  51. data/spec/lib/pact_broker/api/decorators/pact_decorator_spec.rb +10 -0
  52. data/spec/lib/pact_broker/api/decorators/pact_version_decorator_spec.rb +49 -0
  53. data/spec/lib/pact_broker/api/renderers/html_pact_renderer_spec.rb +9 -1
  54. data/spec/lib/pact_broker/api/resources/group_spec.rb +1 -1
  55. data/spec/lib/pact_broker/api/resources/latest_pact_spec.rb +1 -1
  56. data/spec/lib/pact_broker/api/resources/pact_spec.rb +35 -6
  57. data/spec/lib/pact_broker/api/resources/pact_webhooks_spec.rb +6 -4
  58. data/spec/lib/pact_broker/api/resources/pacticipants_spec.rb +91 -0
  59. data/spec/lib/pact_broker/configuration_spec.rb +3 -3
  60. data/spec/lib/pact_broker/functions/find_potential_duplicate_pacticipant_names_spec.rb +82 -0
  61. data/spec/lib/pact_broker/messages_spec.rb +31 -0
  62. data/spec/lib/pact_broker/models/pacticipant_spec.rb +32 -0
  63. data/spec/lib/pact_broker/repositories/pact_repository_spec.rb +29 -0
  64. data/spec/lib/pact_broker/repositories/pacticipant_repository_spec.rb +29 -0
  65. data/spec/lib/pact_broker/repositories/webhook_repository_spec.rb +7 -18
  66. data/spec/lib/pact_broker/services/pact_service_spec.rb +20 -0
  67. data/spec/lib/pact_broker/services/pacticipant_service_spec.rb +87 -2
  68. data/spec/support/provider_state_builder.rb +7 -7
  69. data/tasks/rspec.rake +1 -1
  70. metadata +27 -2
@@ -26,7 +26,7 @@ module PactBroker::Api
26
26
  allow(decorator).to receive(:to_csv).and_return(csv)
27
27
  end
28
28
 
29
- subject { get "/groups/Some%20Service" }
29
+ subject { get "/groups/Some%20Service", '', {"HTTP_X_My_App_Version" => '2'} }
30
30
 
31
31
  context "when the pacticipant exists" do
32
32
 
@@ -34,7 +34,7 @@ module PactBroker::Api
34
34
  end
35
35
 
36
36
  it "uses the configured HTML renderer" do
37
- expect(PactBroker.configuration.html_pact_renderer).to receive(:call).with(json_content)
37
+ expect(PactBroker.configuration.html_pact_renderer).to receive(:call).with(pact)
38
38
  subject
39
39
  end
40
40
 
@@ -11,25 +11,54 @@ module PactBroker::Api
11
11
  include Rack::Test::Methods
12
12
 
13
13
  let(:app) { PactBroker::API }
14
+ let(:json) { {some: 'json'}.to_json }
14
15
 
15
16
  describe "PUT" do
16
17
 
18
+ subject { put "/pacts/provider/Provider/consumer/Consumer/version/1.2", json, {'CONTENT_TYPE' => "application/json"} }
19
+
20
+ let(:response) { subject; last_response }
21
+
17
22
  context "with invalid JSON" do
23
+ let(:json) { '{' }
24
+
25
+ it "returns a 400 response" do
26
+ expect(response.status).to eq 400
27
+ end
28
+
29
+ it "returns a JSON content type" do
30
+ expect(response.headers['Content-Type']).to eq "application/json"
31
+ end
32
+
33
+ it "returns an error message" do
34
+ expect(JSON.parse(response.body)["error"]).to match /Error parsing JSON/
35
+ end
36
+ end
37
+
38
+ context "with a potential duplicate pacticipant" do
39
+
40
+ let(:pacticipant_service) { PactBroker::Services::PacticipantService }
41
+ let(:messages) { ["message1", "message2"] }
18
42
 
19
43
  before do
20
- put "/pacts/provider/Provider/consumer/Consumer/version/1.2", '{', {'CONTENT_TYPE' => "application/json"}
44
+ allow(pacticipant_service).to receive(:messages_for_potential_duplicate_pacticipants).and_return(messages)
45
+ end
46
+
47
+ it "checks for duplicates" do
48
+ expect(pacticipant_service).to receive(:messages_for_potential_duplicate_pacticipants).with(['Consumer', 'Provider'], 'http://example.org')
49
+ response
21
50
  end
22
51
 
23
52
  it "returns a 400 response" do
24
- expect(last_response.status).to eq 400
53
+ expect(response.status).to eq 400
25
54
  end
26
55
 
27
- it "returns a JSON content type" do
28
- expect(last_response.headers['Content-Type']).to eq "application/json"
56
+ it "returns a text response" do
57
+ expect(response.headers['Content-Type']).to eq 'text/plain'
29
58
  end
30
59
 
31
- it "returns an error message" do
32
- expect(JSON.parse(last_response.body)["error"]).to match /Invalid JSON/
60
+ it "returns the messages in the response body" do
61
+ expect(response.body).to eq "message1\nmessage2"
33
62
  end
34
63
  end
35
64
 
@@ -10,7 +10,7 @@ module PactBroker::Api
10
10
  let(:path) { "/webhooks/provider/Some%20Provider/consumer/Some%20Consumer" }
11
11
  let(:headers) { {'CONTENT_TYPE' => 'application/json'} }
12
12
  let(:webhook) { double('webhook')}
13
- let(:saved_webhook) { double('saved_webhook', uuid: 'webhook-uuid')}
13
+ let(:saved_webhook) { double('saved_webhook')}
14
14
  let(:provider) { instance_double(PactBroker::Models::Pacticipant)}
15
15
  let(:consumer) { instance_double(PactBroker::Models::Pacticipant)}
16
16
 
@@ -56,11 +56,13 @@ module PactBroker::Api
56
56
  some: 'json'
57
57
  }.to_json
58
58
  end
59
+ let(:next_uuid) { '123k2nvkkwjrwk34' }
59
60
 
60
61
  let(:errors) { [] }
61
62
 
62
63
  before do
63
64
  allow(PactBroker::Services::WebhookService).to receive(:create).and_return(saved_webhook)
65
+ allow(PactBroker::Services::WebhookService).to receive(:next_uuid).and_return(next_uuid)
64
66
  allow(webhook).to receive(:validate).and_return(errors)
65
67
  allow(PactBroker::Models::Webhook).to receive(:new).and_return(webhook)
66
68
  end
@@ -144,7 +146,7 @@ module PactBroker::Api
144
146
  end
145
147
 
146
148
  it "saves the webhook" do
147
- expect(PactBroker::Services::WebhookService).to receive(:create).with(webhook, consumer, provider)
149
+ expect(PactBroker::Services::WebhookService).to receive(:create).with(next_uuid, webhook, consumer, provider)
148
150
  subject
149
151
  end
150
152
 
@@ -155,12 +157,12 @@ module PactBroker::Api
155
157
 
156
158
  it "returns the Location header" do
157
159
  subject
158
- expect(last_response.headers['Location']).to include('webhook-uuid')
160
+ expect(last_response.headers['Location']).to include(next_uuid)
159
161
  end
160
162
 
161
163
  it "returns a JSON content type" do
162
164
  subject
163
- expect(last_response.headers['Content-Type']).to eq 'application/json'
165
+ expect(last_response.headers['Content-Type']).to eq 'application/hal+json'
164
166
  end
165
167
 
166
168
  it "generates the JSON response body" do
@@ -0,0 +1,91 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/api/resources/pacticipants'
3
+
4
+ module PactBroker
5
+ module Api
6
+ module Resources
7
+
8
+ describe Pacticipants do
9
+
10
+ describe "POST" do
11
+ let(:params) { {name: 'New Consumer'} }
12
+ let(:json) { params.to_json }
13
+ let(:model) { instance_double(model_class, validate: errors) }
14
+ let(:created_model) { instance_double(model_class) }
15
+ let(:errors) { [] }
16
+ let(:model_class) { PactBroker::Models::Pacticipant }
17
+ let(:decorator_class) { PactBroker::Api::Decorators::PacticipantRepresenter }
18
+ let(:decorator) { instance_double(decorator_class, to_json: response_json, from_json: model) }
19
+ let(:response_json) { {some: 'json'}.to_json }
20
+
21
+ before do
22
+ allow(model_class).to receive(:new).and_return(model)
23
+ allow(PactBroker::Services::PacticipantService).to receive(:create).and_return(created_model)
24
+ allow(decorator_class).to receive(:new).with(model).and_return(decorator)
25
+ allow(decorator_class).to receive(:new).with(created_model).and_return(decorator)
26
+ end
27
+
28
+ subject { post "/pacticipants", json, 'CONTENT_TYPE' => 'application/json' }
29
+
30
+ context "structurally incorrect JSON" do
31
+ let(:json) { "{" }
32
+
33
+ it "returns a 400" do
34
+ subject
35
+ expect(last_response.status).to eq 400
36
+ end
37
+ end
38
+
39
+ context "when the model is invalid" do
40
+ let(:errors) { ['error'] }
41
+
42
+ it "returns a 400" do
43
+ subject
44
+ expect(last_response.status).to eq 400
45
+ end
46
+ end
47
+
48
+ context "with valid JSON" do
49
+ it "creates the pacticipant" do
50
+ expect(PactBroker::Services::PacticipantService).to receive(:create).with(params)
51
+ subject
52
+ end
53
+
54
+ it "parses the request JSON" do
55
+ expect(decorator).to receive(:from_json).with(json)
56
+ subject
57
+ end
58
+
59
+ it "returns a 201" do
60
+ subject
61
+ expect(last_response.status).to eq 201
62
+ end
63
+
64
+ it "returns a Content-Type of application/hal+json" do
65
+ subject
66
+ expect(last_response.headers['Content-Type']).to eq 'application/hal+json'
67
+ end
68
+
69
+ it "creates a JSON representation of the new pacticipant" do
70
+ expect(decorator_class).to receive(:new).with(created_model)
71
+ expect(decorator).to receive(:to_json).with(instance_of(Decorators::DecoratorContext))
72
+ subject
73
+ end
74
+
75
+ it "includes the JSON pacticipant in the response body" do
76
+ subject
77
+ expect(last_response.body).to eq response_json
78
+ end
79
+
80
+ it "includes the newly created Location" do
81
+ subject
82
+ expect(last_response.headers['Location']).to eq "http://example.org/pacticpants/New%20Consumer"
83
+ end
84
+ end
85
+ end
86
+
87
+ end
88
+ end
89
+
90
+ end
91
+ end
@@ -8,11 +8,11 @@ module PactBroker
8
8
  context "default configuration" do
9
9
  describe ".html_pact_renderer" do
10
10
 
11
- let(:json_content) { {a: 'b'}.to_json }
11
+ let(:pact) { double('pact') }
12
12
 
13
13
  it "calls the inbuilt HtmlPactRenderer" do
14
- expect(PactBroker::Api::Renderers::HtmlPactRenderer).to receive(:call).with(json_content)
15
- PactBroker.configuration.html_pact_renderer.call json_content
14
+ expect(PactBroker::Api::Renderers::HtmlPactRenderer).to receive(:call).with(pact)
15
+ PactBroker.configuration.html_pact_renderer.call pact
16
16
  end
17
17
 
18
18
  end
@@ -0,0 +1,82 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/functions/find_potential_duplicate_pacticipant_names'
3
+
4
+ module PactBroker
5
+
6
+ module Functions
7
+
8
+ describe FindPotentialDuplicatePacticipantNames do
9
+
10
+ describe ".call" do
11
+
12
+ subject { FindPotentialDuplicatePacticipantNames.call(new_name, existing_names) }
13
+
14
+ context "when an existing name exactly equals the new name" do
15
+ let(:new_name) { 'Contracts Service' }
16
+ let(:existing_names) { ['Contracts Service', 'Contracts', 'Something'] }
17
+
18
+ it "does not return any potential duplicate names" do
19
+ expect(subject).to eq []
20
+ end
21
+ end
22
+
23
+ context "when an existing name mostly includes the new name" do
24
+ let(:new_name) { 'Contracts' }
25
+ let(:existing_names) { ['Contract Service', 'Contacts', 'Something'] }
26
+
27
+ it "returns the existing names that match" do
28
+ expect(subject).to eq ['Contract Service']
29
+ end
30
+ end
31
+
32
+ context "when a new name mostly includes an existing name" do
33
+ let(:new_name) { 'Contract Service' }
34
+ let(:existing_names) { ['Contracts', 'Contacts', 'Something'] }
35
+
36
+ it "returns the existing names that match" do
37
+ expect(subject).to eq ['Contracts']
38
+ end
39
+ end
40
+
41
+ context 'when a new name is the same but a different case' do
42
+ let(:new_name) { 'Contract Service' }
43
+ let(:existing_names) { ['contracts', 'Contacts', 'Something'] }
44
+
45
+ it "returns the existing names that match" do
46
+ expect(subject).to eq ['contracts']
47
+ end
48
+ end
49
+
50
+ context "when a new name is the same as an existing name but without spaces" do
51
+ let(:new_name) { 'ContractService' }
52
+ let(:existing_names) { ['Contracts Service', 'Contacts', 'Something'] }
53
+
54
+ it "returns the existing names that match" do
55
+ expect(subject).to eq ['Contracts Service']
56
+ end
57
+ end
58
+
59
+ context "when an existing name is the same as the new name but without spaces" do
60
+ let(:new_name) { 'Contract Service' }
61
+ let(:existing_names) { ['ContractsService', 'Contacts', 'Something'] }
62
+
63
+ it "returns the existing names that match" do
64
+ expect(subject).to eq ['ContractsService']
65
+ end
66
+ end
67
+
68
+ context "when the new name is similar to an existing but with underscores or dashes instead of spaces" do
69
+ let(:new_name) { 'Contract_Service' }
70
+ let(:existing_names) { ['ContractsService', 'Contracts Service', 'contracts-service', 'Contacts', 'Something'] }
71
+
72
+ it "returns the existing names that match" do
73
+ expect(subject).to eq ['ContractsService', 'Contracts Service', 'contracts-service']
74
+ end
75
+ end
76
+
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/messages'
3
+
4
+ module PactBroker
5
+ module Messages
6
+
7
+
8
+ describe "#potential_duplicate_pacticipant_message" do
9
+ let(:new_name) { 'Contracts' }
10
+ let(:fred) { double('Contracts Service', name: 'Contracts Service') }
11
+ let(:frederich) { double('Accepted Contracts', name: 'Accepted Contracts') }
12
+ let(:potential_duplicate_pacticipants) { [fred, frederich]}
13
+
14
+ let(:expected_message) { String.new <<-EOS
15
+ This is the first time a pact has been published for "Contracts".
16
+ The name "Contracts" is very similar to the following existing consumers/providers:
17
+ * Contracts Service
18
+ * Accepted Contracts
19
+ If you meant to specify one of the above names, please correct the pact configuration, and re-publish the pact.
20
+ If the pact is intended to be for a new consumer or provider, please manually create "Contracts" using the following command, and then re-publish the pact:
21
+ $ curl -v -XPOST -H "Content-Type: application/json" -d "{\\\"name\\\": \\\"Contracts\\\"}" http://example.org/pacticipants
22
+ EOS
23
+ }
24
+ subject { Messages.potential_duplicate_pacticipant_message new_name, potential_duplicate_pacticipants, 'http://example.org' }
25
+
26
+ it "returns a message" do
27
+ expect(subject).to eq expected_message
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/models/pacticipant'
3
+
4
+ module PactBroker
5
+
6
+ module Models
7
+
8
+ describe Pacticipant do
9
+
10
+ describe "validate" do
11
+
12
+ context "with all valid attributes" do
13
+ subject { Pacticipant.new name: 'Name' }
14
+
15
+ it "returns an empty array" do
16
+ expect(subject.validate).to eq []
17
+ end
18
+ end
19
+
20
+ context "with no name" do
21
+ subject { Pacticipant.new }
22
+
23
+ it "returns an error" do
24
+ expect(subject.validate).to eq ["Missing required attribute 'name'"]
25
+ end
26
+ end
27
+ end
28
+
29
+ end
30
+
31
+ end
32
+ end
@@ -5,6 +5,35 @@ module PactBroker
5
5
  module Repositories
6
6
  describe PactRepository do
7
7
 
8
+ describe "#find_all_pacts_between" do
9
+ let(:consumer_name) { 'Consumer' }
10
+ let(:provider_name) { 'Provider' }
11
+
12
+ before do
13
+ ProviderStateBuilder.new
14
+ .create_consumer(consumer_name)
15
+ .create_consumer_version("1.2.3")
16
+ .create_provider(provider_name)
17
+ .create_pact
18
+ .create_consumer_version("2.3.4")
19
+ .create_consumer_version_tag("prod")
20
+ .create_pact
21
+ .create_provider("Another Provider")
22
+ .create_pact
23
+ end
24
+
25
+ subject { PactRepository.new.find_all_pacts_between consumer_name, :and => provider_name }
26
+
27
+ it "returns the pacts between the specified consumer and provider" do
28
+ expect(subject.size).to eq 2
29
+ expect(subject.first.consumer.name).to eq consumer_name
30
+ expect(subject.first.provider.name).to eq provider_name
31
+ expect(subject.first.consumer_version.number).to eq "2.3.4"
32
+ expect(subject.first.consumer_version.tags.first.name).to eq "prod"
33
+ end
34
+
35
+ end
36
+
8
37
  describe "find_previous_pact" do
9
38
  before do
10
39
  ProviderStateBuilder.new
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+ require 'pact_broker/repositories/pacticipant_repository'
3
+ require 'support/provider_state_builder'
4
+
5
+ module PactBroker
6
+ module Repositories
7
+ describe PacticipantRepository do
8
+
9
+
10
+ describe "#pacticipant_names" do
11
+
12
+ before do
13
+ ProviderStateBuilder.new
14
+ .create_pacticipant("Plants")
15
+ .create_pacticipant("Animals")
16
+ end
17
+
18
+ subject { PacticipantRepository.new.pacticipant_names }
19
+
20
+ it "returns an array of pacticipant names" do
21
+ expect(subject).to eq ["Animals", "Plants"]
22
+ end
23
+
24
+ end
25
+
26
+
27
+ end
28
+ end
29
+ end