lib/airborne/request_expectations.rb in airborne-0.1.20 vs lib/airborne/request_expectations.rb in airborne-0.2.0
- old
+ new
@@ -65,81 +65,109 @@
expect(header.downcase).to include(content.downcase)
else
expect(header.downcase).to eq(content.downcase)
end
else
- fail RSpec::Expectations::ExpectationNotMetError, "Header #{key} not present in HTTP response"
+ fail RSpec::Expectations::ExpectationNotMetError, "Header #{key} not present in the HTTP response"
end
end
- def call_with_path(args)
- if args.length == 2
- get_by_path(args[0], json_body) do|json_chunk|
- yield(args[1], json_chunk)
- end
- else
- yield(args[0], json_body)
- end
- end
+ def expect_json_impl(expected, actual)
+ return if nil_optional_hash?(expected, actual)
- def get_mapper
- base_mapper = {
- integer: [Fixnum, Bignum],
- array_of_integers: [Fixnum, Bignum],
- int: [Fixnum, Bignum],
- array_of_ints: [Fixnum, Bignum],
- float: [Float, Fixnum, Bignum],
- array_of_floats: [Float, Fixnum, Bignum],
- string: [String],
- array_of_strings: [String],
- boolean: [TrueClass, FalseClass],
- array_of_booleans: [TrueClass, FalseClass],
- bool: [TrueClass, FalseClass],
- array_of_bools: [TrueClass, FalseClass],
- object: [Hash],
- array_of_objects: [Hash],
- array: [Array],
- array_of_arrays: [Array],
- date: [DateTime],
- null: [NilClass]
- }
+ actual = actual.to_s if expected.class == Regexp
- mapper = base_mapper.clone
- base_mapper.each do |key, value|
- mapper[(key.to_s + '_or_null').to_sym] = value + [NilClass]
+ return expect(actual).to match(expected) if property?(expected)
+
+ keys = []
+
+ keys << expected.keys if match_expected?
+ keys << actual.keys if match_actual?
+ keys = expected.keys & actual.keys if match_none?
+
+ keys.flatten.uniq.each do |prop|
+ expected_value = extract_expected(expected, prop)
+ actual_value = extract_actual(actual, prop)
+
+ next expect_json_impl(expected_value, actual_value) if hash?(expected_value)
+ next expected_value.call(actual_value) if expected_value.is_a?(Proc)
+ next expect(actual_value.to_s).to match(expected_value) if expected_value.is_a?(Regexp)
+
+ expect(actual_value).to eq(expected_value)
end
- mapper
end
- def expect_json_types_impl(expectations, hash_or_value)
- return if nil_optional_hash?(expectations, hash_or_value)
+ def expect_json_types_impl(expected, actual)
+ return if nil_optional_hash?(expected, actual)
@mapper ||= get_mapper
- hash_or_value = convert_to_date(hash_or_value) if expectations == :date
+ actual = convert_to_date(actual) if expected == :date
- return expect_type(expectations, hash_or_value.class) if expectations.class == Symbol
- return expectations.call(hash_or_value) if expectations.class == Proc
+ return expect_type(expected, actual.class) if expected.is_a?(Symbol)
+ return expected.call(actual) if expected.is_a?(Proc)
- expectations.each do |prop_name, expected_type|
- value = ensure_hash_contains_prop(prop_name, hash_or_value) do
- expected_type == :date ? convert_to_date(hash_or_value[prop_name]) : hash_or_value[prop_name]
- end
- expected_class = expected_type.class
- value_class = value.class
+ keys = []
- next expect_json_types_impl(expected_type, value) if hash?(expected_class)
- next expected_type.call(value) if expected_class == Proc
+ keys << expected.keys if match_expected?
+ keys << actual.keys if match_actual?
+ keys = expected.keys & actual.keys if match_none?
- if expected_type.to_s.include?('array_of')
- check_array_types(value, value_class, prop_name, expected_type)
+ keys.flatten.uniq.each do |prop|
+ type = extract_expected(expected, prop)
+ value = extract_actual(actual, prop)
+ value = convert_to_date(value) if type == :date
+
+ next expect_json_types_impl(type, value) if hash?(type)
+ next type.call(value) if type.is_a?(Proc)
+
+ val_class = value.class
+
+ if type.to_s.include?('array_of')
+ check_array_types(value, val_class, prop, type)
else
- expect_type(expected_type, value_class, prop_name)
+ expect_type(type, val_class, prop)
end
end
end
+ def call_with_path(args)
+ if args.length == 2
+ get_by_path(args[0], json_body) do |json_chunk|
+ yield(args[1], json_chunk)
+ end
+ else
+ yield(args[0], json_body)
+ end
+ end
+
+ def extract_expected(expected, prop)
+ begin
+ type = expected[prop]
+ type.nil? ? raise : type
+ rescue
+ raise ExpectationError, "Expectation is expected to contain property: #{prop}"
+ end
+ end
+
+ def extract_actual(actual, prop)
+ begin
+ value = actual[prop]
+ rescue
+ raise ExpectationError, "Expected #{actual.class} #{actual}\nto be an object with property #{prop}"
+ end
+ end
+
+ def expect_type(expected_type, value_class, prop_name = nil)
+ fail ExpectationError, "Expected type #{expected_type}\nis an invalid type" if @mapper[expected_type].nil?
+
+ insert = prop_name.nil? ? '' : "#{prop_name} to be of type"
+ message = "Expected #{insert} #{expected_type}\n got #{value_class} instead"
+
+ expect(@mapper[expected_type].include?(value_class)).to eq(true), message
+ end
+
def convert_to_date(value)
begin
DateTime.parse(value)
rescue
end
@@ -150,50 +178,22 @@
value.each do |val|
expect_type(expected_type, val.class, prop_name)
end
end
- def nil_optional_hash?(expectations, hash)
- expectations.class == Airborne::OptionalHashTypeExpectations && hash.nil?
+ def nil_optional_hash?(expected, hash)
+ expected.is_a?(Airborne::OptionalHashTypeExpectations) && hash.nil?
end
- def expect_type(expected_type, value_class, prop_name = nil)
- insert = prop_name.nil? ? '' : "#{prop_name} to be of type"
- msg = "Expected #{insert} #{expected_type}\n got #{value_class} instead"
- fail ExpectationError, "Expected type #{expected_type}\nis an invalid type" if @mapper[expected_type].nil?
- expect(@mapper[expected_type].include?(value_class)).to eq(true), msg
+ def hash?(hash)
+ hash.is_a?(Hash) || hash.is_a?(Airborne::OptionalHashTypeExpectations)
end
- def hash?(expected_class)
- expected_class == Hash || expected_class == Airborne::OptionalHashTypeExpectations
- end
-
def expect_array(value_class, prop_name, expected_type)
expect(value_class).to eq(Array), "Expected #{prop_name}\n to be of type #{expected_type}\n got #{value_class} instead"
end
- def expect_json_impl(expectations, hash)
- hash = hash.to_s if expectations.class == Regexp
- return expect(hash).to match(expectations) if property?(expectations)
- expectations.each do |prop_name, expected_value|
- actual_value = ensure_hash_contains_prop(prop_name, hash) { hash[prop_name] }
- expected_class = expected_value.class
- next expect(actual_value).to match(expected_value) if expected_class == Hash
- next expected_value.call(actual_value) if expected_class == Proc
- next expect(actual_value.to_s).to match(expected_value) if expected_class == Regexp
- expect(actual_value).to eq(expected_value)
- end
- end
-
- def ensure_hash_contains_prop(prop_name, hash)
- begin
- yield
- rescue
- raise ExpectationError, "Expected #{hash.class} #{hash}\nto be an object with property #{prop_name}"
- end
- end
-
def convert_expectations_for_json_sizes(old_expectations)
unless old_expectations.is_a?(Hash)
return convert_expectation_for_json_sizes(old_expectations)
end
@@ -209,19 +209,68 @@
def convert_expectation_for_json_sizes(expected_size)
->(data) { expect(data.size).to eq(expected_size) }
end
+ def ensure_hash_contains_prop(prop_name, hash)
+ begin
+ yield
+ rescue
+ raise ExpectationError, "Expected #{hash.class} #{hash}\nto be an object with property #{prop_name}"
+ end
+ end
+
def property?(expectations)
[String, Regexp, Float, Fixnum, Bignum, TrueClass, FalseClass, NilClass].include?(expectations.class)
end
+ def get_mapper
+ base_mapper = {
+ integer: [Fixnum, Bignum],
+ array_of_integers: [Fixnum, Bignum],
+ int: [Fixnum, Bignum],
+ array_of_ints: [Fixnum, Bignum],
+ float: [Float, Fixnum, Bignum],
+ array_of_floats: [Float, Fixnum, Bignum],
+ string: [String],
+ array_of_strings: [String],
+ boolean: [TrueClass, FalseClass],
+ array_of_booleans: [TrueClass, FalseClass],
+ bool: [TrueClass, FalseClass],
+ array_of_bools: [TrueClass, FalseClass],
+ object: [Hash],
+ array_of_objects: [Hash],
+ array: [Array],
+ array_of_arrays: [Array],
+ date: [DateTime],
+ null: [NilClass]
+ }
+
+ mapper = base_mapper.clone
+ base_mapper.each do |key, value|
+ mapper[(key.to_s + '_or_null').to_sym] = value + [NilClass]
+ end
+ mapper
+ end
+
def resolve_status(candidate, authority)
candidate = Rack::Utils::SYMBOL_TO_STATUS_CODE[candidate] if candidate.is_a?(Symbol)
case authority
when String then candidate.to_s
when Fixnum then candidate.to_i
else candidate
end
+ end
+
+ def match_none?
+ !match_actual? && !match_expected?
+ end
+
+ def match_actual?
+ Airborne.configuration.match_actual?
+ end
+
+ def match_expected?
+ Airborne.configuration.match_expected?
end
end
end