pacto 0.3.0 → 0.3.1

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