pacto 0.3.0 → 0.3.1

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3465015cc69868938ee729098b3c08df3a0df32a
4
- data.tar.gz: 65e2129430e2679ae8b2bc6dc109447a35e3a174
3
+ metadata.gz: 18ae0d545ae833247f073b842eb80cae0bfe15bd
4
+ data.tar.gz: 7bbc42514d8b1efecaa5dd650d042f4135ead5da
5
5
  SHA512:
6
- metadata.gz: b14f918661f414d1f2e84b7dc6a95903e18f0ead81eb4f7188eedb9761e3e2aeda225b2c8f0bd97d322593a41b6d71f1d1b99c150f4e155d7995e078137f0bf6
7
- data.tar.gz: f030e06b0d84bee8ed495a0f0c9d4434255dec6c7904d5249c62ea5e0a1bffd737e61aaba6b7b3b7e5ca00d8f2e0e507e474ed9f55598af2ab1ee075db4ac7da
6
+ metadata.gz: b3518a69c9f3d65f241f23ed2879349b6016f9411c2991e2bbed85a63ad0091959aedb5d829f7111f14c47a4596a158bf5b3294e8e8c8a951c45f63629cd374a
7
+ data.tar.gz: 22d00ac297683e0233c7d002891f5a5f26e133dbc8f7590a3431b567b48200ddd5c0fc02750f9eb17dcd077480a8fe15a9846c8d885f84c51595c1c3cc290fbb
data/.gitignore CHANGED
@@ -20,6 +20,5 @@ tmp
20
20
  .idea/
21
21
  .floo*
22
22
  .sublime*
