pacto 0.2.5 → 0.3.0.pre

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 (70) hide show
  1. data/.gitignore +3 -0
  2. data/.rspec +0 -2
  3. data/.rubocop-todo.yml +51 -0
  4. data/.rubocop.yml +1 -0
  5. data/.travis.yml +4 -2
  6. data/Guardfile +28 -14
  7. data/README.md +81 -51
  8. data/Rakefile +24 -11
  9. data/features/generation/generation.feature +25 -0
  10. data/features/journeys/validation.feature +74 -0
  11. data/features/support/env.rb +16 -0
  12. data/lib/pacto.rb +63 -34
  13. data/lib/pacto/contract.rb +25 -11
  14. data/lib/pacto/contract_factory.rb +13 -44
  15. data/lib/pacto/core/callback.rb +11 -0
  16. data/lib/pacto/core/configuration.rb +34 -0
  17. data/lib/pacto/core/contract_repository.rb +44 -0
  18. data/lib/pacto/erb_processor.rb +18 -0
  19. data/lib/pacto/exceptions/invalid_contract.rb +10 -1
  20. data/lib/pacto/extensions.rb +2 -2
  21. data/lib/pacto/generator.rb +75 -0
  22. data/lib/pacto/hash_merge_processor.rb +14 -0
  23. data/lib/pacto/hooks/erb_hook.rb +17 -0
  24. data/lib/pacto/logger.rb +42 -0
  25. data/lib/pacto/meta_schema.rb +17 -0
  26. data/lib/pacto/rake_task.rb +75 -12
  27. data/lib/pacto/request.rb +3 -4
  28. data/lib/pacto/response.rb +27 -19
  29. data/lib/pacto/server.rb +2 -0
  30. data/lib/pacto/server/dummy.rb +45 -0
  31. data/lib/pacto/server/playback_servlet.rb +21 -0
  32. data/lib/pacto/stubs/built_in.rb +57 -0
  33. data/lib/pacto/version.rb +1 -1
  34. data/pacto.gemspec +8 -2
  35. data/resources/contract_schema.json +216 -0
  36. data/spec/coveralls_helper.rb +10 -0
  37. data/spec/integration/data/strict_contract.json +33 -0
  38. data/spec/integration/data/templating_contract.json +25 -0
  39. data/spec/integration/e2e_spec.rb +40 -7
  40. data/spec/integration/templating_spec.rb +55 -0
  41. data/spec/spec_helper.rb +17 -0
  42. data/spec/unit/data/simple_contract.json +22 -0
  43. data/spec/unit/hooks/erb_hook_spec.rb +51 -0
  44. data/spec/unit/pacto/configuration_spec.rb +51 -0
  45. data/spec/unit/pacto/contract_factory_spec.rb +4 -35
  46. data/spec/unit/pacto/contract_spec.rb +59 -31
  47. data/spec/unit/pacto/core/configuration_spec.rb +28 -0
  48. data/spec/unit/pacto/core/contract_repository_spec.rb +133 -0
  49. data/spec/unit/pacto/erb_processor_spec.rb +23 -0
  50. data/spec/unit/pacto/extensions_spec.rb +11 -11
  51. data/spec/unit/pacto/generator_spec.rb +142 -0
  52. data/spec/unit/pacto/hash_merge_processor_spec.rb +20 -0
  53. data/spec/unit/pacto/logger_spec.rb +44 -0
  54. data/spec/unit/pacto/meta_schema_spec.rb +70 -0
  55. data/spec/unit/pacto/pacto_spec.rb +32 -58
  56. data/spec/unit/pacto/request_spec.rb +83 -34
  57. data/spec/unit/pacto/response_adapter_spec.rb +9 -11
  58. data/spec/unit/pacto/response_spec.rb +68 -68
  59. data/spec/unit/pacto/server/playback_servlet_spec.rb +24 -0
  60. data/spec/unit/pacto/stubs/built_in_spec.rb +168 -0
  61. metadata +291 -147
  62. data/.rspec_integration +0 -4
  63. data/.rspec_unit +0 -4
  64. data/lib/pacto/file_pre_processor.rb +0 -12
  65. data/lib/pacto/instantiated_contract.rb +0 -62
  66. data/spec/integration/spec_helper.rb +0 -1
  67. data/spec/integration/utils/dummy_server.rb +0 -34
  68. data/spec/unit/pacto/file_pre_processor_spec.rb +0 -13
  69. data/spec/unit/pacto/instantiated_contract_spec.rb +0 -224
  70. data/spec/unit/spec_helper.rb +0 -5
