pacto 0.3.1 → 0.4.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (204) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +3 -0
  3. data/.rubocop.yml +29 -7
  4. data/.travis.yml +8 -1
  5. data/CONTRIBUTING.md +3 -6
  6. data/Gemfile +13 -2
  7. data/Guardfile +4 -4
  8. data/Procfile +1 -0
  9. data/README.md +47 -13
  10. data/Rakefile +66 -19
  11. data/TODO.md +33 -10
  12. data/bin/pacto +4 -0
  13. data/changelog.md +30 -0
  14. data/docs/configuration.md +69 -0
  15. data/docs/consumer.md +18 -0
  16. data/docs/cops.md +39 -0
  17. data/docs/forensics.md +66 -0
  18. data/docs/generation.md +65 -0
  19. data/docs/rake_tasks.md +10 -0
  20. data/docs/rspec.md +0 -0
  21. data/docs/samples.md +133 -0
  22. data/docs/server.md +34 -0
  23. data/docs/server_cli.md +18 -0
  24. data/docs/stenographer.md +20 -0
  25. data/features/configuration/strict_matchers.feature +10 -10
  26. data/features/evolve/existing_services.feature +12 -10
  27. data/features/generate/generation.feature +11 -11
  28. data/features/steps/pacto_steps.rb +17 -12
  29. data/features/stub/templates.feature +4 -4
  30. data/features/support/env.rb +21 -9
  31. data/features/validate/meta_validation.feature +9 -17
  32. data/features/validate/validation.feature +5 -6
  33. data/lib/pacto.rb +41 -33
  34. data/lib/pacto/actor.rb +5 -0
  35. data/lib/pacto/actors/from_examples.rb +67 -0
  36. data/lib/pacto/actors/json_generator.rb +20 -0
  37. data/lib/pacto/cli.rb +75 -0
  38. data/lib/pacto/cli/helpers.rb +20 -0
  39. data/lib/pacto/consumer.rb +80 -0
  40. data/lib/pacto/consumer/faraday_driver.rb +34 -0
  41. data/lib/pacto/contract.rb +48 -20
  42. data/lib/pacto/contract_builder.rb +125 -0
  43. data/lib/pacto/contract_factory.rb +31 -12
  44. data/lib/pacto/contract_files.rb +1 -0
  45. data/lib/pacto/contract_set.rb +12 -0
  46. data/lib/pacto/cops.rb +46 -0
  47. data/lib/pacto/cops/body_cop.rb +23 -0
  48. data/lib/pacto/cops/request_body_cop.rb +10 -0
  49. data/lib/pacto/cops/response_body_cop.rb +10 -0
  50. data/lib/pacto/{validators/response_header_validator.rb → cops/response_header_cop.rb} +9 -15
  51. data/lib/pacto/cops/response_status_cop.rb +18 -0
  52. data/lib/pacto/core/configuration.rb +16 -5
  53. data/lib/pacto/core/contract_registry.rb +13 -32
  54. data/lib/pacto/core/hook.rb +1 -0
  55. data/lib/pacto/core/http_middleware.rb +23 -0
  56. data/lib/pacto/core/investigation_registry.rb +60 -0
  57. data/lib/pacto/core/modes.rb +1 -0
  58. data/lib/pacto/core/pacto_request.rb +59 -0
  59. data/lib/pacto/core/pacto_response.rb +41 -0
  60. data/lib/pacto/dash.rb +9 -0
  61. data/lib/pacto/erb_processor.rb +1 -0
  62. data/lib/pacto/exceptions/invalid_contract.rb +1 -0
  63. data/lib/pacto/extensions.rb +3 -16
  64. data/lib/pacto/forensics/investigation_filter.rb +90 -0
  65. data/lib/pacto/forensics/investigation_matcher.rb +80 -0
  66. data/lib/pacto/generator.rb +31 -53
  67. data/lib/pacto/generator/filters.rb +8 -7
  68. data/lib/pacto/generator/hint.rb +26 -0
  69. data/lib/pacto/generator/native_contract_generator.rb +74 -0
  70. data/lib/pacto/hooks/erb_hook.rb +2 -1
  71. data/lib/pacto/investigation.rb +49 -0
  72. data/lib/pacto/logger.rb +1 -0
  73. data/lib/pacto/meta_schema.rb +12 -6
  74. data/lib/pacto/native_contract_factory.rb +60 -0
  75. data/lib/pacto/observers/stenographer.rb +42 -0
  76. data/lib/pacto/provider.rb +27 -0
  77. data/lib/pacto/rake_task.rb +25 -70
  78. data/lib/pacto/request_clause.rb +31 -29
  79. data/lib/pacto/request_pattern.rb +20 -3
  80. data/lib/pacto/resettable.rb +22 -0
  81. data/lib/pacto/response_clause.rb +5 -12
  82. data/lib/pacto/rspec.rb +38 -31
  83. data/lib/pacto/server.rb +4 -0
  84. data/lib/pacto/stubs/uri_pattern.rb +21 -11
  85. data/lib/pacto/stubs/webmock_adapter.rb +69 -34
  86. data/lib/pacto/swagger_contract_factory.rb +90 -0
  87. data/lib/pacto/test_helper.rb +37 -0
  88. data/lib/pacto/ui.rb +32 -2
  89. data/lib/pacto/uri.rb +2 -1
  90. data/lib/pacto/version.rb +2 -1
  91. data/pacto-server.gemspec +24 -0
  92. data/pacto.gemspec +13 -9
  93. data/resources/contract_schema.json +46 -18
  94. data/resources/draft-04.json +150 -0
  95. data/sample_apis/album/cover_api.rb +12 -0
  96. data/sample_apis/config.ru +25 -0
  97. data/sample_apis/echo_api.rb +26 -0
  98. data/sample_apis/files_api.rb +50 -0
  99. data/sample_apis/hello_api.rb +14 -0
  100. data/sample_apis/ping_api.rb +11 -0
  101. data/sample_apis/reverse_api.rb +20 -0
  102. data/samples/README.md +11 -0
  103. data/samples/Rakefile +2 -0
  104. data/samples/configuration.rb +33 -0
  105. data/samples/consumer.rb +15 -0
  106. data/samples/contracts/README.md +1 -0
  107. data/samples/contracts/contract.js +93 -0
  108. data/samples/contracts/get_album_cover.json +48 -0
  109. data/samples/contracts/localhost/api/echo.json +37 -0
  110. data/samples/contracts/localhost/api/ping.json +38 -0
  111. data/samples/cops.rb +30 -0
  112. data/samples/forensics.rb +54 -0
  113. data/samples/generation.rb +48 -0
  114. data/samples/rake_tasks.sh +7 -0
  115. data/samples/rspec.rb +1 -0
  116. data/samples/samples.rb +92 -0
  117. data/samples/scripts/bootstrap +2 -0
  118. data/samples/scripts/wrapper +11 -0
  119. data/samples/server.rb +24 -0
  120. data/samples/server_cli.sh +12 -0
  121. data/samples/stenographer.rb +17 -0
  122. data/spec/coveralls_helper.rb +1 -0
  123. data/spec/fabricators/contract_fabricator.rb +94 -0
  124. data/spec/fabricators/http_fabricator.rb +48 -0
  125. data/spec/fabricators/webmock_fabricator.rb +24 -0
  126. data/spec/{unit/data → fixtures/contracts}/contract.json +2 -2
  127. data/spec/fixtures/contracts/contract_with_examples.json +58 -0
  128. data/spec/{unit/data → fixtures/contracts}/simple_contract.json +5 -3
  129. data/spec/{integration/data → fixtures/contracts}/strict_contract.json +5 -3
  130. data/spec/{integration/data → fixtures/contracts}/templating_contract.json +3 -2
  131. data/spec/{integration/data/simple_contract.json → fixtures/deprecated_contracts/deprecated_contract.json} +2 -1
  132. data/spec/fixtures/swagger/petstore.yaml +101 -0
  133. data/spec/integration/e2e_spec.rb +19 -20
  134. data/spec/integration/forensics/integration_matcher_spec.rb +90 -0
  135. data/spec/integration/rspec_spec.rb +22 -25
  136. data/spec/integration/templating_spec.rb +7 -6
  137. data/spec/pacto/dummy_server.rb +4 -0
  138. data/spec/pacto/{server → dummy_server}/dummy.rb +7 -6
  139. data/spec/pacto/dummy_server/jruby_workaround_helper.rb +23 -0
  140. data/spec/pacto/{server → dummy_server}/playback_servlet.rb +3 -2
  141. data/spec/spec_helper.rb +16 -7
  142. data/spec/unit/actors/from_examples_spec.rb +70 -0
  143. data/spec/unit/actors/json_generator_spec.rb +105 -0
  144. data/spec/unit/pacto/actor_spec.rb +23 -0
  145. data/spec/unit/pacto/configuration_spec.rb +7 -6
  146. data/spec/unit/pacto/consumer/faraday_driver_spec.rb +40 -0
  147. data/spec/unit/pacto/contract_builder_spec.rb +89 -0
  148. data/spec/unit/pacto/contract_factory_spec.rb +62 -11
  149. data/spec/unit/pacto/contract_files_spec.rb +1 -0
  150. data/spec/unit/pacto/contract_set_spec.rb +36 -0
  151. data/spec/unit/pacto/contract_spec.rb +51 -39
  152. data/spec/unit/pacto/cops/body_cop_spec.rb +107 -0
  153. data/spec/unit/pacto/{validators/response_header_validator_spec.rb → cops/response_header_cop_spec.rb} +30 -19
  154. data/spec/unit/pacto/cops/response_status_cop_spec.rb +26 -0
  155. data/spec/unit/pacto/cops_spec.rb +75 -0
  156. data/spec/unit/pacto/core/configuration_spec.rb +6 -5
  157. data/spec/unit/pacto/core/contract_registry_spec.rb +16 -83
  158. data/spec/unit/pacto/core/http_middleware_spec.rb +36 -0
  159. data/spec/unit/pacto/core/investigation_spec.rb +62 -0
  160. data/spec/unit/pacto/core/modes_spec.rb +5 -4
  161. data/spec/unit/pacto/erb_processor_spec.rb +3 -2
  162. data/spec/unit/pacto/extensions_spec.rb +10 -20
  163. data/spec/unit/pacto/generator/filters_spec.rb +11 -10
  164. data/spec/unit/pacto/generator/native_contract_generator_spec.rb +171 -0
  165. data/spec/unit/{hooks → pacto/hooks}/erb_hook_spec.rb +18 -11
  166. data/spec/unit/pacto/investigation_registry_spec.rb +77 -0
  167. data/spec/unit/pacto/logger_spec.rb +6 -5
  168. data/spec/unit/pacto/meta_schema_spec.rb +5 -4
  169. data/spec/unit/pacto/native_contract_factory_spec.rb +26 -0
  170. data/spec/unit/pacto/pacto_spec.rb +13 -28
  171. data/spec/unit/pacto/request_clause_spec.rb +16 -51
  172. data/spec/unit/pacto/request_pattern_spec.rb +6 -5
  173. data/spec/unit/pacto/response_clause_spec.rb +6 -19
  174. data/spec/unit/pacto/server/playback_servlet_spec.rb +21 -18
  175. data/spec/unit/pacto/stubs/observers/stenographer_spec.rb +33 -0
  176. data/spec/unit/pacto/stubs/uri_pattern_spec.rb +39 -11
  177. data/spec/unit/pacto/stubs/webmock_adapter_spec.rb +67 -117
  178. data/spec/unit/pacto/swagger_contract_factory_spec.rb +56 -0
  179. data/spec/unit/pacto/uri_spec.rb +1 -0
  180. data/tasks/release.rake +57 -0
  181. metadata +247 -76
  182. data/.rubocop-todo.yml +0 -24
  183. data/.ruby-gemset +0 -1
  184. data/.ruby-version +0 -1
  185. data/CHANGELOG +0 -12
  186. data/features/validate/body_only.feature +0 -85
  187. data/lib/pacto/contract_list.rb +0 -17
  188. data/lib/pacto/contract_validator.rb +0 -29
  189. data/lib/pacto/core/validation_registry.rb +0 -40
  190. data/lib/pacto/stubs/webmock_helper.rb +0 -69
  191. data/lib/pacto/validation.rb +0 -54
  192. data/lib/pacto/validators/body_validator.rb +0 -49
  193. data/lib/pacto/validators/request_body_validator.rb +0 -26
  194. data/lib/pacto/validators/response_body_validator.rb +0 -26
  195. data/lib/pacto/validators/response_status_validator.rb +0 -24
  196. data/spec/pacto/server.rb +0 -2
  197. data/spec/unit/pacto/contract_list_spec.rb +0 -35
  198. data/spec/unit/pacto/contract_validator_spec.rb +0 -85
  199. data/spec/unit/pacto/core/validation_registry_spec.rb +0 -76
  200. data/spec/unit/pacto/core/validation_spec.rb +0 -60
  201. data/spec/unit/pacto/generator_spec.rb +0 -132
  202. data/spec/unit/pacto/stubs/webmock_helper_spec.rb +0 -20
  203. data/spec/unit/pacto/validators/body_validator_spec.rb +0 -118
  204. data/spec/unit/pacto/validators/response_status_validator_spec.rb +0 -20
