pacto 0.2.1 → 0.2.2

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