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.
- 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
|