@@ -0,0 +1,33 @@
1
+ # -*- encoding : utf-8 -*-
2
+ require 'spec_helper'
3
+
4
+ module Pacto
5
+ module Observers
6
+ describe Stenographer do
7
+ let(:pacto_request) { Fabricate(:pacto_request) }
8
+ let(:pacto_response) { Fabricate(:pacto_response) }
9
+ let(:contract) { Fabricate(:contract) }
10
+ let(:citations) { %w(one two) }
11
+ let(:investigation) { Pacto::Investigation.new(pacto_request, pacto_response, contract, citations) }
12
+
13
+ subject(:stream) { StringIO.new }
14
+
15
+ subject { described_class.new stream }
16
+
17
+ it 'writes to the stenographer log stream' do
18
+ subject.log_investigation investigation
19
+ expected_log_line = "request #{contract.name.inspect}, values: {}, response: {status: #{pacto_response.status}} # #{citations.size} contract violations\n"
20
+ expect(stream.string).to eq expected_log_line
21
+ end
22
+
23
+ context 'when the stenographer log stream is nil' do
24
+ let(:stream) { nil }
25
+
26
+ it 'does nothing' do
27
+ # Would raise an error if it tried to write to stream
28
+ subject.log_investigation investigation
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -1,27 +1,55 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  module Pacto
4
5
  describe UriPattern do