23
- bin/*
24
23
  tags
25
-
24
+ pacto.log
@@ -0,0 +1,12 @@
1
+ 0.3.1
2
+
3
+ Enhancements:
4
+ * #103 - Display file URI instead of meaningless schema identifier in messages
5
+
6
+ Bug Fixes:
7
+ * #102 - Fix rake pacto:generate task
8
+
9
+
10
+ 0.3.0
11
+
12
+ First stable release
@@ -77,6 +77,12 @@ tasks:
77
77
  - Integration tests (`bundle exec rake integration`).
78
78
  - User journey tests (`bundle exec rake journey`).
79
79
 
80
+ It is also possible run specific tests:
81
+
82
+ - Unit tests (`bundle exec rspec spec/unit/[file_path]`
83
+ - Integration tests (`bundle exec rspec spec/integration/[file_path]`)
84
+ - User journey tests (`bundle exec cucumber features/[file_path] -r features/support/env.rb`)
85
+
80
86
  ### Checking that all is green
81
87
 
82
88
  To know that both tests and static analysis is working fine you just have to
data/Gemfile CHANGED
@@ -1,7 +1,12 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in pacto.gemspec
4
- gemspec
4
+ gemspec :name => 'pacto'
5
+
6
+ Dir['pacto-*.gemspec'].each do |gemspec|
7
+ plugin = gemspec.scan(/pacto-(.*)\.gemspec/).flatten.first
8
+ gemspec(:name => "pacto-#{plugin}", :development_group => plugin)
9
+ end
5
10
 
6
11
  # This is only used by Relish tests. Putting it here let's travis
7
12
  # pre-install so we can speed up the test with `bundle install --local`,
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  **If you're viewing this at https://github.com/thoughtworks/pacto,
8
8
  you're reading the documentation for the master branch.
9
9
  [View documentation for the latest release
10
- (0.2.5).](https://github.com/thoughtworks/pacto/tree/v0.2.5)**
10
+ (0.3.0).](https://github.com/thoughtworks/pacto/tree/v0.3.0)**
11
11
 
12
12
  # Pacto
13
13
 
@@ -44,6 +44,8 @@ Pacto can provide a [**contract writer**](#generating) that tries to strike a ba
44
44
 
45
45
  ## Usage
46
46
 
47
+ **See also: http://thoughtworks.github.io/pacto/usage/**
48
+
47
49
  Pacto can perform three activities: generating, validating, or stubbing services. You can do each of these activities against either live or stubbed services.
48
50
 
49
51
  ### Configuration
@@ -122,6 +124,8 @@ contracts.stub_all(request_id: 14, name: "Marcos")
122
124
 
123
125
  ## Pacto Server (non-Ruby usage)
124
126
 
127
+ **See also: http://thoughtworks.github.io/pacto/patterns/polyglot/**
128
+
125
129
  It is really easy to embed Pacto inside a small server. We haven't bundled a server inside of Pacto, but check out [pacto-demo](https://github.com/thoughtworks/pacto-demo) to see how easily you can expose Pacto via server.
126
130
 
127
131
  That demo lets you easily run a server in several modes:
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- require 'bundler/gem_tasks'
2
1
  require 'rspec/core/rake_task'
3
2
  require 'pacto/rake_task'
4
3
  require 'cucumber'
@@ -27,3 +26,39 @@ RSpec::Core::RakeTask.new(:integration) do |t|
27
26
  end
28
27
 
29
28
  task :default => [:unit, :integration, :journeys, :rubocop, 'coveralls:push']
29
+
30
+ desc 'Build gems into the pkg directory'
31
+ task :build do
32
+ FileUtils.rm_rf('pkg')
33
+ Dir['*.gemspec'].each do |gemspec|
34
+ system "gem build #{gemspec}"
35
+ end
36
+ FileUtils.mkdir_p('pkg')
37
+ FileUtils.mv(Dir['*.gem'], 'pkg')
38
+ end
39
+
40
+ desc 'Tags version, pushes to remote, and pushes gems'
41
+ task :release => :build do
42
+ sh 'git', 'tag', '-m', changelog, "v#{Pacto::VERSION}"
43
+ sh 'git push origin master'
44
+ sh "git push origin v#{Pacto::VERSION}"
45
+ sh 'ls pkg/*.gem | xargs -n 1 gem push'
46
+ end
47
+
48
+ task :changelog do
49
+ changelog
50
+ end
51
+
52
+ def changelog
53
+ changelog = File.read('CHANGELOG').split("\n\n\n", 2).first
54
+ confirm "Does the CHANGELOG look correct? ", changelog
55
+ end
56
+
57
+ def confirm(question, data)
58
+ puts "Please confirm..."
59
+ puts data
60
+ print question
61
+ abort "Aborted" unless $stdin.gets.strip == 'y'
62
+ puts "Confirmed"
63
+ data
64
+ end
@@ -26,3 +26,54 @@ Feature: Contract Generation
26
26
  }
27
27
  }
28
28
  """
29
+
30
+ Scenario: Generating a contract using the rake task
31
+ Given a directory named "contracts"
32
+ When I successfully run `bundle exec rake pacto:generate['tmp/aruba/requests','tmp/aruba/contracts','http://localhost:8000']`
33
+ Then the output should contain "Successfully generated all contracts"
34
+
35
+ Scenario: Generating a contract programmatically
36
+ Given a file named "generate.rb" with:
37
+ """ruby
38
+ require 'pacto'
39
+
40
+ WebMock.allow_net_connect!
41
+ generator = Pacto::Generator.new
42
+ contract = generator.generate('requests/my_contract.json', 'http://localhost:8000')
43
+ puts contract
44
+ """
45
+ When I successfully run `bundle exec ruby generate.rb`
46
+ Then the output should contain exactly:
47
+ """json
48
+ {
49
+ "request": {
50
+ "headers": {
51
+ "Accept": "application/json"
52
+ },
53
+ "method": "get",
54
+ "params": {
55
+ },
56
+ "path": "/hello"
57
+ },
58
+ "response": {
59
+ "headers": {
60
+ "Content-Type": "application/json",
61
+ "Vary": "Accept"
62
+ },
63
+ "status": 200,
64
+ "body": {
65
+ "$schema": "http://json-schema.org/draft-03/schema#",
66
+ "description": "Generated from requests/my_contract.json with shasum 210fa3b144ef2db8d1c160c4d9e8d8bf738ed851",
67
+ "type": "object",
68
+ "required": true,
69
+ "properties": {
70
+ "message": {
71
+ "type": "string",
72
+ "required": true
73
+ }
74
+ }
75
+ }
76
+ }
77
+ }
78
+
79
+ """
@@ -5,7 +5,7 @@ module Pacto
5
5
  def initialize(request, response, file, name = nil, request_pattern_provider = RequestPattern)
6
6
  @request = request
7
7
  @response = response
8
- @file = file.to_s
8
+ @file = Addressable::URI.convert_path(file.to_s).to_s
9
9
  @name = name || @file
10
10
  @request_pattern = request_pattern_provider.for(request)
11
11
  @values = {}
@@ -8,7 +8,7 @@ module Pacto
8
8
  @provider = Stubs::WebMockAdapter.new
9
9
  @strict_matchers = true
10
10
  @contracts_path = nil
11
- @logger = Logger.instance
11
+ @logger = Logger::SimpleLogger.instance
12
12
  define_logger_level
13
13
  @hook = Hook.new {}
14
14
  @generator_options = { :schema_version => 'draft3' }
@@ -1,6 +1,7 @@
1
1
  module Pacto
2
2
  class ValidationRegistry
3
3
  include Singleton
4
+ include Logger
4
5
  attr_reader :validations
5
6
 
6
7
  def initialize
@@ -35,11 +36,5 @@ module Pacto
35
36
  !validation.successful?
36
37
  end
37
38
  end
38
-
39
- private
40
-
41
- def logger
42
- @logger ||= Logger.instance
43
- end
44
39
  end
45
40
  end
@@ -1,9 +1,10 @@
1
1
  module Pacto
2
2
  class ERBProcessor
3
+ include Logger
3
4
  def process(contract, values = {})
4
5
  erb = ERB.new(contract)
5
6
  erb_result = erb.result hash_binding(values)
6
- Logger.instance.debug "Processed contract: #{erb_result.inspect}"
7
+ logger.debug "Processed contract: #{erb_result.inspect}"
7
8
  erb_result
8
9
  end
9
10
 
@@ -14,6 +14,13 @@ module Pacto
14
14
  @filters = filters
15
15
  end
16
16
 
17
+ def generate(request_file, host)
18
+ contract = Pacto.load_contract request_file, host
19
+ request = contract.request
20
+ response = contract.request.execute
21
+ save(request_file, request, response)
22
+ end
23
+
17
24
  def save(source, request, response)
18
25
  contract = generate_contract source, request, response
19
26
  pretty_contract = MultiJson.encode(contract, :pretty => true)
@@ -1,44 +1,50 @@
1
1
  require 'forwardable'
2
2
 
3
3
  module Pacto
4
- class Logger
5
- include Singleton
6
- extend Forwardable
7
-
8
- def_delegators :@log, :debug, :info, :warn, :error, :fatal
9
-
10
- def initialize
11
- log ::Logger.new STDOUT
12
- end
13
-
14
- def log(log)
15
- @log = log
16
- @log.level = default_level
17
- @log.progname = 'Pacto'
18
- end
19
-
20
- def level=(level)
21
- @log.level = log_levels.fetch(level, default_level)
22
- end
23
-
24
- def level
25
- log_levels.key @log.level
26
- end
27
-
28
- private
29
-
30
- def default_level
31
- ::Logger::ERROR
4
+ module Logger
5
+ def logger
6
+ Pacto.configuration.logger
32
7
  end
33
8
 
34
- def log_levels
35
- {
36
- debug: ::Logger::DEBUG,
37
- info: ::Logger::INFO,
38
- warn: ::Logger::WARN,
39
- error: ::Logger::ERROR,
40
- fatal: ::Logger::FATAL
41
- }
9
+ class SimpleLogger
10
+ include Singleton
11
+ extend Forwardable
12
+
13
+ def_delegators :@log, :debug, :info, :warn, :error, :fatal
14
+
15
+ def initialize
16
+ log ::Logger.new STDOUT
17
+ end
18
+
19
+ def log(log)
20
+ @log = log
21
+ @log.level = default_level
22
+ @log.progname = 'Pacto'
23
+ end
24
+
25
+ def level=(level)
26
+ @log.level = log_levels.fetch(level, default_level)
27
+ end
28
+
29
+ def level
30
+ log_levels.key @log.level
31
+ end
32
+
33
+ private
34
+
35
+ def default_level
36
+ ::Logger::ERROR
37
+ end
38
+
39
+ def log_levels
40
+ {
41
+ debug: ::Logger::DEBUG,
42
+ info: ::Logger::INFO,
43
+ warn: ::Logger::WARN,
44
+ error: ::Logger::ERROR,
45
+ fatal: ::Logger::FATAL
46
+ }
47
+ end
42
48
  end
43
49
  end
44
50
  end
@@ -2,6 +2,8 @@ module Pacto
2
2
  module Stubs
3
3
  class WebMockHelper
4
4
  class << self
5
+ include Logger
6
+
5
7
  def validate(request_signature, response)
6
8
  pacto_response = webmock_to_pacto_response(response)
7
9
  contract = Pacto.contracts_for(request_signature).first
@@ -43,10 +45,6 @@ module Pacto
43
45
  File.join(Pacto.configuration.contracts_path, uri.host, File.dirname(uri.path), basename)
44
46
  end
45
47
 
46
- def logger
47
- @logger ||= Logger.instance
48
- end
49
-
50
48
  def webmock_to_pacto_request(webmock_request)
51
49
  uri = webmock_request.uri
52
50
  Faraday::Request.create webmock_request.method do |req|
@@ -1,5 +1,6 @@
1
1
  module Pacto
2
2
  class Validation
3
+ include Logger
3
4
  attr_reader :request, :response, :contract, :results
4
5
 
5
6
  def initialize(request, response, contract)
@@ -45,10 +46,6 @@ module Pacto
45
46
 
46
47
  private
47
48
 
48
- def logger
49
- @logger ||= Logger.instance
50
- end
51
-
52
49
  def validate
53
50
  logger.debug("Validating #{@request}, #{@response} against #{@contract}")
54
51
  @results = contract.validate_consumer(@request, @response)
@@ -5,17 +5,24 @@ module Pacto
5
5
  fail 'section name should be provided by subclass'
6
6
  end
7
7
 
8
- def self.validate(schema, body)
8
+ def self.subschema(contract)
9
+ fail 'override to return the proper subschema the contract'
10
+ end
11
+
12
+ # FIXME: https://github.com/thoughtworks/pacto/issues/10#issuecomment-31281238
13
+ # rubocop:disable MethodLenth
14
+ def self.validate(contract, body)
15
+ schema = subschema(contract)
9
16
  if schema
17
+ schema['id'] = contract.file unless schema.key? 'id'
10
18
  if schema['type'] && schema['type'] == 'string'
11
19
  validate_as_pure_string schema, body.body
12
20
  else
13
- body.respond_to?(:body) ? validate_as_json(schema, body.body) : validate_as_json(schema, body)
21
+ validate_as_json(schema, body)
14
22
  end
15
- else
16
- []
17
- end
23
+ end || []
18
24
  end
25
+ # rubocop:enable MethodLenth
19
26
 
20
27
  private
21
28
 
@@ -34,6 +41,7 @@ module Pacto
34
41
  end
35
42
 
36
43
  def self.validate_as_json(schema, body)
44
+ body = body.body if body.respond_to? :body
37
45
  JSON::Validator.fully_validate(schema, body, :version => :draft3)
38
46
  end
39
47
  end
@@ -9,11 +9,14 @@ module Pacto
9
9
  'request'
10
10
  end
11
11
 
12
+ def self.subschema(contract)
13
+ contract.request.schema
14
+ end
15
+
12
16
  def call(env)
13
17
  if env[:validation_results].empty? # skip body validation if we already have other errors
14
- expected_body = env[:contract].request.schema
15
18
  actual_body = env[:actual_request]
16
- errors = self.class.validate(expected_body, actual_body)
19
+ errors = self.class.validate(env[:contract], actual_body)
17
20
  env[:validation_results].concat errors.compact
18
21
  end
19
22
  @app.call env
@@ -9,11 +9,14 @@ module Pacto
9
9
  'response'
10
10
  end
11
11
 
12
+ def self.subschema(contract)
13
+ contract.response.schema
14
+ end
15
+
12
16
  def call(env)
13
17
  if env[:validation_results].empty? # skip body validation if we already have other errors
14
- expected_body = env[:contract].response.schema
15
18
  actual_body = env[:actual_response]
16
- errors = self.class.validate(expected_body, actual_body)
19
+ errors = self.class.validate(env[:contract], actual_body)
17
20
  env[:validation_results].concat errors.compact
18
21
  end
19
22
  @app.call env
@@ -1,3 +1,3 @@
1
1
  module Pacto
2
- VERSION = '0.3.0'
2
+ VERSION = '0.3.1'
3
3
  end
@@ -3,6 +3,10 @@ lib = File.expand_path('../lib', __FILE__)
3
3
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
  require 'pacto/version'
5
5
 
6
+ plugin_files = Dir['pacto-*.gemspec'].map do |gemspec|
7
+ eval(File.read(gemspec)).files # rubocop:disable Eval
8
+ end.flatten.uniq
9
+
6
10
  Gem::Specification.new do |gem|
7
11
  gem.name = 'pacto'
8
12
  gem.version = Pacto::VERSION
@@ -13,7 +17,7 @@ Gem::Specification.new do |gem|
13
17
  gem.homepage = 'http://thoughtworks.github.io/pacto/'
14
18
  gem.license = 'MIT'
15
19
 
16
- gem.files = `git ls-files`.split($/) # rubocop:disable SpecialGlobalVars
20
+ gem.files = `git ls-files`.split($/) - plugin_files # rubocop:disable SpecialGlobalVars
17
21
  gem.executables = gem.files.grep(/^bin\//).map { |f| File.basename(f) }
18
22
  gem.test_files = gem.files.grep(/^(test|spec|features)\//)
19
23
  gem.require_paths = ['lib']
@@ -22,24 +26,25 @@ Gem::Specification.new do |gem|
22
26
  gem.add_dependency 'middleware', '~> 0.1'
23
27
  gem.add_dependency 'multi_json', '~> 1.8'
24
28
  gem.add_dependency 'json-schema', '~> 2.0'
25
- gem.add_dependency 'json-generator', '>= 0.0.5'
29
+ gem.add_dependency 'json-generator', '~> 0.0', '>= 0.0.5'
26
30
  gem.add_dependency 'hash-deep-merge', '~> 0.1'
27
31
  gem.add_dependency 'faraday', '~> 0.9'
28
32
  gem.add_dependency 'addressable', '~> 2.3'
29
- gem.add_dependency 'json-schema-generator', '>= 0.0.7'
33
+ gem.add_dependency 'json-schema-generator', '~> 0.0', '>= 0.0.7'
30
34
  gem.add_dependency 'term-ansicolor', '~> 1.3'
31
35
 
32
- gem.add_development_dependency 'coveralls'
33
- gem.add_development_dependency 'rake'
34
- gem.add_development_dependency 'rake-notes'
36
+ gem.add_development_dependency 'coveralls', '~> 0'
37
+ gem.add_development_dependency 'rake', '~> 10.0'
38
+ gem.add_development_dependency 'rake-notes', '~> 0'
35
39
  gem.add_development_dependency 'rspec', '~> 2.14'
36
- gem.add_development_dependency 'should_not'
37
- gem.add_development_dependency 'aruba'
38
- gem.add_development_dependency 'relish'
39
- gem.add_development_dependency 'guard-rspec'
40
- gem.add_development_dependency 'rubocop', '~> 0.16.0'
41
- gem.add_development_dependency 'guard-rubocop'
42
- gem.add_development_dependency 'guard-cucumber'
43
- gem.add_development_dependency 'rb-fsevent' if RUBY_PLATFORM =~ /darwin/i
44
- gem.add_development_dependency 'terminal-notifier-guard' if RUBY_PLATFORM =~ /darwin/i
40
+ gem.add_development_dependency 'should_not', '~> 1.0'
41
+ gem.add_development_dependency 'aruba', '~> 0'
42
+ # Only required to push documentation, and not easily installed on Windows
43
+ # gem.add_development_dependency 'relish'
44
+ gem.add_development_dependency 'guard-rspec', '~> 4.2'
45
+ gem.add_development_dependency 'rubocop', '~> 0.16'
46
+ gem.add_development_dependency 'guard-rubocop', '~> 1.0'
47
+ gem.add_development_dependency 'guard-cucumber', '~> 1.4'
48
+ gem.add_development_dependency 'rb-fsevent', '~> 0' if RUBY_PLATFORM =~ /darwin/i
49
+ gem.add_development_dependency 'terminal-notifier-guard', '~> 1.5' if RUBY_PLATFORM =~ /darwin/i
45
50
  end
@@ -10,7 +10,7 @@
10
10
 
11
11
  "response": {
12
12
  "status": 200,
13
- "headers": { "Content-Type": "application/json" },
13
+ "headers": { "Content-Type": "application/json", "Vary": "Accept" },
14
14
  "body": {
15
15
  "type": "object",
16
16
  "required": true,
@@ -25,6 +25,13 @@ describe 'pacto/rspec' do
25
25
  MultiJson.load(response.body)
26
26
  end
27
27
 
28
+ def play_bad_response
29
+ contracts.stub_all(:device_id => 1.5)
30
+ Faraday.get('http://dummyprovider.com/strict') do |req|
31
+ req.headers = {'Accept' => 'application/json' }
32
+ end
33
+ end
34
+
28
35
  context 'successful validations' do
29
36
  let(:contracts) do
30
37
  Pacto.load_contracts 'spec/integration/data/', 'http://dummyprovider.com'
@@ -82,13 +89,15 @@ describe 'pacto/rspec' do
82
89
  end
83
90
 
84
91
  it 'displays Contract validation problems' do
85
- contracts.stub_all(:device_id => 1.5)
86
- Faraday.get('http://dummyprovider.com/strict') do |req|
87
- req.headers = {'Accept' => 'application/json' }
88
- end
92
+ play_bad_response
89
93
  expect_to_raise(/validation errors were found:/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/strict') }
90
-
91
94
  expect_to_raise(/but the following issues were found:/) { expect(Pacto).to_not have_failed_validations }
92
95
  end
96
+
97
+ it 'displays the Contract file' do
98
+ play_bad_response
99
+ schema_file_uri = Addressable::URI.convert_path(File.absolute_path strict_contract_path).to_s
100
+ expect_to_raise(/in schema #{schema_file_uri}/) { expect(Pacto).to have_validated(:get, 'http://dummyprovider.com/strict') }
101
+ end
93
102
  end
94
103
  end
@@ -1,5 +1,6 @@
1
1
  require 'webrick'
2
2
  require 'forwardable'
3
+ require 'tempfile'
3
4
 
4
5
  module Pacto
5
6
  module Server
@@ -21,10 +22,11 @@ module Pacto
21
22
 
22
23
  class Dummy
23
24
  def initialize(port, path, response)
25
+ log_file = File.exists?('/dev/null') ? '/dev/null' : Tempfile.new('log') # So tests run on Windows
24
26
  params = {
25
27
  :Port => port,
26
28
  :AccessLog => [],
27
- :Logger => WEBrick::Log.new('/dev/null', 7)
29
+ :Logger => WEBrick::Log.new(log_file, 7)
28
30
  }
29
31
  @server = WEBrick::HTTPServer.new params
30
32
  @server.mount path, Servlet, response
@@ -16,7 +16,7 @@ module Pacto
16
16
  end
17
17
 
18
18
  it 'sets logger by default to Logger' do
19
- expect(configuration.logger).to be_kind_of Logger
19
+ expect(configuration.logger).to be_kind_of Logger::SimpleLogger
20
20
  end
21
21
 
22
22
  context 'about logging' do
@@ -42,13 +42,13 @@ module Pacto
42
42
 
43
43
  describe '.validate' do
44
44
  before do
45
- allow(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(expected_request.schema, actual_request).and_return([])
46
- allow(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(expected_response.schema, actual_response).and_return([])
45
+ allow(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(contract, actual_request).and_return([])
46
+ allow(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(contract, actual_response).and_return([])
47
47
  end
48
48
 
49
49
  context 'default validator stack' do
50
50
  it 'calls the RequestBodyValidator' do
51
- expect(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(expected_request.schema, actual_request).and_return(validation_errors)
51
+ expect(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(contract, actual_request).and_return(validation_errors)
52
52
  expect(ContractValidator.validate contract, actual_request, actual_response, opts).to eq(validation_errors)
53
53
  end
54
54
 
@@ -63,7 +63,7 @@ module Pacto
63
63
  end
64
64
 
65
65
  it 'calls the ResponseBodyValidator' do
66
- expect(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(expected_response.schema, actual_response).and_return(validation_errors)
66
+ expect(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(contract, actual_response).and_return(validation_errors)
67
67
  expect(ContractValidator.validate contract, actual_request, actual_response, opts).to eq(validation_errors)
68
68
  end
69
69
  end
@@ -73,10 +73,10 @@ module Pacto
73
73
  # JSON::Validator.should_receive(:fully_validate).
74
74
  # with(definition['body'], fake_response.body, :version => :draft3).
75
75
  # and_return([])
76
- expect(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(expected_request.schema, actual_request).and_return([])
76
+ expect(Pacto::Validators::RequestBodyValidator).to receive(:validate).with(contract, actual_request).and_return([])
77
77
  expect(Pacto::Validators::ResponseStatusValidator).to receive(:validate).with(expected_response.status, actual_response.status).and_return([])
78
78
  expect(Pacto::Validators::ResponseHeaderValidator).to receive(:validate).with(expected_response.headers, actual_response.headers).and_return([])
79
- expect(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(expected_response.schema, actual_response).and_return([])
79
+ expect(Pacto::Validators::ResponseBodyValidator).to receive(:validate).with(contract, actual_response).and_return([])
80
80
  expect(ContractValidator.validate contract, actual_request, actual_response, opts).to be_empty
81
81
  end
82
82
  end
@@ -11,7 +11,7 @@ module Pacto
11
11
  end
12
12
 
13
13
  it 'logs the erb processed' do
14
- Logger.instance.should_receive(:debug).with("Processed contract: \"#{result}\"")
14
+ Pacto.configuration.logger.should_receive(:debug).with("Processed contract: \"#{result}\"")
15
15
  processor.process erb
16
16
  end
17
17
 
@@ -3,19 +3,20 @@ module Pacto
3
3
  let(:record_host) do
4
4
  'http://example.com'
5
5
  end
6
-
7
6
  let(:request) do
8
- Faraday::Request.create :get do |req|
9
- req.path = '/abcd'
10
- req.params = { 'apikey' => "<%= ENV['MY_API_KEY'] %>" }
11
- req.headers = {
12
- 'Content-Length' => [1234],
13
- 'Via' => ['Some Proxy'],
14
- 'User-Agent' => ['rspec']
15
- }
16
- end
7
+ Pacto::RequestClause.new(record_host,
8
+ 'method' => 'GET',
9
+ 'path' => '/abcd',
10
+ 'headers' => {
11
+ 'Content-Length' => [1234],
12
+ 'Via' => ['Some Proxy'],
13
+ 'User-Agent' => ['rspec']
14
+ },
15
+ 'params' => {
16
+ 'apikey' => "<%= ENV['MY_API_KEY'] %>"
17
+ }
18
+ )
17
19
  end
18
-
19
20
  let(:response_adapter) do
20
21
  Faraday::Response.new(
21
22
  :status => 200,
@@ -43,6 +44,34 @@ module Pacto
43
44
  MultiJson.encode(obj, :pretty => true).gsub(/^$\n/, '')
44
45
  end
45
46
 
47
+ describe '#generate' do
48
+ let(:request_contract) do
49
+ double(
50
+ :request => request
51
+ )
52
+ end
53
+ let(:generated_contract) { double('generated contract') }
54
+ before do
55
+ Pacto.should_receive(:load_contract).with(request_file, record_host).and_return request_contract
56
+ request.should_receive(:execute).and_return response_adapter
57
+ end
58
+
59
+ it 'parses the request' do
60
+ generator.should_receive(:save).with(request_file, request, anything)
61
+ generator.generate request_file, record_host
62
+ end
63
+
64
+ it 'fetches a response' do
65
+ generator.should_receive(:save).with(request_file, anything, response_adapter)
66
+ generator.generate request_file, record_host
67
+ end
68
+
69
+ it 'saves the result' do
70
+ generator.should_receive(:save).with(request_file, request, response_adapter).and_return generated_contract
71
+ expect(generator.generate request_file, record_host).to eq(generated_contract)
72
+ end
73
+ end
74
+
46
75
  describe '#save' do
47
76
  before do
48
77
  filters.should_receive(:filter_request_headers).with(request, response_adapter).and_return filtered_request_headers
@@ -1,44 +1,46 @@
1
1
  module Pacto
2
- describe Logger do
3
- before do
4
- logger.log logger_lib
5
- end
6
-
7
- subject(:logger) { described_class.instance }
8
- let(:logger_lib) { ::Logger.new(StringIO.new) }
9
-
10
- it 'delegates debug to the logger lib' do
11
- logger_lib.should_receive(:debug)
12
- logger.debug
13
- end
14
-
15
- it 'delegates info to the logger lib' do
16
- logger_lib.should_receive(:info)
17
- logger.info
18
- end
19
-
20
- it 'delegates warn to the logger lib' do
21
- logger_lib.should_receive(:warn)
22
- logger.warn
23
- end
24
-
25
- it 'delegates error to the logger lib' do
26
- logger_lib.should_receive(:error)
27
- logger.error
28
- end
29
-
30
- it 'delegates fatal to the logger lib' do
31
- logger_lib.should_receive(:error)
32
- logger.error
33
- end
34
-
35
- it 'has the default log level as error' do
36
- expect(logger.level).to eq :error
37
- end
38
-
39
- it 'provides access to the log level' do
40
- logger.level = :info
41
- expect(logger.level).to eq :info
2
+ module Logger
3
+ describe SimpleLogger do
4
+ before do
5
+ logger.log logger_lib
6
+ end
7
+
8
+ subject(:logger) { described_class.instance }
9
+ let(:logger_lib) { ::Logger.new(StringIO.new) }
10
+
11
+ it 'delegates debug to the logger lib' do
12
+ logger_lib.should_receive(:debug)
13
+ logger.debug
14
+ end
15
+
16
+ it 'delegates info to the logger lib' do
17
+ logger_lib.should_receive(:info)
18
+ logger.info
19
+ end
20
+
21
+ it 'delegates warn to the logger lib' do
22
+ logger_lib.should_receive(:warn)
23
+ logger.warn
24
+ end
25
+
26
+ it 'delegates error to the logger lib' do
27
+ logger_lib.should_receive(:error)
28
+ logger.error
29
+ end
30
+
31
+ it 'delegates fatal to the logger lib' do
32
+ logger_lib.should_receive(:error)
33
+ logger.error
34
+ end
35
+
36
+ it 'has the default log level as error' do
37
+ expect(logger.level).to eq :error
38
+ end
39
+
40
+ it 'provides access to the log level' do
41
+ logger.level = :info
42
+ expect(logger.level).to eq :info
43
+ end
42
44
  end
43
45
  end
44
46
  end
@@ -39,7 +39,7 @@ module Pacto
39
39
  stubbed_request.stub(:to_return).with(
40
40
  :status => response.status,
41
41
  :headers => response.headers,
42
- :body => response.body.to_json,
42
+ :body => response.body.to_json
43
43
  )
44
44
  stubbed_request.stub(:request_pattern).and_return request_pattern
45
45
  end
@@ -50,7 +50,8 @@ module Pacto
50
50
  expect(block.parameters).to have(2).items
51
51
  end
52
52
 
53
- WebMockAdapter.new
53
+ # WebMockAdapter.new
54
+ Pacto.configuration.provider # this way the rpec after block doesn't create a second instance
54
55
  end
55
56
  end
56
57
 
@@ -2,23 +2,36 @@ module Pacto
2
2
  module Validators
3
3
  describe BodyValidator do
4
4
  class MyBodyValidator < BodyValidator
5
- def self.section_name
6
- 'my_section'
5
+ class << self
6
+ attr_writer :subschema
7
+
8
+ def section_name
9
+ 'my_section'
10
+ end
11
+
12
+ def subschema(contract)
13
+ @subschema
14
+ end
7
15
  end
8
16
  end
9
17
 
10
18
  subject(:validator) { MyBodyValidator }
11
19
  let(:string_required) { true }
20
+ let(:contract) { double('contract', :file => 'file:///a.json') }
12
21
  let(:body) { 'a simple string' }
13
22
  let(:fake_interaction) { double(:fake_interaction, body: body) }
14
23
 
24
+ before(:each) do
25
+ MyBodyValidator.subschema = schema
26
+ end
27
+
15
28
  describe '#validate' do
16
29
  context 'when schema is not specified' do
17
30
  let(:schema) { nil }
18
31
 
19
32
  it 'gives no errors without validating body' do
20
33
  JSON::Validator.should_not_receive(:fully_validate)
21
- expect(validator.validate(schema, fake_interaction)).to be_empty
34
+ expect(validator.validate(contract, fake_interaction)).to be_empty
22
35
  end
23
36
  end
24
37
 
@@ -31,17 +44,17 @@ module Pacto
31
44
  # FIXME: This seems like a design flaw. We're partially reproducing json-schema behavior
32
45
  # instead of finding a way to use it.
33
46
  JSON::Validator.should_not_receive(:fully_validate)
34
- validator.validate(schema, fake_interaction)
47
+ validator.validate(contract, fake_interaction)
35
48
  end
36
49
 
37
50
  context 'if required' do
38
51
  it 'does not return an error when body is a string' do
39
- expect(validator.validate(schema, fake_interaction)).to be_empty
52
+ expect(validator.validate(contract, fake_interaction)).to be_empty
40
53
  end
41
54
 
42
55
  it 'returns an error when body is nil' do
43
56
  expect(fake_interaction).to receive(:body).and_return nil
44
- expect(validator.validate(schema, fake_interaction).size).to eq 1
57
+ expect(validator.validate(contract, fake_interaction).size).to eq 1
45
58
  end
46
59
  end
47
60
 
@@ -49,12 +62,12 @@ module Pacto
49
62
  let(:string_required) { false }
50
63
 
51
64
  it 'does not return an error when body is a string' do
52
- expect(validator.validate(schema, fake_interaction)).to be_empty
65
+ expect(validator.validate(contract, fake_interaction)).to be_empty
53
66
  end
54
67
 
55
68
  it 'does not return an error when body is nil' do
56
69
  expect(fake_interaction).to receive(:body).and_return nil
57
- expect(validator.validate(schema, fake_interaction)).to be_empty
70
+ expect(validator.validate(contract, fake_interaction)).to be_empty
58
71
  end
59
72
  end
60
73
 
@@ -67,7 +80,7 @@ module Pacto
67
80
  let(:body) { 'abc' } # This matches the pattern /a.c/
68
81
 
69
82
  it 'does not return an error' do
70
- expect(validator.validate(schema, fake_interaction)).to be_empty
83
+ expect(validator.validate(contract, fake_interaction)).to be_empty
71
84
  end
72
85
  end
73
86
 
@@ -75,7 +88,7 @@ module Pacto
75
88
  let(:body) { 'acb' } # This does not matches the pattern /a.c/
76
89
 
77
90
  it 'returns an error' do
78
- expect(validator.validate(schema, fake_interaction).size).to eq 1
91
+ expect(validator.validate(contract, fake_interaction).size).to eq 1
79
92
  end
80
93
  end
81
94
  end
@@ -87,7 +100,7 @@ module Pacto
87
100
  context 'when body matches' do
88
101
  it 'does not return any errors' do
89
102
  expect(JSON::Validator).to receive(:fully_validate).and_return([])
90
- expect(validator.validate(schema, fake_interaction)).to be_empty
103
+ expect(validator.validate(contract, fake_interaction)).to be_empty
91
104
  end
92
105
  end
93
106
 
@@ -95,7 +108,7 @@ module Pacto
95
108
  it 'returns a list of errors' do
96
109
  errors = double 'some errors'
97
110
  expect(JSON::Validator).to receive(:fully_validate).and_return(errors)
98
- expect(validator.validate(schema, fake_interaction)).to eq(errors)
111
+ expect(validator.validate(contract, fake_interaction)).to eq(errors)
99
112
  end
100
113
  end
101
114
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pacto
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - ThoughtWorks & Abril
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-02-12 00:00:00.000000000 Z
11
+ date: 2014-03-03 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: webmock
@@ -70,6 +70,9 @@ dependencies:
70
70
  name: json-generator
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.0'
73
76
  - - ">="
74
77
  - !ruby/object:Gem::Version
75
78
  version: 0.0.5
@@ -77,6 +80,9 @@ dependencies:
77
80
  prerelease: false
78
81
  version_requirements: !ruby/object:Gem::Requirement
79
82
  requirements:
83
+ - - "~>"
84
+ - !ruby/object:Gem::Version
85
+ version: '0.0'
80
86
  - - ">="
81
87
  - !ruby/object:Gem::Version
82
88
  version: 0.0.5
@@ -126,6 +132,9 @@ dependencies:
126
132
  name: json-schema-generator
127
133
  requirement: !ruby/object:Gem::Requirement
128
134
  requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '0.0'
129
138
  - - ">="
130
139
  - !ruby/object:Gem::Version
131
140
  version: 0.0.7
@@ -133,6 +142,9 @@ dependencies:
133
142
  prerelease: false
134
143
  version_requirements: !ruby/object:Gem::Requirement
135
144
  requirements:
145
+ - - "~>"
146
+ - !ruby/object:Gem::Version
147
+ version: '0.0'
136
148
  - - ">="
137
149
  - !ruby/object:Gem::Version
138
150
  version: 0.0.7
@@ -154,42 +166,42 @@ dependencies:
154
166
  name: coveralls
155
167
  requirement: !ruby/object:Gem::Requirement
156
168
  requirements:
157
- - - ">="
169
+ - - "~>"
158
170
  - !ruby/object:Gem::Version
159
171
  version: '0'
160
172
  type: :development
161
173
  prerelease: false
162
174
  version_requirements: !ruby/object:Gem::Requirement
163
175
  requirements:
164
- - - ">="
176
+ - - "~>"
165
177
  - !ruby/object:Gem::Version
166
178
  version: '0'
167
179
  - !ruby/object:Gem::Dependency
168
180
  name: rake
169
181
  requirement: !ruby/object:Gem::Requirement
170
182
  requirements:
171
- - - ">="
183
+ - - "~>"
172
184
  - !ruby/object:Gem::Version
173
- version: '0'
185
+ version: '10.0'
174
186
  type: :development
175
187
  prerelease: false
176
188
  version_requirements: !ruby/object:Gem::Requirement
177
189
  requirements:
178
- - - ">="
190
+ - - "~>"
179
191
  - !ruby/object:Gem::Version
180
- version: '0'
192
+ version: '10.0'
181
193
  - !ruby/object:Gem::Dependency
182
194
  name: rake-notes
183
195
  requirement: !ruby/object:Gem::Requirement
184
196
  requirements:
185
- - - ">="
197
+ - - "~>"
186
198
  - !ruby/object:Gem::Version
187
199
  version: '0'
188
200
  type: :development
189
201
  prerelease: false
190
202
  version_requirements: !ruby/object:Gem::Requirement
191
203
  requirements:
192
- - - ">="
204
+ - - "~>"
193
205
  - !ruby/object:Gem::Version
194
206
  version: '0'
195
207
  - !ruby/object:Gem::Dependency
@@ -210,128 +222,114 @@ dependencies:
210
222
  name: should_not
211
223
  requirement: !ruby/object:Gem::Requirement
212
224
  requirements:
213
- - - ">="
225
+ - - "~>"
214
226
  - !ruby/object:Gem::Version
215
- version: '0'
227
+ version: '1.0'
216
228
  type: :development
217
229
  prerelease: false
218
230
  version_requirements: !ruby/object:Gem::Requirement
219
231
  requirements:
220
- - - ">="
232
+ - - "~>"
221
233
  - !ruby/object:Gem::Version
222
- version: '0'
234
+ version: '1.0'
223
235
  - !ruby/object:Gem::Dependency
224
236
  name: aruba
225
237
  requirement: !ruby/object:Gem::Requirement
226
238
  requirements:
227
- - - ">="
228
- - !ruby/object:Gem::Version
229
- version: '0'
230
- type: :development
231
- prerelease: false
232
- version_requirements: !ruby/object:Gem::Requirement
233
- requirements:
234
- - - ">="
235
- - !ruby/object:Gem::Version
236
- version: '0'
237
- - !ruby/object:Gem::Dependency
238
- name: relish
239
- requirement: !ruby/object:Gem::Requirement
240
- requirements:
241
- - - ">="
239
+ - - "~>"
242
240
  - !ruby/object:Gem::Version
243
241
  version: '0'
244
242
  type: :development
245
243
  prerelease: false
246
244
  version_requirements: !ruby/object:Gem::Requirement
247
245
  requirements:
248
- - - ">="
246
+ - - "~>"
249
247
  - !ruby/object:Gem::Version
250
248
  version: '0'
251
249
  - !ruby/object:Gem::Dependency
252
250
  name: guard-rspec
253
251
  requirement: !ruby/object:Gem::Requirement
254
252
  requirements:
255
- - - ">="
253
+ - - "~>"
256
254
  - !ruby/object:Gem::Version
257
- version: '0'
255
+ version: '4.2'
258
256
  type: :development
259
257
  prerelease: false
260
258
  version_requirements: !ruby/object:Gem::Requirement
261
259
  requirements:
262
- - - ">="
260
+ - - "~>"
263
261
  - !ruby/object:Gem::Version
264
- version: '0'
262
+ version: '4.2'
265
263
  - !ruby/object:Gem::Dependency
266
264
  name: rubocop
267
265
  requirement: !ruby/object:Gem::Requirement
268
266
  requirements:
269
267
  - - "~>"
270
268
  - !ruby/object:Gem::Version
271
- version: 0.16.0
269
+ version: '0.16'
272
270
  type: :development
273
271
  prerelease: false
274
272
  version_requirements: !ruby/object:Gem::Requirement
275
273
  requirements:
276
274
  - - "~>"
277
275
  - !ruby/object:Gem::Version
278
- version: 0.16.0
276
+ version: '0.16'
279
277
  - !ruby/object:Gem::Dependency
280
278
  name: guard-rubocop
281
279
  requirement: !ruby/object:Gem::Requirement
282
280
  requirements:
283
- - - ">="
281
+ - - "~>"
284
282
  - !ruby/object:Gem::Version
285
- version: '0'
283
+ version: '1.0'
286
284
  type: :development
287
285
  prerelease: false
288
286
  version_requirements: !ruby/object:Gem::Requirement
289
287
  requirements:
290
- - - ">="
288
+ - - "~>"
291
289
  - !ruby/object:Gem::Version
292
- version: '0'
290
+ version: '1.0'
293
291
  - !ruby/object:Gem::Dependency
294
292
  name: guard-cucumber
295
293
  requirement: !ruby/object:Gem::Requirement
296
294
  requirements:
297
- - - ">="
295
+ - - "~>"
298
296
  - !ruby/object:Gem::Version
299
- version: '0'
297
+ version: '1.4'
300
298
  type: :development
301
299
  prerelease: false
302
300
  version_requirements: !ruby/object:Gem::Requirement
303
301
  requirements:
304
- - - ">="
302
+ - - "~>"
305
303
  - !ruby/object:Gem::Version
306
- version: '0'
304
+ version: '1.4'
307
305
  - !ruby/object:Gem::Dependency
308
306
  name: rb-fsevent
309
307
  requirement: !ruby/object:Gem::Requirement
310
308
  requirements:
311
- - - ">="
309
+ - - "~>"
312
310
  - !ruby/object:Gem::Version
313
311
  version: '0'
314
312
  type: :development
315
313
  prerelease: false
316
314
  version_requirements: !ruby/object:Gem::Requirement
317
315
  requirements:
318
- - - ">="
316
+ - - "~>"
319
317
  - !ruby/object:Gem::Version
320
318
  version: '0'
321
319
  - !ruby/object:Gem::Dependency
322
320
  name: terminal-notifier-guard
323
321
  requirement: !ruby/object:Gem::Requirement
324
322
  requirements:
325
- - - ">="
323
+ - - "~>"
326
324
  - !ruby/object:Gem::Version
327
- version: '0'
325
+ version: '1.5'
328
326
  type: :development
329
327
  prerelease: false
330
328
  version_requirements: !ruby/object:Gem::Requirement
331
329
  requirements:
332
- - - ">="
330
+ - - "~>"
333
331
  - !ruby/object:Gem::Version
334
- version: '0'
332
+ version: '1.5'
335
333
  description: Pacto is a judge that arbitrates contract disputes between a service
336
334
  provider and one or more consumers. In other words, it is a framework for Integration
337
335
  Contract Testing, and helps guide service evolution patterns like Consumer-Driven
@@ -349,6 +347,7 @@ files:
349
347
  - ".ruby-gemset"
350
348
  - ".ruby-version"
351
349
  - ".travis.yml"
350
+ - CHANGELOG
352
351
  - CONTRIBUTING.md
353
352
  - Gemfile
354
353
  - Guardfile