pacto 0.0.1

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.
@@ -0,0 +1,87 @@
1
+ describe Pacto do
2
+ let(:host) { 'http://localhost' }
3
+ let(:contract_name) { 'contract' }
4
+ let(:contract) { double('contract') }
5
+
6
+ after do
7
+ described_class.unregister_all!
8
+ end
9
+
10
+ describe '.register' do
11
+ context 'by default' do
12
+ it 'should register a contract under a given name' do
13
+ described_class.register(contract_name, contract)
14
+ described_class.registered[contract_name].should == contract
15
+ end
16
+ end
17
+
18
+ context 'when a contract has already been registered with the same name' do
19
+ it 'should raise an argument error' do
20
+ described_class.register(contract_name, contract)
21
+ expect { described_class.register(contract_name, contract) }.to raise_error(ArgumentError)
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '.build_from_file' do
27
+ let(:contract_path) { File.join('spec', 'data', "#{contract_name}.json") }
28
+ let(:file_pre_processor) { double('file_pre_processor') }
29
+ let(:file_content) {File.read(contract_path)}
30
+
31
+ it 'should build a contract given a JSON file path and a host' do
32
+ file_pre_processor.stub(:process).and_return(file_content)
33
+ described_class.build_from_file(contract_path, host, file_pre_processor).
34
+ should be_a_kind_of(Pacto::Contract)
35
+ end
36
+
37
+ it 'should process files using File Pre Processor module' do
38
+ file_pre_processor.should_receive(:process).with(file_content).and_return(file_content)
39
+ described_class.build_from_file(contract_path, host, file_pre_processor)
40
+ end
41
+ end
42
+
43
+ describe '.use' do
44
+ before do
45
+ described_class.register(contract_name, contract)
46
+ end
47
+
48
+ context 'by default' do
49
+ let(:instantiated_contract) { double('instantiated contract', :response_body => response_body)}
50
+ let(:response_body) { double('response_body') }
51
+
52
+ before do
53
+ described_class.registered[contract_name].stub(:instantiate => instantiated_contract)
54
+ instantiated_contract.stub(:stub!)
55
+ end
56
+
57
+ it 'should instantiate a contract with default values' do
58
+ described_class.registered[contract_name].should_receive(:instantiate).with(nil).and_return(instantiated_contract)
59
+ described_class.use(contract_name)
60
+ end
61
+
62
+ it 'should return the instantiated contract' do
63
+ described_class.use(contract_name).should == instantiated_contract
64
+ end
65
+
66
+ it 'should stub further requests with the instantiated contract' do
67
+ instantiated_contract.should_receive(:stub!)
68
+ described_class.use(contract_name)
69
+ end
70
+
71
+ end
72
+
73
+ context 'when contract has not been registered' do
74
+ it 'should raise an argument error' do
75
+ expect { described_class.use('unregistered') }.to raise_error ArgumentError
76
+ end
77
+ end
78
+ end
79
+
80
+ describe '.unregister_all!' do
81
+ it 'should unregister all previously registered contracts' do
82
+ described_class.register(contract_name, contract)
83
+ described_class.unregister_all!
84
+ described_class.registered.should be_empty
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,73 @@
1
+ module Pacto
2
+ describe Request do
3
+ let(:host) { 'http://localhost' }
4
+ let(:method) { 'GET' }
5
+ let(:path) { '/hello_world' }
6
+ let(:headers) { {'accept' => 'application/json'} }
7
+ let(:params) { {'foo' => 'bar'} }
8
+
9
+ let(:request) do
10
+ described_class.new(host, {
11
+ 'method' => method,
12
+ 'path' => path,
13
+ 'headers' => headers,
14
+ 'params' => params
15
+ })
16
+ end
17
+ subject { request }
18
+
19
+ its(:method) { should == :get }
20
+ its(:path) { should == path }
21
+ its(:headers) { should == headers }
22
+ its(:params) { should == params }
23
+
24
+ describe '#execute' do
25
+ let(:connection) { double('connection') }
26
+ let(:response) { double('response') }
27
+ let(:adapted_response) { double('adapted response') }
28
+
29
+ context 'for a GET request' do
30
+ it 'should make a GET request and return the response' do
31
+ HTTParty.should_receive(:get).
32
+ with(host + path, {:query => params, :headers => headers}).
33
+ and_return(response)
34
+ ResponseAdapter.should_receive(:new).with(response).and_return(adapted_response)
35
+ request.execute.should == adapted_response
36
+ end
37
+ end
38
+
39
+ context 'for a POST request' do
40
+ let(:method) { 'POST' }
41
+
42
+ it 'should make a POST request and return the response' do
43
+ HTTParty.should_receive(:post).
44
+ with(host + path, {:body => params.to_json, :headers => headers}).
45
+ and_return(response)
46
+ ResponseAdapter.should_receive(:new).with(response).and_return(adapted_response)
47
+ request.execute.should == adapted_response
48
+ end
49
+ end
50
+ end
51
+
52
+ describe "#absolute_uri" do
53
+ it "should be equal to the host followed by the path" do
54
+ request.absolute_uri.should == "http://localhost/hello_world"
55
+ end
56
+ end
57
+
58
+ describe "#full_uri" do
59
+ context "when the query (params) exists" do
60
+ it "should be equal to the host followed by the path and the query" do
61
+ request.full_uri.should == "http://localhost/hello_world?foo=bar"
62
+ end
63
+ end
64
+
65
+ context "when the query (params) does not exists" do
66
+ let(:params) { {} }
67
+ it "should be equal to the host followed by the path" do
68
+ request.full_uri.should == "http://localhost/hello_world"
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,27 @@
1
+ module Pacto
2
+ describe ResponseAdapter do
3
+ let(:response) do
4
+ double({
5
+ :code => 200,
6
+ :headers => {'foo' => ['bar', 'baz'], 'hello' => ['world']},
7
+ :body => double('body')
8
+ })
9
+ end
10
+
11
+ before do
12
+ @response_adapter = described_class.new(response)
13
+ end
14
+
15
+ it 'should have a status' do
16
+ @response_adapter.status.should == response.code
17
+ end
18
+
19
+ it 'should have a body' do
20
+ @response_adapter.body.should == response.body
21
+ end
22
+
23
+ it 'should normalize headers values according to RFC2616' do
24
+ @response_adapter.headers.should == {'foo' => 'bar,baz', 'hello' => 'world'}
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,114 @@
1
+ module Pacto
2
+ describe Response do
3
+ let(:definition) do
4
+ {
5
+ 'status' => 200,
6
+ 'headers' => {'Content-Type' => 'application/json'},
7
+ 'body' => double('definition body')
8
+ }
9
+ end
10
+
11
+ describe '#instantiate' do
12
+ let(:generated_body) { double('generated body') }
13
+
14
+ it 'should instantiate a response with a body that matches the given definition' do
15
+ JSON::Generator.should_receive(:generate).
16
+ with(definition['body']).
17
+ and_return(generated_body)
18
+
19
+ response = described_class.new(definition).instantiate
20
+ response.status.should == definition['status']
21
+ response.headers.should == definition['headers']
22
+ response.body.should == generated_body
23
+ end
24
+ end
25
+
26
+ describe '#validate' do
27
+ let(:status) { 200 }
28
+ let(:headers) { {'Content-Type' => 'application/json', 'Age' => '60'} }
29
+ let(:fake_response) do
30
+ double({
31
+ :status => status,
32
+ :headers => headers,
33
+ :body => 'body'
34
+ })
35
+ end
36
+
37
+ context 'when status, headers and body match' do
38
+ it 'should not return any errors' do
39
+ JSON::Validator.should_receive(:fully_validate).
40
+ with(definition['body'], fake_response.body).
41
+ and_return([])
42
+
43
+ response = described_class.new(definition)
44
+ response.validate(fake_response).should == []
45
+ end
46
+ end
47
+ context 'when status, headers and body match' do
48
+ it 'should not return any errors' do
49
+ JSON::Validator.should_receive(:fully_validate).
50
+ with(definition['body'], fake_response.body).
51
+ and_return([])
52
+
53
+ response = described_class.new(definition)
54
+ response.validate(fake_response).should == []
55
+ end
56
+ end
57
+
58
+ context 'when status does not match' do
59
+ let(:status) { 500 }
60
+
61
+ it 'should return a status error' do
62
+ JSON::Validator.stub(:fully_validate).and_return([])
63
+
64
+ response = described_class.new(definition)
65
+ response.validate(fake_response).should == ["Invalid status: expected #{definition['status']} but got #{status}"]
66
+ end
67
+ end
68
+
69
+ context 'when headers do not match' do
70
+ let(:headers) { {'Content-Type' => 'text/html'} }
71
+
72
+ it 'should return a header error' do
73
+ JSON::Validator.stub(:fully_validate).and_return([])
74
+
75
+ response = described_class.new(definition)
76
+ response.validate(fake_response).should == ["Invalid headers: expected #{definition['headers'].inspect} to be a subset of #{headers.inspect}"]
77
+ end
78
+ end
79
+
80
+ context 'when headers are a subset of expected headers' do
81
+ let(:headers) { {'Content-Type' => 'application/json'} }
82
+
83
+ it 'should not return any errors' do
84
+ JSON::Validator.stub(:fully_validate).and_return([])
85
+
86
+ response = described_class.new(definition)
87
+ response.validate(fake_response).should == []
88
+ end
89
+ end
90
+
91
+ context 'when headers values match but keys have different case' do
92
+ let(:headers) { {'content-type' => 'application/json'} }
93
+
94
+ it 'should not return any errors' do
95
+ JSON::Validator.stub(:fully_validate).and_return([])
96
+
97
+ response = described_class.new(definition)
98
+ response.validate(fake_response).should == []
99
+ end
100
+ end
101
+
102
+ context 'when body does not match' do
103
+ let(:errors) { [double('error1'), double('error2')] }
104
+
105
+ it 'should return a list of errors' do
106
+ JSON::Validator.stub(:fully_validate).and_return(errors)
107
+
108
+ response = described_class.new(definition)
109
+ response.validate(fake_response).should == errors
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,4 @@
1
+ require 'coveralls'
2
+ Coveralls.wear!
3
+
4
+ require 'pacto'
metadata ADDED
@@ -0,0 +1,295 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pacto
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ThoughtWorks & Abril
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: webmock
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: json-schema
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - '='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.4
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - '='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.0.4
62
+ - !ruby/object:Gem::Dependency
63
+ name: json-generator
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: hash-deep-merge
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: httparty
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ - !ruby/object:Gem::Dependency
111
+ name: addressable
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - ! '>='
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
126
+ - !ruby/object:Gem::Dependency
127
+ name: coveralls
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - ! '>='
140
+ - !ruby/object:Gem::Version
141
+ version: '0'
142
+ - !ruby/object:Gem::Dependency
143
+ name: rake
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: '0'
150
+ type: :development
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: '0'
158
+ - !ruby/object:Gem::Dependency
159
+ name: rspec
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ! '>='
164
+ - !ruby/object:Gem::Version
165
+ version: '0'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ! '>='
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ - !ruby/object:Gem::Dependency
175
+ name: guard-rspec
176
+ requirement: !ruby/object:Gem::Requirement
177
+ none: false
178
+ requirements:
179
+ - - ! '>='
180
+ - !ruby/object:Gem::Version
181
+ version: '0'
182
+ type: :development
183
+ prerelease: false
184
+ version_requirements: !ruby/object:Gem::Requirement
185
+ none: false
186
+ requirements:
187
+ - - ! '>='
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
190
+ - !ruby/object:Gem::Dependency
191
+ name: rb-fsevent
192
+ requirement: !ruby/object:Gem::Requirement
193
+ none: false
194
+ requirements:
195
+ - - ! '>='
196
+ - !ruby/object:Gem::Version
197
+ version: '0'
198
+ type: :development
199
+ prerelease: false
200
+ version_requirements: !ruby/object:Gem::Requirement
201
+ none: false
202
+ requirements:
203
+ - - ! '>='
204
+ - !ruby/object:Gem::Version
205
+ version: '0'
206
+ - !ruby/object:Gem::Dependency
207
+ name: terminal-notifier-guard
208
+ requirement: !ruby/object:Gem::Requirement
209
+ none: false
210
+ requirements:
211
+ - - ! '>='
212
+ - !ruby/object:Gem::Version
213
+ version: '0'
214
+ type: :development
215
+ prerelease: false
216
+ version_requirements: !ruby/object:Gem::Requirement
217
+ none: false
218
+ requirements:
219
+ - - ! '>='
220
+ - !ruby/object:Gem::Version
221
+ version: '0'
222
+ description: Pacto is a Ruby implementation of the [Consumer-Driven Contracts](http://martinfowler.com/articles/consumerDrivenContracts.html)
223
+ pattern for evolving services
224
+ email:
225
+ - abril_vejasp_dev@thoughtworks.com
226
+ executables: []
227
+ extensions: []
228
+ extra_rdoc_files: []
229
+ files:
230
+ - .gitignore
231
+ - .rspec
232
+ - .travis.yml
233
+ - Gemfile
234
+ - Guardfile
235
+ - LICENSE.txt
236
+ - README.md
237
+ - Rakefile
238
+ - TODO.md
239
+ - lib/pacto.rb
240
+ - lib/pacto/contract.rb
241
+ - lib/pacto/extensions.rb
242
+ - lib/pacto/file_pre_processor.rb
243
+ - lib/pacto/instantiated_contract.rb
244
+ - lib/pacto/rake_task.rb
245
+ - lib/pacto/request.rb
246
+ - lib/pacto/response.rb
247
+ - lib/pacto/response_adapter.rb
248
+ - lib/pacto/version.rb
249
+ - pacto.gemspec
250
+ - spec/data/contract.json
251
+ - spec/pacto/contract_spec.rb
252
+ - spec/pacto/extensions_spec.rb
253
+ - spec/pacto/file_pre_processor_spec.rb
254
+ - spec/pacto/instantiated_contract_spec.rb
255
+ - spec/pacto/pacto_spec.rb
256
+ - spec/pacto/request_spec.rb
257
+ - spec/pacto/response_adapter_spec.rb
258
+ - spec/pacto/response_spec.rb
259
+ - spec/spec_helper.rb
260
+ homepage: https://github.com/thoughtworks/pacto
261
+ licenses:
262
+ - MIT
263
+ post_install_message:
264
+ rdoc_options: []
265
+ require_paths:
266
+ - lib
267
+ required_ruby_version: !ruby/object:Gem::Requirement
268
+ none: false
269
+ requirements:
270
+ - - ! '>='
271
+ - !ruby/object:Gem::Version
272
+ version: '0'
273
+ required_rubygems_version: !ruby/object:Gem::Requirement
274
+ none: false
275
+ requirements:
276
+ - - ! '>='
277
+ - !ruby/object:Gem::Version
278
+ version: '0'
279
+ requirements: []
280
+ rubyforge_project:
281
+ rubygems_version: 1.8.25
282
+ signing_key:
283
+ specification_version: 3
284
+ summary: Consumer-Driven Contracts implementation
285
+ test_files:
286
+ - spec/data/contract.json
287
+ - spec/pacto/contract_spec.rb
288
+ - spec/pacto/extensions_spec.rb
289
+ - spec/pacto/file_pre_processor_spec.rb
290
+ - spec/pacto/instantiated_contract_spec.rb
291
+ - spec/pacto/pacto_spec.rb
292
+ - spec/pacto/request_spec.rb
293
+ - spec/pacto/response_adapter_spec.rb
294
+ - spec/pacto/response_spec.rb
295
+ - spec/spec_helper.rb