5
6
  context 'with non-strict matchers' do
6
- it 'returns a regex containing the host and path' do
7
+ before(:each) do
7
8
  Pacto.configuration.strict_matchers = false
8
- request = double(host: 'myhost.com', path: '/stuff')
9
- expect(UriPattern.for(request).to_s).to eq(/myhost\.com\/stuff/.to_s)
10
9
  end
11
10
 
12
- it 'turns segments preceded by : into wildcards' do
13
- Pacto.configuration.strict_matchers = false
14
- request = double(host: 'myhost.com', path: '/:id')
15
- wildcard = '[^\/\?#]+'
16
- expect(UriPattern.for(request).to_s).to eq(/myhost\.com\/#{wildcard}/.to_s)
11
+ it 'appends host if path is an Addressable::Template' do
12
+ path_pattern = '/{account}/data{.format}{?page,per_page}'
13
+ path = Addressable::Template.new path_pattern
14
+ request = Fabricate(:request_clause, host: 'https://www.example.com', path: path)
15
+ expect(UriPattern.for(request).pattern).to include(Addressable::Template.new("https://www.example.com#{path_pattern}").pattern)
16
+ end
17
+
18
+ it 'returns a URITemplate containing the host and path and wildcard vars' do
19
+ request = Fabricate(:request_clause, host: 'myhost.com', path: '/stuff')
20
+ uri_pattern = UriPattern.for(request)
21
+ expect(uri_pattern.pattern).to eql('{scheme}://myhost.com/stuff{?anyvars*}')
22
+ end
23
+
24
+ it 'convers segments preceded by : into variables', :deprecated do
25
+ request = Fabricate(:request_clause, host: 'myhost.com', path: '/:id')
26
+ uri_pattern = UriPattern.for(request)
27
+ expect(uri_pattern.keys).to include('id')
28
+ expect(uri_pattern.pattern).to_not include(':id')
29
+ end
30
+
31
+ it 'creates a regex that does not allow additional path elements' do
32
+ request = Fabricate(:request_clause, host: 'myhost.com', path: '/:id')
33
+ pattern = UriPattern.for(request)
34
+ expect(pattern).to match('http://myhost.com/foo')
35
+ expect(pattern).to_not match('http://myhost.com/foo/bar')
36
+ end
37
+
38
+ it 'creates a regex that does allow query parameters', :deprecated do
39
+ request = Fabricate(:request_clause, host: 'myhost.com', path: '/:id')
40
+ pattern = UriPattern.for(request)
41
+ expect(pattern.match('http://myhost.com/foo?a=b')). to be_truthy
42
+ expect(pattern.match('http://myhost.com/foo?a=b&c=d')).to be_truthy
17
43
  end
18
44
  end
19
45
 
20
- context 'with strict matchers' do
46
+ # Strict/relaxed matching should be done against the full URI or path only
47
+ context 'with strict matchers', deprecated: true do
21
48
  it 'returns a string with the host and path' do
22
49
  Pacto.configuration.strict_matchers = true
23
- request = double(host: 'myhost.com', path: '/stuff')
24
- expect(UriPattern.for(request)).to eq('myhost.com/stuff')
50
+ request = Fabricate(:request_clause, host: 'myhost.com', path: '/stuff')
51
+ uri_pattern = UriPattern.for(request)
52
+ expect(uri_pattern.pattern).to eq('{scheme}://myhost.com/stuff')
25
53
  end
26
54
  end
27
55
  end
@@ -1,89 +1,85 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  module Pacto
2
3
  module Stubs
4
+ # FIXME: Review this test and see which requests are Pacto vs WebMock, then use Fabricate
3
5
  describe WebMockAdapter do
6
+ let(:middleware) { double('middleware') }
7
+
4
8
  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'}
9
+ Fabricate(:request_clause,
10
+ host: 'http://localhost',
11
+ http_method: http_method,
12
+ path: '/hello_world',
13
+ headers: { 'Accept' => 'application/json' },
14
+ params: { 'foo' => 'bar' }
11
15
  )
12
16
  end
13
17
 
14
- let(:method) { :get }
18
+ let(:http_method) { :get }
15
19
 
16
20
  let(:response) do
17
- double(
18
- :status => 200,
19
- :headers => {},
20
- :body => body
21
+ Fabricate(
22
+ :response_clause,
23
+ status: 200,
24
+ headers: {},
25
+ schema: {
26
+ type: 'object',
27
+ required: ['message'],
28
+ properties: {
29
+ message: {
30
+ type: 'string',
31
+ default: 'foo'
32
+ }
33
+ }
34
+ }
21
35
  )
22
36
  end
23
37
 
38
+ let(:contract) do
39
+ Fabricate(:contract, request: request, response: response)
40
+ end
41
+
24
42
  let(:body) do
25
- {'message' => 'foo'}
43
+ { 'message' => 'foo' }
26
44
  end
27
45
 
28
46
  let(:stubbed_request) do
29
47
  {
30
- :path => nil
48
+ path: nil
31
49
  }
32
50
  end
33
51
 
34
52
  let(:request_pattern) { double('request_pattern') }
35
53
 
36
- subject(:built_in) { WebMockAdapter.new }
54
+ subject(:adapter) { WebMockAdapter.new middleware }
37
55
 
38
56
  before(:each) do
39
- stubbed_request.stub(:to_return).with(
40
- :status => response.status,
41
- :headers => response.headers,
42
- :body => response.body.to_json
43
- )
44
- stubbed_request.stub(:request_pattern).and_return request_pattern
57
+ allow(stubbed_request).to receive(:to_return).with(no_args)
58
+ allow(stubbed_request).to receive(:request_pattern).and_return request_pattern
45
59
  end
46
60
 
47
61
  describe '#initialize' do
48
62
  it 'sets up a hook' do
49
- WebMock.should_receive(:after_request) do | arg, &block |
50
- expect(block.parameters).to have(2).items
63
+ expect(WebMock).to receive(:after_request) do | _arg, &block |
64
+ expect(block.parameters.size).to eq(2)
51
65
  end
52
66
 
53
- # WebMockAdapter.new
54
- Pacto.configuration.provider # this way the rpec after block doesn't create a second instance
67
+ Pacto.configuration.adapter # (rather than WebMockAdapter.new, to prevent rpec after block from creating a second instance
55
68
  end
56
69
  end
57
70
 
58
71
  describe '#process_hooks' do
59
72
  let(:request_signature) { double('request_signature') }
60
73
 
61
- before do
62
- Pacto.configuration.hook.stub(:process)
63
- end
64
-
65
- it 'calls the registered hook' do
66
- Pacto.configuration.hook.should_receive(:process)
67
- .with(anything, request_signature, response)
68
- built_in.process_hooks request_signature, response
69
- end
70
-
71
- it 'calls generate when generate is enabled' do
72
- Pacto.generate!
73
- WebMockHelper.should_receive(:generate).with(request_signature, response)
74
- built_in.process_hooks request_signature, response
75
- end
76
-
77
- it 'calls validate when validate mode is enabled' do
78
- Pacto.validate!
79
- WebMockHelper.should_receive(:validate).with(request_signature, response)
80
- built_in.process_hooks request_signature, response
74
+ it 'calls the middleware for processing' do
75
+ expect(middleware).to receive(:process).with(a_kind_of(Pacto::PactoRequest), a_kind_of(Pacto::PactoResponse))
76
+ adapter.process_hooks request_signature, response
81
77
  end
82
78
  end
83
79
 
84
80
  describe '#stub_request!' do
85
81
  before(:each) do
86
- WebMock.should_receive(:stub_request) do | method, url |
82
+ expect(WebMock).to receive(:stub_request) do | _method, url |
87
83
  stubbed_request[:path] = url
88
84
  stubbed_request
89
85
  end
@@ -91,112 +87,66 @@ module Pacto
91
87
 
92
88
  context 'when the response body is an object' do
93
89
  let(:body) do
94
- {'message' => 'foo'}
95
- end
96
-
97
- it 'stubs the response body with a json representation' do
98
- stubbed_request.should_receive(:to_return).with(
99
- :status => response.status,
100
- :headers => response.headers,
101
- :body => response.body.to_json
102
- )
103
-
104
- request_pattern.stub(:with)
105
-
106
- built_in.stub_request! request, response
107
- end
108
-
109
- context 'when the response body is an array' do
110
- let(:body) do
111
- [1, 2, 3]
112
- end
113
-
114
- it 'stubs the response body with a json representation' do
115
- stubbed_request.should_receive(:to_return).with(
116
- :status => response.status,
117
- :headers => response.headers,
118
- :body => response.body.to_json
119
- )
120
-
121
- request_pattern.stub(:with)
122
-
123
- built_in.stub_request! request, response
124
- end
125
- end
126
-
127
- context 'when the response body is not an object or an array' do
128
- let(:body) { nil }
129
-
130
- it 'stubs the response body with the original body' do
131
- stubbed_request.should_receive(:to_return).with(
132
- :status => response.status,
133
- :headers => response.headers,
134
- :body => response.body
135
- )
136
-
137
- request_pattern.stub(:with)
138
-
139
- built_in.stub_request! request, response
140
- end
90
+ { 'message' => 'foo' }
141
91
  end
142
92
 
143
93
  context 'a GET request' do
144
- let(:method) { :get }
94
+ let(:http_method) { :get }
145
95
 
146
96
  it 'uses WebMock to stub the request' do
147
- request_pattern.should_receive(:with).
148
- with(:headers => request.headers, :query => request.params).
97
+ expect(request_pattern).to receive(:with).
98
+ with(headers: request.headers, query: request.params).
149
99
  and_return(stubbed_request)
150
- built_in.stub_request! request, response
100
+ adapter.stub_request! contract
151
101
  end
152
102
  end
153
103
 
154
104
  context 'a POST request' do
155
- let(:method) { :post }
105
+ let(:http_method) { :post }
156
106
 
157
107
  it 'uses WebMock to stub the request' do
158
- request_pattern.should_receive(:with).
159
- with(:headers => request.headers, :body => request.params).
108
+ expect(request_pattern).to receive(:with).
109
+ with(headers: request.headers, body: request.params).
160
110
  and_return(stubbed_request)
161
- built_in.stub_request! request, response
111
+ adapter.stub_request! contract
162
112
  end
163
113
  end
164
114
 
165
115
  context 'a request with no headers' do
166
116
  let(:request) do
167
- double(
168
- :host => 'http://localhost',
169
- :method => :get,
170
- :path => '/hello_world',
171
- :headers => {},
172
- :params => {'foo' => 'bar'}
117
+ Fabricate(:request_clause,
118
+ host: 'http://localhost',
119
+ http_method: :get,
120
+ path: '/hello_world',
121
+ headers: {},
122
+ params: { 'foo' => 'bar' }
173
123
  )
174
124
  end
175
125
 
176
126
  it 'uses WebMock to stub the request' do
177
- request_pattern.should_receive(:with).
178
- with(:query => request.params).
127
+ expect(request_pattern).to receive(:with).
128
+ with(query: request.params).
179
129
  and_return(stubbed_request)
180
- built_in.stub_request! request, response
130
+ adapter.stub_request! contract
181
131
  end
182
132
  end
183
133
 
184
134
  context 'a request with no params' do
185
135
  let(:request) do
186
- double(
187
- :host => 'http://localhost',
188
- :method => :get,
189
- :path => '/hello_world',
190
- :headers => {},
191
- :params => {}
136
+ Fabricate(:request_clause,
137
+ host: 'http://localhost',
138
+ http_method: :get,
139
+ path: '/hello_world',
140
+ headers: {},
141
+ params: {}
192
142
  )
193
143
  end
194
144
 
195
145
  it 'uses WebMock to stub the request' do
196
- request_pattern.should_receive(:with).
146
+ expect(request_pattern).to receive(:with).
197
147
  with({}).
198
148
  and_return(stubbed_request)
199
- built_in.stub_request! request, response
149
+ adapter.stub_request! contract
200
150
  end
201
151
  end
202
152
  end
@@ -0,0 +1,56 @@
1
+ # -*- encoding : utf-8 -*-
2
+ module Pacto
3
+ module Generator
4
+ describe SwaggerContractFactory do
5
+ let(:swagger_file) { 'spec/fixtures/swagger/petstore.yaml' }
6
+ let(:expected_schema) do
7
+ {
8
+ 'type' => 'array',
9
+ 'items' => {
10
+ 'required' => %w(id name),
11
+ 'properties' => {
12
+ 'id' => { 'type' => 'integer', 'format' => 'int64' },
13
+ 'name' => { 'type' => 'string' },
14
+ 'tag' => { 'type' => 'string' }
15
+ }
16
+ }
17
+ }
18
+ end
19
+ describe '#load_hints' do
20
+ it 'loads hints from Swagger' do
21
+ hints = subject.load_hints(swagger_file)
22
+ expect(hints.size).to eq(3) # number of API operations
23
+ hints.each do | hint |
24
+ expect(hint).to be_a_kind_of(Pacto::Generator::Hint)
25
+ expect(hint.host).to eq('petstore.swagger.wordnik.com')
26
+ expect([:get, :post]).to include(hint.http_method)
27
+ expect(hint.path).to match(/\/pets/)
28
+ end
29
+ end
30
+ end
31
+
32
+ describe '#build_from_file' do
33
+ it 'loads Contracts from Swagger' do
34
+ contracts = subject.build_from_file(swagger_file)
35
+ expect(contracts.size).to eq(3) # number of API operations
36
+ contracts.each do | contract |
37
+ expect(contract).to be_a_kind_of(Pacto::Contract)
38
+
39
+ request_clause = contract.request
40
+ expect(request_clause.host).to eq('petstore.swagger.wordnik.com')
41
+ expect([:get, :post]).to include(request_clause.http_method)
42
+ expect(request_clause.path).to match(/\/pets/)
43
+
44
+ response_clause = contract.response
45
+ if request_clause.http_method == :get
46
+ expect(response_clause.status).to eq(200)
47
+ else
48
+ expect(response_clause.status).to eq(201)
49
+ end
50
+ expect(response_clause.schema).to eq(expected_schema) if response_clause.status == 200
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,3 +1,4 @@
1
+ # -*- encoding : utf-8 -*-
1
2
  require 'spec_helper'
2
3
 
3
4
  module Pacto
@@ -0,0 +1,57 @@
1
+ require 'octokit'
2
+
3
+ def github
4
+ @client ||= Octokit::Client.new :access_token => ENV['GITHUB_TOKEN']
5
+ end
6
+
7
+ def release_tag
8
+ "v#{Pacto::VERSION}"
9
+ end
10
+
11
+ def release
12
+ @release ||= github.list_releases('thoughtworks/pacto').find{|r| r.name == release_tag }
13
+ end
14
+
15
+ def changelog
16
+ changelog = File.read('changelog.md').split("\n\n\n", 2).first
17
+ confirm 'Does the CHANGELOG look correct? ', changelog
18
+ end
19
+
20
+ def confirm(question, data)
21
+ puts 'Please confirm...'
22
+ puts data
23
+ print question
24
+ abort 'Aborted' unless $stdin.gets.strip == 'y'
25
+ puts 'Confirmed'
26
+ data
27
+ end
28
+
29
+ desc 'Tags and pushes the gem'
30
+ task :release_gem do
31
+ sh 'git', 'tag', '-m', changelog, "v#{Pacto::VERSION}"
32
+ sh 'git push origin master'
33
+ sh "git push origin v#{Pacto::VERSION}"
34
+ sh 'ls pkg/*.gem | xargs -n 1 gem push'
35
+ end
36
+
37
+ desc 'Releases to RubyGems and GitHub'
38
+ task :release => [:build, :release_gem, :documentation, :package, :create_release, :upload_docs]
39
+
40
+ desc 'Preview the changelog'
41
+ task :changelog do
42
+ changelog
43
+ end
44
+
45
+ desc 'Create a release on GitHub'
46
+ task :create_release do
47
+ github.create_release 'thoughtworks/pacto', release_tag, {:name => release_tag, :body => changelog}
48
+ end
49
+
50
+ desc 'Upload docs to the GitHub release'
51
+ task :upload_docs do
52
+ Dir['pkg/pacto_docs*'].each do |file|
53
+ next if File.directory? file
54
+ puts "Uploading #{file}"
55
+ github.upload_asset release.url, file
56
+ end
57
+ end