pacto 0.2.5 → 0.3.0.pre

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