@@ -1,12 +1,13 @@
1
1
  module Pacto
2
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
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
+ let(:params_as_json) { "{\"foo\":\"bar\"}" }
9
+ let(:absolute_uri) { "#{host}#{path}" }
10
+ subject(:request) do
10
11
  described_class.new(host, {
11
12
  'method' => method,
12
13
  'path' => path,
@@ -14,58 +15,106 @@ module Pacto
14
15
  'params' => params
15
16
  })
16
17
  end
17
- subject { request }
18
18
 
19
- its(:method) { should == :get }
20
- its(:path) { should == path }
21
- its(:headers) { should == headers }
22
- its(:params) { should == params }
19
+ it 'has a host' do
20
+ expect(request.host).to eq host
21
+ end
22
+
23
+ describe '#method' do
24
+ it 'delegates to definition' do
25
+ expect(request.method).to eq :get
26
+ end
27
+
28
+ it 'downcases the method' do
29
+ expect(request.method).to eq request.method.downcase
30
+ end
31
+
32
+ it 'returns a symbol' do
33
+ expect(request.method).to be_kind_of Symbol
34
+ end
35
+ end
36
+
37
+ describe '#path' do
38
+ it 'delegates to definition' do
39
+ expect(request.path).to eq path
40
+ end
41
+ end
42
+
43
+ describe '#headers' do
44
+ it 'delegates to definition' do
45
+ expect(request.headers).to eq headers
46
+ end
47
+ end
48
+
49
+ describe '#params' do
50
+ it 'delegates to definition' do
51
+ expect(request.params).to eq params
52
+ end
53
+ end
23
54
 
24
55
  describe '#execute' do
25
- let(:connection) { double('connection') }
26
- let(:response) { double('response') }
27
- let(:adapted_response) { double('adapted response') }
56
+ let(:connection) { double 'connection' }
57
+ let(:response) { double 'response' }
58
+ let(:adapted_response) { double 'adapted response' }
59
+
60
+ before do
61
+ HTTParty.stub(:get => response)
62
+ HTTParty.stub(:post => response)
63
+ ResponseAdapter.stub(:new => adapted_response)
64
+ end
65
+
66
+ 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
76
+ end
77
+ end
28
78
 
29
79
  context 'for a GET request' do
30
- it 'should make a GET request and return the response' do
80
+ it 'makes the request thru the http client' do
31
81
  HTTParty.should_receive(:get).
32
- with(host + path, {:query => params, :headers => headers}).
82
+ with(absolute_uri, {:query => params, :headers => headers}).
33
83
  and_return(response)
34
- ResponseAdapter.should_receive(:new).with(response).and_return(adapted_response)
35
- request.execute.should == adapted_response
84
+ request.execute
36
85
  end
37
86
  end
38
87
 
39
88
  context 'for a POST request' do
40
- let(:method) { 'POST' }
89
+ let(:method) { 'POST' }
41
90
 
42
- it 'should make a POST request and return the response' do
91
+ it 'makes the request thru the http client' do
43
92
  HTTParty.should_receive(:post).
44
- with(host + path, {:body => params.to_json, :headers => headers}).
93
+ with(absolute_uri, {:body => params_as_json, :headers => headers}).
45
94
  and_return(response)
