Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

Commit

Permalink
Merge branch 'master' of github.com:thoughtworks/pacto into remove_sh…
Browse files Browse the repository at this point in the history
…ould_from_specs

Conflicts:
	spec/spec_helper.rb
	spec/unit/pacto/core/contract_repository_spec.rb
  • Loading branch information
maxlinc committed Oct 1, 2013
2 parents 5b278e7 + 2e654f9 commit e2c4ff0
Show file tree
Hide file tree
Showing 23 changed files with 238 additions and 75 deletions.
2 changes: 0 additions & 2 deletions .rspec
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
--colour
--require spec_helper
--require integration/spec_helper
--require unit/spec_helper
4 changes: 0 additions & 4 deletions .rspec_integration

This file was deleted.

4 changes: 0 additions & 4 deletions .rspec_unit

This file was deleted.

3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,9 @@ contract = Pacto.build_from_file('/path/to/contract.json', 'http://dummyprovider
response = Net::HTTP.get_response(URI.parse('http://dummyprovider.com')).body
contract.validate response, body_only: true
```

Pacto also has the ability to match a request signature to a contract that is currently in used, via ```Pacto.contract_for request_signature```

## Auto-Generated Stubs

Pacto provides an API to be used in the consumer's acceptance tests. It uses a custom JSON Schema parser and generator
Expand Down
18 changes: 7 additions & 11 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,16 +18,12 @@ Cucumber::Rake::Task.new(:journeys) do |t|
t.cucumber_opts = 'features --format pretty'
end

if defined?(RSpec)
desc 'Run unit tests'
task :unit do
abort unless system('rspec --option .rspec_unit')
end

desc 'Run integration tests'
task :integration do
abort unless system('rspec --option .rspec_integration')
end
RSpec::Core::RakeTask.new(:unit) do |t|
t.pattern = 'spec/unit/**/*_spec.rb'
end

task :default => [:unit, :integration, :journeys, :rubocop, 'coveralls:push']
RSpec::Core::RakeTask.new(:integration) do |t|
t.pattern = 'spec/integration/**/*_spec.rb'
end

task :default => [:unit, :integration, :journeys, :rubocop, 'coveralls:push']
3 changes: 3 additions & 0 deletions lib/pacto.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

require 'pacto/core/contract_repository'
require 'pacto/core/configuration'
require 'pacto/core/callback'
require 'pacto/logger'
require 'pacto/exceptions/invalid_contract.rb'
require 'pacto/extensions'
Expand All @@ -25,6 +26,7 @@
require 'pacto/hash_merge_processor'
require 'pacto/stubs/built_in'
require 'pacto/meta_schema'
require 'pacto/hooks/erb_hook'

module Pacto
class << self
Expand All @@ -34,6 +36,7 @@ def configuration
end

def clear!
Pacto.configuration.provider.reset!
@configuration = nil
unregister_all!
end
Expand Down
15 changes: 12 additions & 3 deletions lib/pacto/contract.rb
Original file line number Diff line number Diff line change
@@ -1,18 +1,26 @@
module Pacto
class Contract
def initialize(request, response)
attr_reader :values

def initialize(request, response, file = nil)
@request = request
@response = response
@file = file
end

def stub!
Pacto.configuration.provider.stub!(@request, stub_response) unless @request.nil?
def stub_contract! values = {}
@values = values
@stub = Pacto.configuration.provider.stub_request!(@request, stub_response) unless @request.nil?
end

def validate(response_gotten = provider_response, opt = {})
@response.validate(response_gotten, opt)
end

def matches? request_signature
@stub.matches? request_signature unless @stub.nil?
end

private

def provider_response
Expand All @@ -22,5 +30,6 @@ def provider_response
def stub_response
@response.instantiate
end

end
end
2 changes: 1 addition & 1 deletion lib/pacto/contract_factory.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ def self.build_from_file(contract_path, host, preprocessor)
schema.validate definition
request = Request.new(host, definition['request'])
response = Response.new(definition['response'])
Contract.new(request, response)
Contract.new(request, response, contract_path)
end

def self.schema
Expand Down
11 changes: 11 additions & 0 deletions lib/pacto/core/callback.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
module Pacto
class Callback
def initialize(&block)
@callback = block
end

def process(contracts, request_signature, response)
@callback.call contracts, request_signature, response
end
end
end
11 changes: 11 additions & 0 deletions lib/pacto/core/configuration.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
module Pacto
class Configuration
attr_accessor :preprocessor, :postprocessor, :provider, :strict_matchers, :contracts_path, :logger
attr_reader :callback

def initialize
@preprocessor = ERBProcessor.new
Expand All @@ -10,10 +11,20 @@ def initialize
@contracts_path = nil
@logger = Logger.instance
@logger.level = :debug if ENV['PACTO_DEBUG']
@callback = Pacto::Hooks::ERBHook.new
end

def register_contract(contract = nil, *tags)
Pacto.register_contract(contract, *tags)
end

def register_callback(callback = nil, &block)
if block_given?
@callback = Pacto::Callback.new(&block)
else
raise 'Expected a Pacto::Callback' unless callback.is_a? Pacto::Callback
@callback = callback
end
end
end
end
20 changes: 15 additions & 5 deletions lib/pacto/core/contract_repository.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,13 @@ def register_contract(contract = nil, *tags)
registered.count - start_count
end

def use(tag, values = nil)
merged_contracts = registered[:default].merge registered[tag]
def use(tag, values = {})
merged_contracts = registered[:default] + registered[tag]

raise ArgumentError, "contract \"#{tag}\" not found" if merged_contracts.empty?

configuration.provider.values = values

merged_contracts.each do |contract|
contract.stub!
contract.stub_contract! values
end
merged_contracts.count
end
Expand All @@ -30,5 +28,17 @@ def registered
def unregister_all!
registered.clear
end

def contract_for(request_signature)
matches = Set.new
registered.values.each do |contract_set|
contract_set.each do |contract|
if contract.matches? request_signature
matches.add contract
end
end
end
matches
end
end
end
17 changes: 17 additions & 0 deletions lib/pacto/hooks/erb_hook.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
module Pacto
module Hooks
class ERBHook < Pacto::Callback
def initialize
@processor = ERBProcessor.new
end

def process(contracts, request_signature, response)
bound_values = contracts.empty? ? {} : contracts.first.values
bound_values.merge!({:req => { 'HEADERS' => request_signature.headers}})
response.body = @processor.process response.body, bound_values
response.body
end

end
end
end
21 changes: 6 additions & 15 deletions lib/pacto/stubs/built_in.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
module Pacto
module Stubs
class BuiltIn
attr_accessor :values

def initialize
register_callbacks
end

def stub! request, response
def stub_request! request, response
stub = WebMock.stub_request(request.method, "#{request.host}#{request.path}")
stub = stub.with(request_details(request)) if Pacto.configuration.strict_matchers
stub.to_return({
Expand All @@ -17,28 +16,20 @@ def stub! request, response
})
end

def process(request_signature, response)
unless processor.nil?
bound_values = {}
bound_values.merge!({:req => {'HEADERS' => request_signature.headers}}) if processor.class == ERBProcessor
bound_values.merge! @values unless @values.nil?
response.body = processor.process response.body, bound_values
end
response.body
def reset!
WebMock.reset!
WebMock.reset_callbacks
end

private

def register_callbacks
WebMock.after_request do |request_signature, response|
process request_signature, response
contracts = Pacto.contract_for request_signature
Pacto.configuration.callback.process contracts, request_signature, response
end
end

def processor
Pacto.configuration.postprocessor
end

def format_body(body)
if body.is_a?(Hash) || body.is_a?(Array)
body.to_json
Expand Down
6 changes: 4 additions & 2 deletions spec/integration/e2e_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,21 @@

context 'Journey' do
it 'stubs multiple services with a single use' do

Pacto.configure do |c|
c.strict_matchers = false
c.postprocessor = Pacto::ERBProcessor.new
c.preprocessor = nil
c.register_callback Pacto::Hooks::ERBHook.new
end

# Preprocessor must be off before building!
login_contract = Pacto.build_from_file(contract_path, 'http://dummyprovider.com')
contract = Pacto.build_from_file(strict_contract_path, 'http://dummyprovider.com')

Pacto.configure do |c|
c.register_contract login_contract, :default
c.register_contract contract, :devices
end

Pacto.use(:devices, {:device_id => 42})

raw_response = HTTParty.get('http://dummyprovider.com/hello', headers: {'Accept' => 'application/json' })
Expand Down
Empty file removed spec/integration/spec_helper.rb
Empty file.
4 changes: 4 additions & 0 deletions spec/integration/templating_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@
c.preprocessor = nil
c.postprocessor = nil
c.strict_matchers = false
c.register_callback do |contracts, req, res|
res
end
end

expect(response.keys).to eq ['message']
Expand All @@ -41,6 +44,7 @@
it 'processes erb on each request' do
Pacto.configure do |c|
c.preprocessor = nil
c.strict_matchers = false
c.postprocessor = Pacto::ERBProcessor.new
end

Expand Down
7 changes: 7 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,11 @@
config.expect_with :rspec do |c|
c.syntax = :expect
end
config.before(:each) do
provider = Pacto.configuration.provider
unless provider.respond_to? :reset!
provider.stub(:reset!)
end
Pacto.clear!
end
end
51 changes: 51 additions & 0 deletions spec/unit/hooks/erb_hook_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
describe Pacto::Hooks::ERBHook do
describe '.process' do
let(:req) {
OpenStruct.new({:headers => {'User-Agent' => 'abcd'}})
}
let(:converted_req) {
{'HEADERS' => {'User-Agent' => 'abcd'}}
}
let(:res) {
OpenStruct.new({:body => 'before'})
}

before do
end

context 'no matching contracts' do
it 'binds the request' do
contracts = Set.new
mock_erb({ :req => converted_req })
described_class.new.process contracts, req, res
expect(res.body).to eq('after')
end
end

context 'one matching contract' do
it 'binds the request and the contract\'s values' do
contract = OpenStruct.new({:values => {:max => 'test'}})
contracts = Set.new([contract])
mock_erb({ :req => converted_req, :max => 'test'})
described_class.new.process contracts, req, res
expect(res.body).to eq('after')
end
end

context 'multiple matching contracts' do
it 'binds the request and the first contract\'s values' do
contract1 = OpenStruct.new({:values => {:max => 'test'}})
contract2 = OpenStruct.new({:values => {:mob => 'team'}})
res = OpenStruct.new({:body => 'before'})
mock_erb({ :req => converted_req, :max => 'test'})
contracts = Set.new([contract1, contract2])
described_class.new.process contracts, req, res
expect(res.body).to eq('after')
end
end
end

def mock_erb(hash)
Pacto::ERBProcessor.any_instance.should_receive(:process).with('before', hash).and_return('after')
end
end

0 comments on commit e2c4ff0

Please sign in to comment.