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.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/.rubocop-todo.yml +0 -27
- data/.rubocop.yml +9 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +4 -5
- data/CONTRIBUTING.md +112 -0
- data/Gemfile +5 -0
- data/Guardfile +18 -13
- data/README.md +157 -101
- data/Rakefile +3 -3
- data/features/configuration/strict_matchers.feature +97 -0
- data/features/evolve/README.md +11 -0
- data/features/evolve/existing_services.feature +82 -0
- data/features/generate/README.md +5 -0
- data/features/generate/generation.feature +28 -0
- data/features/steps/pacto_steps.rb +75 -0
- data/features/stub/README.md +2 -0
- data/features/stub/templates.feature +46 -0
- data/features/support/env.rb +11 -5
- data/features/validate/README.md +1 -0
- data/features/validate/body_only.feature +85 -0
- data/features/{journeys/validation.feature → validate/meta_validation.feature} +41 -24
- data/features/validate/validation.feature +36 -0
- data/lib/pacto.rb +61 -33
- data/lib/pacto/contract.rb +18 -15
- data/lib/pacto/contract_factory.rb +14 -11
- data/lib/pacto/contract_files.rb +17 -0
- data/lib/pacto/contract_list.rb +17 -0
- data/lib/pacto/contract_validator.rb +29 -0
- data/lib/pacto/core/configuration.rb +19 -17
- data/lib/pacto/core/contract_registry.rb +43 -0
- data/lib/pacto/core/{callback.rb → hook.rb} +3 -3
- data/lib/pacto/core/modes.rb +33 -0
- data/lib/pacto/core/validation_registry.rb +45 -0
- data/lib/pacto/erb_processor.rb +0 -1
- data/lib/pacto/extensions.rb +18 -4
- data/lib/pacto/generator.rb +34 -49
- data/lib/pacto/generator/filters.rb +41 -0
- data/lib/pacto/hooks/erb_hook.rb +4 -3
- data/lib/pacto/logger.rb +4 -2
- data/lib/pacto/meta_schema.rb +4 -2
- data/lib/pacto/rake_task.rb +28 -25
- data/lib/pacto/request_clause.rb +43 -0
- data/lib/pacto/request_pattern.rb +8 -0
- data/lib/pacto/response_clause.rb +15 -0
- data/lib/pacto/rspec.rb +102 -0
- data/lib/pacto/stubs/uri_pattern.rb +23 -0
- data/lib/pacto/stubs/webmock_adapter.rb +69 -0
- data/lib/pacto/stubs/webmock_helper.rb +71 -0
- data/lib/pacto/ui.rb +7 -0
- data/lib/pacto/uri.rb +9 -0
- data/lib/pacto/validation.rb +57 -0
- data/lib/pacto/validators/body_validator.rb +41 -0
- data/lib/pacto/validators/request_body_validator.rb +23 -0
- data/lib/pacto/validators/response_body_validator.rb +23 -0
- data/lib/pacto/validators/response_header_validator.rb +49 -0
- data/lib/pacto/validators/response_status_validator.rb +24 -0
- data/lib/pacto/version.rb +1 -1
- data/pacto.gemspec +33 -29
- data/resources/contract_schema.json +8 -176
- data/resources/draft-03.json +174 -0
- data/spec/integration/data/strict_contract.json +2 -2
- data/spec/integration/e2e_spec.rb +22 -31
- data/spec/integration/rspec_spec.rb +94 -0
- data/spec/integration/templating_spec.rb +9 -12
- data/{lib → spec}/pacto/server.rb +0 -0
- data/{lib → spec}/pacto/server/dummy.rb +11 -8
- data/{lib → spec}/pacto/server/playback_servlet.rb +1 -1
- data/spec/spec_helper.rb +2 -0
- data/spec/unit/hooks/erb_hook_spec.rb +15 -15
- data/spec/unit/pacto/configuration_spec.rb +2 -10
- data/spec/unit/pacto/contract_factory_spec.rb +16 -13
- data/spec/unit/pacto/contract_files_spec.rb +42 -0
- data/spec/unit/pacto/contract_list_spec.rb +35 -0
- data/spec/unit/pacto/contract_spec.rb +43 -44
- data/spec/unit/pacto/contract_validator_spec.rb +85 -0
- data/spec/unit/pacto/core/configuration_spec.rb +4 -11
- data/spec/unit/pacto/core/contract_registry_spec.rb +119 -0
- data/spec/unit/pacto/core/modes_spec.rb +18 -0
- data/spec/unit/pacto/core/validation_registry_spec.rb +76 -0
- data/spec/unit/pacto/core/validation_spec.rb +60 -0
- data/spec/unit/pacto/extensions_spec.rb +14 -23
- data/spec/unit/pacto/generator/filters_spec.rb +99 -0
- data/spec/unit/pacto/generator_spec.rb +34 -73
- data/spec/unit/pacto/meta_schema_spec.rb +46 -6
- data/spec/unit/pacto/pacto_spec.rb +17 -15
- data/spec/unit/pacto/{request_spec.rb → request_clause_spec.rb} +32 -44
- data/spec/unit/pacto/request_pattern_spec.rb +22 -0
- data/spec/unit/pacto/response_clause_spec.rb +54 -0
- data/spec/unit/pacto/stubs/uri_pattern_spec.rb +28 -0
- data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +205 -0
- data/spec/unit/pacto/stubs/webmock_helper_spec.rb +20 -0
- data/spec/unit/pacto/uri_spec.rb +20 -0
- data/spec/unit/pacto/validators/body_validator_spec.rb +105 -0
- data/spec/unit/pacto/validators/response_header_validator_spec.rb +94 -0
- data/spec/unit/pacto/validators/response_status_validator_spec.rb +20 -0
- metadata +230 -146
- data/features/generation/generation.feature +0 -25
- data/lib/pacto/core/contract_repository.rb +0 -44
- data/lib/pacto/hash_merge_processor.rb +0 -14
- data/lib/pacto/request.rb +0 -57
- data/lib/pacto/response.rb +0 -63
- data/lib/pacto/response_adapter.rb +0 -24
- data/lib/pacto/stubs/built_in.rb +0 -57
- data/spec/unit/pacto/core/contract_repository_spec.rb +0 -133
- data/spec/unit/pacto/hash_merge_processor_spec.rb +0 -20
- data/spec/unit/pacto/response_adapter_spec.rb +0 -25
- data/spec/unit/pacto/response_spec.rb +0 -201
- data/spec/unit/pacto/stubs/built_in_spec.rb +0 -168
@@ -1,25 +0,0 @@
|
|
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
|
-
subject(:response_adapter) { described_class.new response }
|
12
|
-
|
13
|
-
it 'has a status' do
|
14
|
-
expect(response_adapter.status).to eq response.code
|
15
|
-
end
|
16
|
-
|
17
|
-
it 'has a body' do
|
18
|
-
expect(response_adapter.body).to eq response.body
|
19
|
-
end
|
20
|
-
|
21
|
-
it 'normalizes headers values according to RFC2616' do
|
22
|
-
expect(response_adapter.headers).to eq({'foo' => 'bar,baz', 'hello' => 'world'})
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
@@ -1,201 +0,0 @@
|
|
1
|
-
module Pacto
|
2
|
-
describe Response do
|
3
|
-
let(:body_definition) do
|
4
|
-
{:type => 'object', :required => true, :properties => double('body definition properties')}
|
5
|
-
end
|
6
|
-
let(:definition) do
|
7
|
-
{
|
8
|
-
'status' => 200,
|
9
|
-
'headers' => {'Content-Type' => 'application/json'},
|
10
|
-
'body' => body_definition
|
11
|
-
}
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '#instantiate' do
|
15
|
-
let(:generated_body) { double('generated body') }
|
16
|
-
|
17
|
-
it 'instantiates a response with a body that matches the given definition' do
|
18
|
-
JSON::Generator.should_receive(:generate).
|
19
|
-
with(definition['body']).
|
20
|
-
and_return(generated_body)
|
21
|
-
|
22
|
-
response = described_class.new(definition).instantiate
|
23
|
-
expect(response.status).to eq definition['status']
|
24
|
-
expect(response.headers).to eq definition['headers']
|
25
|
-
expect(response.body).to eq generated_body
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
describe '#validate' do
|
30
|
-
let(:status) { 200 }
|
31
|
-
let(:headers) { {'Content-Type' => 'application/json', 'Age' => '60'} }
|
32
|
-
let(:response_body) { {'message' => 'response'} }
|
33
|
-
let(:fake_response) do
|
34
|
-
double({
|
35
|
-
:status => status,
|
36
|
-
:headers => headers,
|
37
|
-
:body => response_body
|
38
|
-
})
|
39
|
-
end
|
40
|
-
|
41
|
-
context 'when status, headers and body match' do
|
42
|
-
it 'does not return any errors' do
|
43
|
-
JSON::Validator.should_receive(:fully_validate).
|
44
|
-
with(definition['body'], fake_response.body, :version => :draft3).
|
45
|
-
and_return([])
|
46
|
-
|
47
|
-
response = described_class.new(definition)
|
48
|
-
expect(response.validate(fake_response)).to be_empty
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
context 'when body is a pure string and matches the description' do
|
53
|
-
let(:string_required) { true }
|
54
|
-
let(:body_definition) do
|
55
|
-
{ 'type' => 'string', 'required' => string_required }
|
56
|
-
end
|
57
|
-
let(:response_body) { 'a simple string' }
|
58
|
-
|
59
|
-
it 'does not validate using JSON Schema' do
|
60
|
-
response = described_class.new(definition)
|
61
|
-
|
62
|
-
JSON::Validator.should_not_receive(:fully_validate)
|
63
|
-
response.validate(fake_response)
|
64
|
-
end
|
65
|
-
|
66
|
-
context 'if required' do
|
67
|
-
it 'does not return an error when body is a string' do
|
68
|
-
response = described_class.new(definition)
|
69
|
-
|
70
|
-
expect(response.validate(fake_response)).to be_empty
|
71
|
-
end
|
72
|
-
|
73
|
-
it 'returns an error when body is nil' do
|
74
|
-
response = described_class.new(definition)
|
75
|
-
|
76
|
-
fake_response.stub(:body).and_return(nil)
|
77
|
-
expect(response.validate(fake_response).size).to eq 1
|
78
|
-
end
|
79
|
-
end
|
80
|
-
|
81
|
-
context 'if not required' do
|
82
|
-
let(:string_required) { false }
|
83
|
-
|
84
|
-
it 'does not return an error when body is a string' do
|
85
|
-
response = described_class.new(definition)
|
86
|
-
|
87
|
-
expect(response.validate(fake_response)).to be_empty
|
88
|
-
end
|
89
|
-
|
90
|
-
it 'does not return an error when body is nil' do
|
91
|
-
response = described_class.new(definition)
|
92
|
-
|
93
|
-
fake_response.stub(:body).and_return(nil)
|
94
|
-
expect(response.validate(fake_response)).to be_empty
|
95
|
-
end
|
96
|
-
end
|
97
|
-
|
98
|
-
context 'if contains pattern' do
|
99
|
-
let(:body_definition) do
|
100
|
-
{ 'type' => 'string', 'required' => string_required, 'pattern' => 'a.c' }
|
101
|
-
end
|
102
|
-
|
103
|
-
context 'body matches pattern' do
|
104
|
-
let(:response_body) { 'cabcd' }
|
105
|
-
|
106
|
-
it 'does not return an error' do
|
107
|
-
response = described_class.new(definition)
|
108
|
-
|
109
|
-
expect(response.validate(fake_response)).to be_empty
|
110
|
-
end
|
111
|
-
end
|
112
|
-
|
113
|
-
context 'body does not match pattern' do
|
114
|
-
let(:response_body) { 'cabscd' }
|
115
|
-
|
116
|
-
it 'returns an error' do
|
117
|
-
response = described_class.new(definition)
|
118
|
-
|
119
|
-
expect(response.validate(fake_response).size).to eq 1
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
context 'when status does not match' do
|
127
|
-
let(:status) { 500 }
|
128
|
-
|
129
|
-
it 'returns a status error' do
|
130
|
-
JSON::Validator.should_not_receive(:fully_validate)
|
131
|
-
|
132
|
-
response = described_class.new(definition)
|
133
|
-
expect(response.validate(fake_response)).to eq ["Invalid status: expected #{definition['status']} but got #{status}"]
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
context 'when headers do not match' do
|
138
|
-
let(:headers) { {'Content-Type' => 'text/html'} }
|
139
|
-
|
140
|
-
it 'returns a header error' do
|
141
|
-
JSON::Validator.should_not_receive(:fully_validate)
|
142
|
-
|
143
|
-
response = described_class.new(definition)
|
144
|
-
expect(response.validate(fake_response)).to eq ["Invalid headers: expected #{definition['headers'].inspect} to be a subset of #{headers.inspect}"]
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
context 'when headers are a subset of expected headers' do
|
149
|
-
let(:headers) { {'Content-Type' => 'application/json'} }
|
150
|
-
|
151
|
-
it 'does not return any errors' do
|
152
|
-
JSON::Validator.stub(:fully_validate).and_return([])
|
153
|
-
|
154
|
-
response = described_class.new(definition)
|
155
|
-
expect(response.validate(fake_response)).to be_empty
|
156
|
-
end
|
157
|
-
end
|
158
|
-
|
159
|
-
context 'when headers values match but keys have different case' do
|
160
|
-
let(:headers) { {'content-type' => 'application/json'} }
|
161
|
-
|
162
|
-
it 'does not return any errors' do
|
163
|
-
JSON::Validator.stub(:fully_validate).and_return([])
|
164
|
-
|
165
|
-
response = described_class.new(definition)
|
166
|
-
expect(response.validate(fake_response)).to be_empty
|
167
|
-
end
|
168
|
-
end
|
169
|
-
|
170
|
-
context 'when body does not match' do
|
171
|
-
let(:errors) { [double('error1'), double('error2')] }
|
172
|
-
|
173
|
-
it 'returns a list of errors' do
|
174
|
-
JSON::Validator.stub(:fully_validate).and_return(errors)
|
175
|
-
|
176
|
-
response = described_class.new(definition)
|
177
|
-
expect(response.validate(fake_response)).to eq errors
|
178
|
-
end
|
179
|
-
end
|
180
|
-
|
181
|
-
context 'when body not specified' do
|
182
|
-
let(:definition) do
|
183
|
-
{
|
184
|
-
'status' => status,
|
185
|
-
'headers' => headers
|
186
|
-
}
|
187
|
-
end
|
188
|
-
|
189
|
-
it 'does not validate body' do
|
190
|
-
JSON::Validator.should_not_receive(:fully_validate)
|
191
|
-
described_class.new(definition)
|
192
|
-
end
|
193
|
-
|
194
|
-
it 'gives no errors' do
|
195
|
-
response = described_class.new(definition)
|
196
|
-
expect(response.validate(fake_response)).to be_empty
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|
200
|
-
end
|
201
|
-
end
|
@@ -1,168 +0,0 @@
|
|
1
|
-
module Pacto
|
2
|
-
module Stubs
|
3
|
-
describe BuiltIn 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) { double('stubbed request') }
|
29
|
-
let(:processor) { double('processor') }
|
30
|
-
|
31
|
-
describe '#initialize' do
|
32
|
-
it 'sets up a callback' do
|
33
|
-
WebMock.should_receive(:after_request) do | arg, &block |
|
34
|
-
expect(block.parameters).to have(2).items
|
35
|
-
end
|
36
|
-
|
37
|
-
described_class.new
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
describe '#stub_request!' do
|
42
|
-
before do
|
43
|
-
WebMock.should_receive(:stub_request).
|
44
|
-
with(request.method, "#{request.host}#{request.path}").
|
45
|
-
and_return(stubbed_request)
|
46
|
-
|
47
|
-
stubbed_request.stub(:to_return).with({
|
48
|
-
:status => response.status,
|
49
|
-
:headers => response.headers,
|
50
|
-
:body => response.body.to_json
|
51
|
-
})
|
52
|
-
end
|
53
|
-
|
54
|
-
context 'when the response body is an object' do
|
55
|
-
let(:body) do
|
56
|
-
{'message' => 'foo'}
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'stubs the response body with a json representation' do
|
60
|
-
stubbed_request.should_receive(:to_return).with({
|
61
|
-
:status => response.status,
|
62
|
-
:headers => response.headers,
|
63
|
-
:body => response.body.to_json
|
64
|
-
})
|
65
|
-
|
66
|
-
stubbed_request.stub(:with).and_return(stubbed_request)
|
67
|
-
|
68
|
-
described_class.new.stub_request! request, response
|
69
|
-
end
|
70
|
-
end
|
71
|
-
|
72
|
-
context 'when the response body is an array' do
|
73
|
-
let(:body) do
|
74
|
-
[1, 2, 3]
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'stubs the response body with a json representation' do
|
78
|
-
stubbed_request.should_receive(:to_return).with({
|
79
|
-
:status => response.status,
|
80
|
-
:headers => response.headers,
|
81
|
-
:body => response.body.to_json
|
82
|
-
})
|
83
|
-
|
84
|
-
stubbed_request.stub(:with).and_return(stubbed_request)
|
85
|
-
|
86
|
-
described_class.new.stub_request! request, response
|
87
|
-
end
|
88
|
-
end
|
89
|
-
|
90
|
-
context 'when the response body is not an object or an array' do
|
91
|
-
let(:body) { nil }
|
92
|
-
|
93
|
-
it 'stubs the response body with the original body' do
|
94
|
-
stubbed_request.should_receive(:to_return).with({
|
95
|
-
:status => response.status,
|
96
|
-
:headers => response.headers,
|
97
|
-
:body => response.body
|
98
|
-
})
|
99
|
-
|
100
|
-
stubbed_request.stub(:with).and_return(stubbed_request)
|
101
|
-
|
102
|
-
described_class.new.stub_request! request, response
|
103
|
-
end
|
104
|
-
end
|
105
|
-
|
106
|
-
context 'a GET request' do
|
107
|
-
let(:method) { :get }
|
108
|
-
|
109
|
-
it 'uses WebMock to stub the request' do
|
110
|
-
stubbed_request.should_receive(:with).
|
111
|
-
with({:headers => request.headers, :query => request.params}).
|
112
|
-
and_return(stubbed_request)
|
113
|
-
described_class.new.stub_request! request, response
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
context 'a POST request' do
|
118
|
-
let(:method) { :post }
|
119
|
-
|
120
|
-
it 'uses WebMock to stub the request' do
|
121
|
-
stubbed_request.should_receive(:with).
|
122
|
-
with({:headers => request.headers, :body => request.params}).
|
123
|
-
and_return(stubbed_request)
|
124
|
-
described_class.new.stub_request! request, response
|
125
|
-
end
|
126
|
-
end
|
127
|
-
|
128
|
-
context 'a request with no headers' do
|
129
|
-
let(:request) do
|
130
|
-
double({
|
131
|
-
:host => 'http://localhost',
|
132
|
-
:method => :get,
|
133
|
-
:path => '/hello_world',
|
134
|
-
:headers => {},
|
135
|
-
:params => {'foo' => 'bar'}
|
136
|
-
})
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'uses WebMock to stub the request' do
|
140
|
-
stubbed_request.should_receive(:with).
|
141
|
-
with({:query => request.params}).
|
142
|
-
and_return(stubbed_request)
|
143
|
-
described_class.new.stub_request! request, response
|
144
|
-
end
|
145
|
-
end
|
146
|
-
|
147
|
-
context 'a request with no params' do
|
148
|
-
let(:request) do
|
149
|
-
double({
|
150
|
-
:host => 'http://localhost',
|
151
|
-
:method => :get,
|
152
|
-
:path => '/hello_world',
|
153
|
-
:headers => {},
|
154
|
-
:params => {}
|
155
|
-
})
|
156
|
-
end
|
157
|
-
|
158
|
-
it 'uses WebMock to stub the request' do
|
159
|
-
stubbed_request.should_receive(:with).
|
160
|
-
with({}).
|
161
|
-
and_return(stubbed_request)
|
162
|
-
described_class.new.stub_request! request, response
|
163
|
-
end
|
164
|
-
end
|
165
|
-
end
|
166
|
-
end
|
167
|
-
end
|
168
|
-
end
|