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

Commit

Permalink
Merge pull request #32 from thoughtworks/issue32
Browse files Browse the repository at this point in the history
Pacto hooks
  • Loading branch information
maxlinc committed Oct 1, 2013
2 parents c549be2 + 6ec4985 commit 2e654f9
Show file tree
Hide file tree
Showing 22 changed files with 168 additions and 73 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.

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
9 changes: 6 additions & 3 deletions lib/pacto/contract.rb
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
module Pacto
class Contract
attr_reader :values

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

def stub!
@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 = {})
Expand Down
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
18 changes: 11 additions & 7 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 @@ -32,9 +30,15 @@ def unregister_all!
end

def contract_for(request_signature)
registered.values.inject(Set.new) do |result, contract_set|
result.merge(contract_set.keep_if { |c| c.matches? 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

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

Expand Down
11 changes: 11 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,14 @@
require 'pacto'
require 'pacto/server'
require 'stringio'

RSpec.configure do |config|
# I'd like this to be before :each, but there is an issue with one test
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 'should bind the request' do
contracts = Set.new
mock_erb({ :req => converted_req })
described_class.new.process contracts, req, res
res.body.should == 'after'
end
end

context 'one matching contract' do
it 'should bind 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
res.body.should == 'after'
end
end

context 'multiple matching contracts' do
it 'should bind 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
res.body.should == 'after'
end
end
end

def mock_erb(hash)
Pacto::ERBProcessor.any_instance.should_receive(:process).with('before', hash).and_return('after')
end
end
10 changes: 5 additions & 5 deletions spec/unit/pacto/contract_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ module Pacto
Pacto.configuration.provider = provider
end

describe '#stub!' do
describe '#stub_contract!' do
it 'instantiates the response and registers a stub' do
response.should_receive :instantiate
provider.should_receive(:stub!).with request, instantiated_response
contract.stub!
provider.should_receive(:stub_request!).with request, instantiated_response
contract.stub_contract!
end
end

Expand Down Expand Up @@ -67,8 +67,8 @@ module Pacto

context 'when the contract is stubbed' do
it 'returns true if it matches the request' do
provider.should_receive(:stub!).with(request, instantiated_response).and_return(request_matcher)
contract.stub!
provider.should_receive(:stub_request!).with(request, instantiated_response).and_return(request_matcher)
contract.stub_contract!
expect(contract.matches? request_signature).to be_true
expect(contract.matches? :anything).to be_false
end
Expand Down
8 changes: 8 additions & 0 deletions spec/unit/pacto/core/configuration_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,13 @@
end
Pacto.configuration.contracts_path.should eql(contracts_path)
end

it 'register a Pacto Callback' do
callback_block = Pacto::Callback.new { }
Pacto.configure do |c|
c.register_callback(callback_block)
end
Pacto.configuration.callback.should eq(callback_block)
end
end
end
Loading

0 comments on commit 2e654f9

Please sign in to comment.