require 'pact/matchers' require 'pact/reification' module Pact module Request class NullExpectation def to_s "" end def ==(other_object) other_object.is_a? NullExpectation end def ===(other_object) other_object.is_a? NullExpectation end def eql?(other_object) self == other_object end def hash 2934820948209428748274238642672 end def empty? true end def nil? true end end class Base include Pact::Matchers extend Pact::Matchers NULL_EXPECTATION = NullExpectation.new attr_reader :method, :path, :headers, :body, :query, :options def self.from_hash(hash) sym_hash = symbolize_keys hash method = sym_hash.fetch(:method) path = sym_hash.fetch(:path) query = sym_hash.fetch(:query, NULL_EXPECTATION) headers = sym_hash.fetch(:headers, NULL_EXPECTATION) body = sym_hash.fetch(:body, NULL_EXPECTATION) new(method, path, headers, body, query) end def self.symbolize_keys hash hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo } end def initialize(method, path, headers, body, query) @method = method.to_s @path = path.chomp('/') @headers = headers @body = body @query = query end def to_json(options = {}) as_json.to_json(options) end def as_json base_json = { method: method, path: path, } base_json.merge!(body: body) unless body.is_a? NullExpectation base_json.merge!(headers: headers) unless headers.is_a? NullExpectation base_json.merge!(query: query) unless query.is_a? NullExpectation base_json end def as_json_without_body keep_keys = [:method, :path, :headers, :query] as_json.reject{ |key, value| !keep_keys.include? key } end def short_description "#{method} #{full_path}" end def full_path fp = '' if path.empty? fp << "/" else fp << path end if query && !query.empty? fp << ("?" + query) end fp end end class Expected < Base DEFAULT_OPTIONS = {:allow_unexpected_keys => false}.freeze attr_accessor :description attr_accessor :options def self.from_hash(hash) request = super request.description = hash.fetch(:description, nil) request.options = symbolize_keys(hash).fetch(:options, {}) request end def initialize(method, path, headers, body, query, options = {}) super(method, path, headers, body, query) @options = options end def match(actual_request) difference(actual_request).empty? end def matches_route? actual_request diff({:method => method, :path => path}, {:method => actual_request.method, :path => actual_request.path}).empty? end def difference(actual_request) request_diff = diff(as_json_without_body, actual_request.as_json_without_body) unless body.is_a? NullExpectation request_diff.merge(body_difference(actual_request.body)) else request_diff end end def body_difference(actual_body) diff({:body => body}, {body: actual_body}, allow_unexpected_keys: runtime_options[:allow_unexpected_keys_in_body]) end def as_json_with_options as_json.merge( options.empty? ? {} : { options: options} ) end def generated_body Pact::Reification.from_term(body) end # Don't want to put the default options in the pact json just yet, so calculating these at run time, rather than assigning # the result to @options def runtime_options DEFAULT_OPTIONS.merge(self.class.symbolize_keys(options)) end end class Actual < Base end end end