pacto 0.3.0.pre → 0.3.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 (111) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/.rubocop-todo.yml +0 -27
  4. data/.rubocop.yml +9 -0
  5. data/.ruby-gemset +1 -0
  6. data/.ruby-version +1 -0
  7. data/.travis.yml +4 -5
  8. data/CONTRIBUTING.md +112 -0
  9. data/Gemfile +5 -0
  10. data/Guardfile +18 -13
  11. data/README.md +157 -101
  12. data/Rakefile +3 -3
  13. data/features/configuration/strict_matchers.feature +97 -0
  14. data/features/evolve/README.md +11 -0
  15. data/features/evolve/existing_services.feature +82 -0
  16. data/features/generate/README.md +5 -0
  17. data/features/generate/generation.feature +28 -0
  18. data/features/steps/pacto_steps.rb +75 -0
  19. data/features/stub/README.md +2 -0
  20. data/features/stub/templates.feature +46 -0
  21. data/features/support/env.rb +11 -5
  22. data/features/validate/README.md +1 -0
  23. data/features/validate/body_only.feature +85 -0
  24. data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
  25. data/features/validate/validation.feature +36 -0
  26. data/lib/pacto.rb +61 -33
  27. data/lib/pacto/contract.rb +18 -15
  28. data/lib/pacto/contract_factory.rb +14 -11
  29. data/lib/pacto/contract_files.rb +17 -0
  30. data/lib/pacto/contract_list.rb +17 -0
  31. data/lib/pacto/contract_validator.rb +29 -0
  32. data/lib/pacto/core/configuration.rb +19 -17
  33. data/lib/pacto/core/contract_registry.rb +43 -0
  34. data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
  35. data/lib/pacto/core/modes.rb +33 -0
  36. data/lib/pacto/core/validation_registry.rb +45 -0
  37. data/lib/pacto/erb_processor.rb +0 -1
  38. data/lib/pacto/extensions.rb +18 -4
  39. data/lib/pacto/generator.rb +34 -49
  40. data/lib/pacto/generator/filters.rb +41 -0
  41. data/lib/pacto/hooks/erb_hook.rb +4 -3
  42. data/lib/pacto/logger.rb +4 -2
  43. data/lib/pacto/meta_schema.rb +4 -2
  44. data/lib/pacto/rake_task.rb +28 -25
  45. data/lib/pacto/request_clause.rb +43 -0
  46. data/lib/pacto/request_pattern.rb +8 -0
  47. data/lib/pacto/response_clause.rb +15 -0
  48. data/lib/pacto/rspec.rb +102 -0
  49. data/lib/pacto/stubs/uri_pattern.rb +23 -0
  50. data/lib/pacto/stubs/webmock_adapter.rb +69 -0
  51. data/lib/pacto/stubs/webmock_helper.rb +71 -0
  52. data/lib/pacto/ui.rb +7 -0
  53. data/lib/pacto/uri.rb +9 -0
  54. data/lib/pacto/validation.rb +57 -0
  55. data/lib/pacto/validators/body_validator.rb +41 -0
  56. data/lib/pacto/validators/request_body_validator.rb +23 -0
  57. data/lib/pacto/validators/response_body_validator.rb +23 -0
  58. data/lib/pacto/validators/response_header_validator.rb +49 -0
  59. data/lib/pacto/validators/response_status_validator.rb +24 -0
  60. data/lib/pacto/version.rb +1 -1
  61. data/pacto.gemspec +33 -29
  62. data/resources/contract_schema.json +8 -176
  63. data/resources/draft-03.json +174 -0
  64. data/spec/integration/data/strict_contract.json +2 -2
  65. data/spec/integration/e2e_spec.rb +22 -31
  66. data/spec/integration/rspec_spec.rb +94 -0
  67. data/spec/integration/templating_spec.rb +9 -12
  68. data/{lib → spec}/pacto/server.rb +0 -0
  69. data/{lib → spec}/pacto/server/dummy.rb +11 -8
  70. data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
  71. data/spec/spec_helper.rb +2 -0
  72. data/spec/unit/hooks/erb_hook_spec.rb +15 -15
  73. data/spec/unit/pacto/configuration_spec.rb +2 -10
  74. data/spec/unit/pacto/contract_factory_spec.rb +16 -13
  75. data/spec/unit/pacto/contract_files_spec.rb +42 -0
  76. data/spec/unit/pacto/contract_list_spec.rb +35 -0
  77. data/spec/unit/pacto/contract_spec.rb +43 -44
  78. data/spec/unit/pacto/contract_validator_spec.rb +85 -0
  79. data/spec/unit/pacto/core/configuration_spec.rb +4 -11
  80. data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
  81. data/spec/unit/pacto/core/modes_spec.rb +18 -0
  82. data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
  83. data/spec/unit/pacto/core/validation_spec.rb +60 -0
  84. data/spec/unit/pacto/extensions_spec.rb +14 -23
  85. data/spec/unit/pacto/generator/filters_spec.rb +99 -0
  86. data/spec/unit/pacto/generator_spec.rb +34 -73
  87. data/spec/unit/pacto/meta_schema_spec.rb +46 -6
  88. data/spec/unit/pacto/pacto_spec.rb +17 -15
  89. data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
  90. data/spec/unit/pacto/request_pattern_spec.rb +22 -0
  91. data/spec/unit/pacto/response_clause_spec.rb +54 -0
  92. data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
  93. data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
  94. data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
  95. data/spec/unit/pacto/uri_spec.rb +20 -0
  96. data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
  97. data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
  98. data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
  99. metadata +230 -146
  100. data/features/generation/generation.feature +0 -25
  101. data/lib/pacto/core/contract_repository.rb +0 -44
  102. data/lib/pacto/hash_merge_processor.rb +0 -14
  103. data/lib/pacto/request.rb +0 -57
  104. data/lib/pacto/response.rb +0 -63
  105. data/lib/pacto/response_adapter.rb +0 -24
  106. data/lib/pacto/stubs/built_in.rb +0 -57
  107. data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
  108. data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
  109. data/spec/unit/pacto/response_adapter_spec.rb +0 -25
  110. data/spec/unit/pacto/response_spec.rb +0 -201
  111. data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
