pact_broker 1.1.0 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
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