46
- ResponseAdapter.should_receive(:new).with(response).and_return(adapted_response)
47
- request.execute.should == adapted_response
95
+ request.execute
48
96
  end
49
97
  end
50
98
  end
51
99
 
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"
100
+ describe '#absolute_uri' do
101
+ it 'returns the host followed by the path' do
102
+ expect(request.absolute_uri).to eq absolute_uri
55
103
  end
56
104
  end
57
105
 
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"
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'
62
110
  end
63
111
  end
64
112
 
65
- context "when the query (params) does not exists" do
113
+ context 'when the query does not have a query' do
66
114
  let(:params) { {} }
67
- it "should be equal to the host followed by the path" do
68
- request.full_uri.should == "http://localhost/hello_world"
115
+
116
+ it 'returns the host followed by the path' do
117
+ expect(request.absolute_uri).to eq 'http://localhost/hello_world'
69
118
  end
70
119
  end
71
120
  end
@@ -1,27 +1,25 @@
1
1
  module Pacto
2
2
  describe ResponseAdapter do
3
3
  let(:response) do
4
- double({
4
+ double(
5
5
  :code => 200,
6
6
  :headers => {'foo' => ['bar', 'baz'], 'hello' => ['world']},
7
7
  :body => double('body')
8
- })
8
+ )
9
9
  end
10
10
 
11
- before do
12
- @response_adapter = described_class.new(response)
13
- end
11
+ subject(:response_adapter) { described_class.new response }
14
12
 
15
- it 'should have a status' do
16
- @response_adapter.status.should == response.code
13
+ it 'has a status' do
14
+ expect(response_adapter.status).to eq response.code
17
15
  end
18
16
 
19
- it 'should have a body' do
20
- @response_adapter.body.should == response.body
17
+ it 'has a body' do
18
+ expect(response_adapter.body).to eq response.body
21
19
  end
22
20
 
23
- it 'should normalize headers values according to RFC2616' do
24
- @response_adapter.headers.should == {'foo' => 'bar,baz', 'hello' => 'world'}
21
+ it 'normalizes headers values according to RFC2616' do
22
+ expect(response_adapter.headers).to eq({'foo' => 'bar,baz', 'hello' => 'world'})
25
23
  end
26
24
  end
27
25
  end
@@ -1,7 +1,7 @@
1
1
  module Pacto
2
- describe Response do
2
+ describe Response do
3
3
  let(:body_definition) do
4
- {:type => "object", :required => true, :properties => double("body definition properties")}
4
+ {:type => 'object', :required => true, :properties => double('body definition properties')}
5
5
  end
6
6
  let(:definition) do
7
7
  {
@@ -11,20 +11,20 @@ module Pacto
11
11
  }
12
12
  end
13
13
 
14
- describe '#instantiate' do
15
- let(:generated_body) { double('generated body') }
14
+ describe '#instantiate' do
15
+ let(:generated_body) { double('generated body') }
16
16
 
17
- it 'should instantiate 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)
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
21
 
22
- response = described_class.new(definition).instantiate
23
- response.status.should == definition['status']
24
- response.headers.should == definition['headers']
25
- response.body.should == generated_body
26
- end
27
- end
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
28
 
29
29
  describe '#validate' do
30
30
  let(:status) { 200 }
@@ -39,142 +39,142 @@ module Pacto
39
39
  end
40
40
 
41
41
  context 'when status, headers and body match' do
42
- it 'should not return any errors' do
42
+ it 'does not return any errors' do
43
43
  JSON::Validator.should_receive(:fully_validate).
44
- with(definition['body'], fake_response.body).
44
+ with(definition['body'], fake_response.body, :version => :draft3).
45
45
  and_return([])
46
46
 
47
47
  response = described_class.new(definition)
48
- response.validate(fake_response).should == []
48
+ expect(response.validate(fake_response)).to be_empty
49
49
  end
50
50
  end