@@ -1,19 +1,22 @@
1
1
  module Pacto
2
- describe Request do
2
+ describe RequestClause do
3
3
  let(:host) { 'http://localhost' }
4
4
  let(:method) { 'GET' }
5
5
  let(:path) { '/hello_world' }
6
6
  let(:headers) { {'accept' => 'application/json'} }
7
7
  let(:params) { {'foo' => 'bar'} }
8
+ let(:body) { double :body }
8
9
  let(:params_as_json) { "{\"foo\":\"bar\"}" }
9
10
  let(:absolute_uri) { "#{host}#{path}" }
10
11
  subject(:request) do
11
- described_class.new(host, {
12
+ RequestClause.new(
13
+ host,
12
14
  'method' => method,
13
15
  'path' => path,
14
16
  'headers' => headers,
15
- 'params' => params
16
- })
17
+ 'params' => params,
18
+ 'body' => body
19
+ )
17
20
  end
18
21
 
19
22
  it 'has a host' do
@@ -34,6 +37,20 @@ module Pacto
34
37
  end
35
38
  end
36
39
 
40
+ describe '#schema' do
41
+ it 'delegates to definition\'s body' do
42
+ expect(request.schema).to eq body
43
+ end
44
+
45
+ describe 'when definition does not have body' do
46
+ let(:body) { nil }
47
+
48
+ it 'returns an empty empty hash' do
49
+ expect(request.schema).to eq({})
50
+ end
51
+ end
52
+ end
53
+
37
54
  describe '#path' do
38
55
  it 'delegates to definition' do
39
56
  expect(request.path).to eq path
@@ -58,30 +75,24 @@ module Pacto
58
75
  let(:adapted_response) { double 'adapted response' }
59
76
 
60
77
  before do
