pacto 0.2.1 → 0.2.2

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.
data/.rspec CHANGED
@@ -1,4 +1,4 @@
1
1
  --colour
2
2
  --require spec_helper
3
- --pattern spec/unit/**/*_spec.rb
3
+ --require integration/spec_helper
4
4
  --require unit/spec_helper
data/.rspec_integration CHANGED
@@ -1,4 +1,4 @@
1
1
  --colour
2
2
  --require spec_helper
3
- --pattern spec/integration/**/*_spec.rb
4
3
  --require integration/spec_helper
4
+ --pattern spec/integration/**/*_spec.rb
data/.rspec_unit ADDED
@@ -0,0 +1,4 @@
1
+ --colour
2
+ --require spec_helper
3
+ --require unit/spec_helper
4
+ --pattern spec/unit/**/*_spec.rb
data/.travis.yml CHANGED
@@ -1,3 +1,5 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
+ env:
5
+ - CI=true
data/Guardfile CHANGED
@@ -1,8 +1,16 @@
1
- guard 'rspec', :cli => '--color --require spec_helper', :version => 2 do
2
- watch(%r{^spec/pacto/.+_spec\.rb$})
3
- watch(%r{^spec/json/.+_spec\.rb$})
4
- watch(%r{^lib/pacto\.rb$}) { |m| "spec" }
5
- watch(%r{^lib/json-generator\.rb$}) { |m| "spec" }
6
- watch(%r{^lib/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
7
- watch('spec/spec_helper.rb') { "spec" }
1
+ guard :rspec do
2
+ # Unit tests
3
+ watch(%r{^spec/unit/.+_spec\.rb$})
4
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/unit/#{m[1]}_spec.rb" }
5
+ watch('spec/spec_helper.rb') { "spec/unit" }
6
+ watch('spec/unit/spec_helper.rb') { "spec/unit" }
7
+ watch(%r{^spec/unit/data/.+\.json$}) { "spec/unit" }
8
+
9
+ # Integration tests
10
+ watch(%r{^spec/integration/.+_spec\.rb$})
11
+ watch(%r{^spec/integration/utils/.+\.rb$}) { "spec/integration" }
12
+ watch(%r{^lib/.+.rb$}) { "spec/integration" }
13
+ watch('spec/spec_helper.rb') { "spec/integration" }
14
+ watch('spec/integration/spec_helper.rb') { "spec/integration" }
15
+ watch(%r{^spec/integration/data/.+\.json$}) { "spec/integration" }
8
16
  end
data/Rakefile CHANGED
@@ -3,8 +3,8 @@ require 'rspec/core/rake_task'
3
3
 
4
4
  if defined?(RSpec)
5
5
  desc "Run unit tests"
6
- task :spec do
7
- abort unless system('rspec --option .rspec')
6
+ task :unit do
7
+ abort unless system('rspec --option .rspec_unit')
8
8
  end
9
9
 
10
10
  desc "Run integration tests"
@@ -12,5 +12,5 @@ if defined?(RSpec)
12
12
  abort unless system('rspec --option .rspec_integration')
13
13
  end
14
14
 
15
- task :default => [:spec, :integration]
15
+ task :default => [:unit, :integration]
16
16
  end
data/lib/pacto.rb CHANGED
@@ -2,7 +2,7 @@ require "pacto/version"
2
2
 
3
3
  require "httparty"
4
4
  require "hash_deep_merge"
5
- require "json"
5
+ require "yajl/json_gem"
6
6
  require "json-schema"
7
7
  require "json-generator"
8
8
  require "webmock"
@@ -22,10 +22,34 @@ module Pacto
22
22
  end
23
23
 
24
24
  if @definition['body']
25
- JSON::Validator.fully_validate(@definition['body'], response.body)
25
+ if @definition['body']['type'] && @definition['body']['type'] == 'string'
26
+ validate_as_pure_string response.body
27
+ else
28
+ validate_as_json response.body
29
+ end
26
30
  else
27
31
  []
28
32
  end
29
33
  end
34
+
35
+ private
36
+
37
+ def validate_as_pure_string response_body
38
+ errors = []
39
+ if @definition['body']['required'] && response_body.nil?
40
+ errors << "The response does not contain a body"
41
+ end
42
+
43
+ pattern = @definition['body']['pattern']
44
+ if pattern && !(response_body =~ Regexp.new(pattern))
45
+ errors << "The response does not match the pattern #{pattern}"
46
+ end
47
+
48
+ errors
49
+ end
50
+
51
+ def validate_as_json response_body
52
+ JSON::Validator.fully_validate(@definition['body'], response_body)
53
+ end
30
54
  end
31
55
  end
data/lib/pacto/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Pacto
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.2"
3
3
  end
data/pacto.gemspec CHANGED
@@ -13,14 +13,13 @@ Gem::Specification.new do |gem|
13
13
  gem.homepage = 'https://github.com/thoughtworks/pacto'
14
14
  gem.license = 'MIT'
15
15
 
16
-
17
16
  gem.files = `git ls-files`.split($/)
18
17
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
19
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
20
19
  gem.require_paths = ["lib"]
21
20
 
22
21
  gem.add_dependency "webmock"
23
- gem.add_dependency "json"
22
+ gem.add_dependency "yajl-ruby"
24
23
  gem.add_dependency "json-schema", "1.0.4"
25
24
  gem.add_dependency "json-generator"
26
25
  gem.add_dependency "hash-deep-merge"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "request": {
3
3
  "method": "GET",
4
- "path": "/simple_contract.json",
4
+ "path": "/hello",
5
5
  "headers": {
6
6
  "Accept": "application/json"
7
7
  },
@@ -10,15 +10,12 @@
10
10
 
11
11
  "response": {
12
12
  "status": 200,
13
- "headers": {
14
- "Content-Type": "application/json"
15
- },
13
+ "headers": { "Content-Type": "application/json" },
16
14
  "body": {
17
15
  "type": "object",
16
+ "required": true,
18
17
  "properties": {
19
- "message": {
20
- "type": "string"
21
- }
18
+ "message": { "type": "string", "required": true }
22
19
  }
23
20
  }
24
21
  }
@@ -1,26 +1,38 @@
1
- describe "Pacto" do
1
+ describe 'Pacto' do
2
+ let(:contract_path) { 'spec/integration/data/simple_contract.json' }
3
+
2
4
  before :all do
3
- @server = DummyServer.new
4
- @server.start
5
+ WebMock.allow_net_connect!
5
6
  end
6
7
 
7
- after :all do
8
- @server.terminate
9
- end
8
+ context 'Contract validation' do
9
+ before :all do
10
+ @server = DummyServer.new 8000, '/hello', '{"message": "Hello World!"}'
11
+ @server.start
12
+ end
10
13
 
11
- let(:contract_path) { 'spec/integration/data/simple_contract.json' }
12
- let(:end_point_address) { 'http://localhost:8000' }
14
+ after :all do
15
+ @server.terminate
16
+ end
13
17
 
14
- it "validates a contract against a server" do
15
- WebMock.allow_net_connect!
16
- contract = Pacto.build_from_file(contract_path, end_point_address)
17
- contract.validate.should == []
18
+ it 'verifies the contract against a producer' do
19
+ contract = Pacto.build_from_file(contract_path, 'http://localhost:8000')
20
+ contract.validate.should == []
21
+ end
18
22
  end
19
23
 
20
- pending "generates a mocked response based on a contract specification" do
21
- contract = Pacto.build_from_file(contract_path, end_point_address)
22
- Pacto.register('my_contract', contract)
23
- Pacto.use('my_contract')
24
+ context 'Stub generation' do
25
+ it 'generates a stub to be used by a consumer' do
26
+ contract = Pacto.build_from_file(contract_path, 'http://dummyprovider.com')
27
+ Pacto.register('my_contract', contract)
28
+ Pacto.use('my_contract')
29
+ response.keys.should == ['message']
30
+ response['message'].should be_kind_of(String)
31
+ end
24
32
 
33
+ let :response do
34
+ raw_response = HTTParty.get('http://dummyprovider.com/hello', headers: {'Accept' => 'application/json' })
35
+ JSON.parse(raw_response.body)
36
+ end
25
37
  end
26
38
  end
@@ -14,11 +14,11 @@ class Servlet < WEBrick::HTTPServlet::AbstractServlet
14
14
  end
15
15
 
16
16
  class DummyServer
17
- def initialize
18
- @server = WEBrick::HTTPServer.new :Port => 8000,
17
+ def initialize port, path, response
18
+ @server = WEBrick::HTTPServer.new :Port => port,
19
19
  :AccessLog => [],
20
20
  :Logger => WEBrick::Log::new("/dev/null", 7)
21
- @server.mount "/simple_contract.json", Servlet, '{"message": "Hello World!"}'
21
+ @server.mount path, Servlet, response
22
22
  end
23
23
 
24
24
  def start
@@ -1,10 +1,13 @@
1
1
  module Pacto
2
2
  describe Response do
3
+ let(:body_definition) do
4
+ {:type => "object", :required => true, :properties => double("body definition properties")}
5
+ end
3
6
  let(:definition) do
4
7
  {
5
8
  'status' => 200,
6
9
  'headers' => {'Content-Type' => 'application/json'},
7
- 'body' => double('definition body')
10
+ 'body' => body_definition
8
11
  }
9
12
  end
10
13
 
@@ -26,11 +29,12 @@ module Pacto
26
29
  describe '#validate' do
27
30
  let(:status) { 200 }
28
31
  let(:headers) { {'Content-Type' => 'application/json', 'Age' => '60'} }
32
+ let(:response_body) { {'message' => 'response'} }
29
33
  let(:fake_response) do
30
34
  double({
31
35
  :status => status,
32
36
  :headers => headers,
33
- :body => 'body'
37
+ :body => response_body
34
38
  })
35
39
  end
36
40
 
@@ -45,6 +49,80 @@ module Pacto
45
49
  end
46
50
  end
47
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 'should 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 'should not return an error when body is a string' do
68
+ response = described_class.new(definition)
69
+
70
+ response.validate(fake_response).should == []
71
+ end
72
+
73
+ it 'should return an error when body is nil' do
74
+ response = described_class.new(definition)
75
+
76
+ fake_response.stub(:body).and_return(nil)
77
+ response.validate(fake_response).size.should == 1
78
+ end
79
+ end
80
+
81
+ context 'if not required' do
82
+ let(:string_required) { false }
83
+
84
+ it 'should not return an error when body is a string' do
85
+ response = described_class.new(definition)
86
+
87
+ response.validate(fake_response).should == []
88
+ end
89
+
90
+ it 'should 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
+ response.validate(fake_response).should == []
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 "should not return an error" do
107
+ response = described_class.new(definition)
108
+
109
+ response.validate(fake_response).should == []
110
+ end
111
+ end
112
+
113
+ context 'body does not match pattern' do
114
+ let(:response_body) { 'cabscd' }
115
+
116
+ it "should return an error" do
117
+ response = described_class.new(definition)
118
+
119
+ response.validate(fake_response).size.should == 1
120
+ end
121
+ end
122
+
123
+ end
124
+ end
125
+
48
126
  context 'when status does not match' do
49
127
  let(:status) { 500 }
50
128
 
@@ -1,2 +1,5 @@
1
- require 'coveralls'
2
- Coveralls.wear!
1
+ # Enable Coveralls only on the CI environment
2
+ if ENV['CI']
3
+ require 'coveralls'
4
+ Coveralls.wear!
5
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pacto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-16 00:00:00.000000000 Z
12
+ date: 2013-08-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: webmock
@@ -28,7 +28,7 @@ dependencies:
28
28
  - !ruby/object:Gem::Version
29
29
  version: '0'
30
30
  - !ruby/object:Gem::Dependency
31
- name: json
31
+ name: yajl-ruby
32
32
  requirement: !ruby/object:Gem::Requirement
33
33
  none: false
34
34
  requirements:
@@ -230,6 +230,7 @@ files:
230
230
  - .gitignore
231
231
  - .rspec
232
232
  - .rspec_integration
233
+ - .rspec_unit
233
234
  - .travis.yml
234
235
  - Gemfile
235
236
  - Guardfile
@@ -278,12 +279,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
278
279
  - - ! '>='
279
280
  - !ruby/object:Gem::Version
280
281
  version: '0'
282
+ segments:
283
+ - 0
284
+ hash: -3258303232236998448
281
285
  required_rubygems_version: !ruby/object:Gem::Requirement
282
286
  none: false
283
287
  requirements:
284
288
  - - ! '>='
285
289
  - !ruby/object:Gem::Version
286
290
  version: '0'
291
+ segments:
292
+ - 0
293
+ hash: -3258303232236998448
287
294
  requirements: []
288
295
  rubyforge_project:
289
296
  rubygems_version: 1.8.25