51
-
51
+
52
52
  context 'when body is a pure string and matches the description' do
53
53
  let(:string_required) { true }
54
54
  let(:body_definition) do
55
55
  { 'type' => 'string', 'required' => string_required }
56
56
  end
57
- let(:response_body) { "a simple string" }
58
-
59
- it 'should not validate using JSON Schema' do
57
+ let(:response_body) { 'a simple string' }
58
+
59
+ it 'does not validate using JSON Schema' do
60
60
  response = described_class.new(definition)
61
-
61
+
62
62
  JSON::Validator.should_not_receive(:fully_validate)
63
63
  response.validate(fake_response)
64
64
  end
65
-
65
+
66
66
  context 'if required' do
67
- it 'should not return an error when body is a string' do
67
+ it 'does not return an error when body is a string' do
68
68
  response = described_class.new(definition)
69
-
70
- response.validate(fake_response).should == []
69
+
70
+ expect(response.validate(fake_response)).to be_empty
71
71
  end
72
-
73
- it 'should return an error when body is nil' do
72
+
73
+ it 'returns an error when body is nil' do
74
74
  response = described_class.new(definition)
75
-
75
+
76
76
  fake_response.stub(:body).and_return(nil)
77
- response.validate(fake_response).size.should == 1
77
+ expect(response.validate(fake_response).size).to eq 1
78
78
  end
79
79
  end
80
-
80
+
81
81
  context 'if not required' do
82
82
  let(:string_required) { false }
83
-
84
- it 'should not return an error when body is a string' do
83
+
84
+ it 'does not return an error when body is a string' do
85
85
  response = described_class.new(definition)
86
-
87
- response.validate(fake_response).should == []
86
+
87
+ expect(response.validate(fake_response)).to be_empty
88
88
  end
89
-
90
- it 'should not return an error when body is nil' do
89
+
90
+ it 'does not return an error when body is nil' do
91
91
  response = described_class.new(definition)
92
-
92
+
93
93
  fake_response.stub(:body).and_return(nil)
94
- response.validate(fake_response).should == []
94
+ expect(response.validate(fake_response)).to be_empty
95
95
  end
96
96
  end
97
-
97
+
98
98
  context 'if contains pattern' do
99
99
  let(:body_definition) do
100
100
  { 'type' => 'string', 'required' => string_required, 'pattern' => 'a.c' }
101
101
  end
102
-
102
+
103
103
  context 'body matches pattern' do
104
104
  let(:response_body) { 'cabcd' }
105
-
106
- it "should not return an error" do
105
+
106
+ it 'does not return an error' do
107
107
  response = described_class.new(definition)
108
-
109
- response.validate(fake_response).should == []
108
+
109
+ expect(response.validate(fake_response)).to be_empty
110
110
  end
111
111
  end
112
-
112
+
113
113
  context 'body does not match pattern' do
114
114
  let(:response_body) { 'cabscd' }
115
-
116
- it "should return an error" do
115
+
116
+ it 'returns an error' do
117
117
  response = described_class.new(definition)
118
-
119
- response.validate(fake_response).size.should == 1
118
+
119
+ expect(response.validate(fake_response).size).to eq 1
120
120
  end
121
121
  end
122
-
122
+
123
123
  end
124
124
  end
125
-
125
+
126
126
  context 'when status does not match' do
127
127
  let(:status) { 500 }
128
128
 
129
- it 'should return a status error' do
129
+ it 'returns a status error' do
130
130
  JSON::Validator.should_not_receive(:fully_validate)
131
-
131
+
132
132
  response = described_class.new(definition)
133
- response.validate(fake_response).should == ["Invalid status: expected #{definition['status']} but got #{status}"]
133
+ expect(response.validate(fake_response)).to eq ["Invalid status: expected #{definition['status']} but got #{status}"]
134
134
  end
135
135
  end
136
136
 
137
137
  context 'when headers do not match' do