61
- HTTParty.stub(:get => response)
62
- HTTParty.stub(:post => response)
63
- ResponseAdapter.stub(:new => adapted_response)
78
+ WebMock.stub_request(:get, 'http://localhost/hello_world?foo=bar').
79
+ to_return(:status => 200, :body => '', :headers => {})
80
+ WebMock.stub_request(:post, 'http://localhost/hello_world?foo=bar').
81
+ to_return(:status => 200, :body => '', :headers => {})
82
+ # TODO: Should we just use WebMock?
64
83
  end
65
84
 
66
85
  context 'for any request' do
67
- it 'processes the response with an ResponseAdapter' do
68
- ResponseAdapter.should_receive(:new).
69
- with(response).
70
- and_return(adapted_response)
71
- request.execute
72
- end
73
-
74
- it 'returns the adapted response' do
75
- expect(request.execute).to be adapted_response
86
+ it 'returns the a Faraday response' do
87
+ expect(request.execute).to be_a Faraday::Response
76
88
  end
77
89
  end
78
90
 
79
91
  context 'for a GET request' do
80
92
  it 'makes the request thru the http client' do
81
- HTTParty.should_receive(:get).
82
- with(absolute_uri, {:query => params, :headers => headers}).
83
- and_return(response)
84
93
  request.execute
94
+ expect(WebMock).to have_requested(:get, 'http://localhost/hello_world?foo=bar').
95
+ with(:headers => headers)
85
96
  end
86
97
  end
87
98
 
@@ -89,32 +100,9 @@ module Pacto
89
100
  let(:method) { 'POST' }
90
101
 
91
102
  it 'makes the request thru the http client' do
92
- HTTParty.should_receive(:post).
93
- with(absolute_uri, {:body => params_as_json, :headers => headers}).
94
- and_return(response)
95
103
  request.execute
96
- end
97
- end
98
- end
99
-
100
- describe '#absolute_uri' do
101
- it 'returns the host followed by the path' do
102
- expect(request.absolute_uri).to eq absolute_uri
103
- end
104
- end
105
-
106
- describe '#full_uri' do
107
- context 'when the request has a query' do
108
- it 'returns the host followed by the path and the query' do
109
- expect(request.full_uri).to eq 'http://localhost/hello_world?foo=bar'
110
- end
111
- end
112
-
113
- context 'when the query does not have a query' do
114
- let(:params) { {} }
115
-
116
- it 'returns the host followed by the path' do
117
- expect(request.absolute_uri).to eq 'http://localhost/hello_world'
104
+ expect(WebMock).to have_requested(:post, 'http://localhost/hello_world?foo=bar').
105
+ with(:headers => headers)
118
106
  end
119
107
  end
120
108
  end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ module Pacto
