pacto 0.3.0.pre → 0.3.0

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