138
138
  let(:headers) { {'Content-Type' => 'text/html'} }
139
139
 
140
- it 'should return a header error' do
140
+ it 'returns a header error' do
141
141
  JSON::Validator.should_not_receive(:fully_validate)
142
142
 
143
143
  response = described_class.new(definition)
144
- response.validate(fake_response).should == ["Invalid headers: expected #{definition['headers'].inspect} to be a subset of #{headers.inspect}"]
144
+ expect(response.validate(fake_response)).to eq ["Invalid headers: expected #{definition['headers'].inspect} to be a subset of #{headers.inspect}"]
145
145
  end
146
146
  end
147
147
 
148
148
  context 'when headers are a subset of expected headers' do
149
149
  let(:headers) { {'Content-Type' => 'application/json'} }
150
150
 
151
- it 'should not return any errors' do
151
+ it 'does not return any errors' do
152
152
  JSON::Validator.stub(:fully_validate).and_return([])
153
153
 
154
154
  response = described_class.new(definition)
155
- response.validate(fake_response).should == []
155
+ expect(response.validate(fake_response)).to be_empty
156
156
  end
157
157
  end
158
158
 
159
159
  context 'when headers values match but keys have different case' do
160
160
  let(:headers) { {'content-type' => 'application/json'} }
161
161
 
162
- it 'should not return any errors' do
162
+ it 'does not return any errors' do
163
163
  JSON::Validator.stub(:fully_validate).and_return([])
164
164
 
165
165
  response = described_class.new(definition)
166
- response.validate(fake_response).should == []
166
+ expect(response.validate(fake_response)).to be_empty
167
167
  end
168
168
  end
169
169
 
170
170
  context 'when body does not match' do
171
171
  let(:errors) { [double('error1'), double('error2')] }
172
172
 
173
- it 'should return a list of errors' do
173
+ it 'returns a list of errors' do
174
174
  JSON::Validator.stub(:fully_validate).and_return(errors)
175
175
 
176
176
  response = described_class.new(definition)
177
- response.validate(fake_response).should == errors
177
+ expect(response.validate(fake_response)).to eq errors
178
178
  end
179
179
  end
180
180
 
@@ -186,16 +186,16 @@ module Pacto
186
186
  }
187
187
  end
188
188
 
189
- it 'should not validate body' do
189
+ it 'does not validate body' do
190
190
  JSON::Validator.should_not_receive(:fully_validate)
191
- response = described_class.new(definition)
191
+ described_class.new(definition)
192
192
  end
193
193
 
194
- it 'should give no errors' do
194
+ it 'gives no errors' do
195
195
  response = described_class.new(definition)
196
- response.validate(fake_response).should == []
196
+ expect(response.validate(fake_response)).to be_empty
197
197
  end
198
198
  end
199
199
  end
200
- end
200
+ end
201
201
  end
@@ -0,0 +1,24 @@
1
+ module Pacto::Server
2
+ describe PlaybackServlet do
3
+ let(:request) { double }
4
+ let(:response) { double('response', :status= => '', :[]= => '', :body= => '') }
5
+
6
+ it 'alters response data with recorded status' do
7
+ servlet = PlaybackServlet.new status: 200
8
+ servlet.do_GET(request, response)
9
+ expect(response).to have_received(:status=).with(200)
10
+ end
11
+
12
+ it 'alters reponse data with recorded headers' do
13
+ servlet = PlaybackServlet.new headers: {'Content-Type' => 'application/json'}
14
+ servlet.do_GET(request, response)
15
+ expect(response).to have_received(:[]=).with('Content-Type', 'application/json')
16
+ end
17
+
18
+ it 'alters reponse data with recorded ' do
19
+ servlet = PlaybackServlet.new body: 'recorded'
20
+ servlet.do_GET(request, response)
21
+ expect(response).to have_received(:body=).with('recorded')
22
+ end
23
+ end
24
+ end