4
+ describe RequestPattern do
5
+ let(:method) { :get }
6
+ let(:uri_pattern) { double }
7
+ let(:request_pattern) { double }
8
+ let(:request) { double(method: method) }
9
+
10
+ it 'returns a pattern that combines the contracts method and uri_pattern' do
11
+ expect(UriPattern).to receive(:for).
12
+ with(request).
13
+ and_return(uri_pattern)
14
+
15
+ expect(WebMock::RequestPattern).to receive(:new).
16
+ with(method, uri_pattern).
17
+ and_return(request_pattern)
18
+
19
+ expect(RequestPattern.for(request)).to eq request_pattern
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,54 @@
1
+ module Pacto
2
+ describe ResponseClause do
3
+ let(:body_definition) do
4
+ {
5
+ :type => 'object',
6
+ :required => true,
7
+ :properties => double('body definition properties')
8
+ }
9
+ end
10
+
11
+ let(:definition) do
12
+ {
13
+ 'status' => 200,
14
+ 'headers' => {
15
+ 'Content-Type' => 'application/json'
16
+ },
17
+ 'body' => body_definition
18
+ }
19
+ end
20
+
21
+ subject(:response) { ResponseClause.new(definition) }
22
+
23
+ it 'has a status' do
24
+ expect(response.status).to eq(200)
25
+ end
26
+
27
+ it 'has a headers hash' do
28
+ expect(response.headers).to eq(
29
+ 'Content-Type' => 'application/json'
30
+ )
31
+ end
32
+
33
+ it 'has a schema' do
34
+ expect(response.schema).to eq(body_definition)
35
+ end
36
+
37
+ it 'has a default value for the schema' do
38
+ response = ResponseClause.new(definition.merge('body' => nil))
39
+ expect(response.schema).to eq(Hash.new)
40
+ end
41
+
42
+ describe 'the response body' do
43
+ let(:generated_body) { double }
44
+
45
+ it 'is the json generated from the schema' do
46
+ JSON::Generator.should_receive(:generate).
47
+ with(definition['body']).
48
+ and_return(generated_body)
49
+
50
+ expect(response.body).to eq(generated_body)
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ module Pacto
4
+ describe UriPattern do
5
+ context 'with non-strict matchers' do
6
+ it 'returns a regex containing the host and path' do
7
+ Pacto.configuration.strict_matchers = false
8
+ request = double(host: 'myhost.com', path: '/stuff')
9
+ expect(UriPattern.for(request).to_s).to eq(/myhost\.com\/stuff/.to_s)
10
+ end
11
+
12
+ it 'turns segments preceded by : into wildcards' do
13
+ Pacto.configuration.strict_matchers = false
14
+ request = double(host: 'myhost.com', path: '/:id')
15
+ wildcard = '[^\/\?#]+'
16
+ expect(UriPattern.for(request).to_s).to eq(/myhost\.com\/#{wildcard}/.to_s)
17
+ end
18
+ end
19
+
20
+ context 'with strict matchers' do
21
+ it 'returns a string with the host and path' do
22
+ Pacto.configuration.strict_matchers = true
23
+ request = double(host: 'myhost.com', path: '/stuff')
24
+ expect(UriPattern.for(request)).to eq('myhost.com/stuff')
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,205 @@
1
+ module Pacto
2
+ module Stubs
3
+ describe WebMockAdapter do
4
+ let(:request) do
5
+ double(
6
+ :host => 'http://localhost',
7
+ :method => method,
8
+ :path => '/hello_world',
9
+ :headers => {'Accept' => 'application/json'},
10
+ :params => {'foo' => 'bar'}
11
+ )
12
+ end
13
+
14
+ let(:method) { :get }
15
+
16
+ let(:response) do
17
+ double(
18
+ :status => 200,
19
+ :headers => {},
20
+ :body => body
21
+ )
22
+ end
23
+
24
+ let(:body) do
25
+ {'message' => 'foo'}
26
+ end
27
+
28
+ let(:stubbed_request) do
29
+ {
30
+ :path => nil
31
+ }
32
+ end
33
+
34
+ let(:request_pattern) { double('request_pattern') }
35
+
36
+ subject(:built_in) { WebMockAdapter.new }
37
+
38
+ before(:each) do
39
+ stubbed_request.stub(:to_return).with(
40
+ :status => response.status,
41
+ :headers => response.headers,
42
+ :body => response.body.to_json,
43
+ )
44
+ stubbed_request.stub(:request_pattern).and_return request_pattern
45
+ end
46
+
47
+ describe '#initialize' do
48
+ it 'sets up a hook' do
49
+ WebMock.should_receive(:after_request) do | arg, &block |
50
+ expect(block.parameters).to have(2).items
51
+ end
52
+
53
+ WebMockAdapter.new
54
+ end
55
+ end
56
+
57
+ describe '#process_hooks' do
58
+ let(:request_signature) { double('request_signature') }
59
+
60
+ before do
61
+ Pacto.configuration.hook.stub(:process)
62
+ end
63
+
64
+ it 'calls the registered hook' do
65
+ Pacto.configuration.hook.should_receive(:process)
66
+ .with(anything, request_signature, response)
67
+ built_in.process_hooks request_signature, response
68
+ end
69
+
70
+ it 'calls generate when generate is enabled' do
71
+ Pacto.generate!
72
+ WebMockHelper.should_receive(:generate).with(request_signature, response)
73
+ built_in.process_hooks request_signature, response
74
+ end
75
+
76
+ it 'calls validate when validate mode is enabled' do
77
+ Pacto.validate!
78
+ WebMockHelper.should_receive(:validate).with(request_signature, response)
79
+ built_in.process_hooks request_signature, response
80
+ end
81
+ end
82
+
83
+ describe '#stub_request!' do
84
+ before(:each) do
85
+ WebMock.should_receive(:stub_request) do | method, url |
86
+ stubbed_request[:path] = url
87
+ stubbed_request
88
+ end
89
+ end
90
+
91
+ context 'when the response body is an object' do
92
+ let(:body) do
93
+ {'message' => 'foo'}
94
+ end
95
+
96
+ it 'stubs the response body with a json representation' do
97
+ stubbed_request.should_receive(:to_return).with(
98
+ :status => response.status,
99
+ :headers => response.headers,
100
+ :body => response.body.to_json
101
+ )
102
+
103
+ request_pattern.stub(:with)
104
+
105
+ built_in.stub_request! request, response
106
+ end
107
+
108
+ context 'when the response body is an array' do
109
+ let(:body) do
110
+ [1, 2, 3]
111
+ end
112
+
113
+ it 'stubs the response body with a json representation' do
114
+ stubbed_request.should_receive(:to_return).with(
115
+ :status => response.status,
116
+ :headers => response.headers,
117
+ :body => response.body.to_json
118
+ )
119
+
120
+ request_pattern.stub(:with)
121
+
122
+ built_in.stub_request! request, response
123
+ end
124
+ end
125
+
126
+ context 'when the response body is not an object or an array' do
127
+ let(:body) { nil }
128
+
129
+ it 'stubs the response body with the original body' do
130
+ stubbed_request.should_receive(:to_return).with(
131
+ :status => response.status,
132
+ :headers => response.headers,
133
+ :body => response.body
134
+ )
135
+
136
+ request_pattern.stub(:with)
137
+
138
+ built_in.stub_request! request, response
139
+ end
140
+ end
141
+
142
+ context 'a GET request' do
143
+ let(:method) { :get }
144
+
145
+ it 'uses WebMock to stub the request' do
146
+ request_pattern.should_receive(:with).
147
+ with(:headers => request.headers, :query => request.params).
148
+ and_return(stubbed_request)
149
+ built_in.stub_request! request, response
150
+ end
151
+ end
152
+
153
+ context 'a POST request' do
154
+ let(:method) { :post }
155
+
156
+ it 'uses WebMock to stub the request' do
157
+ request_pattern.should_receive(:with).
158
+ with(:headers => request.headers, :body => request.params).
159
+ and_return(stubbed_request)
160
+ built_in.stub_request! request, response
161
+ end
162
+ end
163
+
164
+ context 'a request with no headers' do
165
+ let(:request) do
166
+ double(
167
+ :host => 'http://localhost',
168
+ :method => :get,
169
+ :path => '/hello_world',
170
+ :headers => {},
171
+ :params => {'foo' => 'bar'}
172
+ )
173
+ end
174
+
175
+ it 'uses WebMock to stub the request' do
176
+ request_pattern.should_receive(:with).
177
+ with(:query => request.params).
178
+ and_return(stubbed_request)
179
+ built_in.stub_request! request, response
180
+ end
181
+ end
182
+
183
+ context 'a request with no params' do
184
+ let(:request) do
185
+ double(
186
+ :host => 'http://localhost',
187
+ :method => :get,
188
+ :path => '/hello_world',
189
+ :headers => {},
190
+ :params => {}
191
+ )
192
+ end
193
+
194
+ it 'uses WebMock to stub the request' do
195
+ request_pattern.should_receive(:with).
196
+ with({}).
197
+ and_return(stubbed_request)
198
+ built_in.stub_request! request, response
199
+ end
200
+ end
201
+ end
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,20 @@
1
+ module Pacto
2
+ module Stubs
3
+ describe WebMockHelper do
4
+ before do
5
+ WebMock.stub_request(:get, 'www.example.com').to_return(:body => 'pacto')
6
+ WebMock.after_request do |request_signature, response|
7
+ @request_signature = request_signature
8
+ @response = response
9
+ end
10
+ Faraday.get 'http://www.example.com'
11
+ end
12
+
13
+ describe '#validate' do
14
+ it 'validates a WebMock request/response pair' do
15
+ described_class.validate @request_signature, @response
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end