spec/contracts_spec.rb in contracts-0.7 vs spec/contracts_spec.rb in contracts-0.8

- old
+ new

@@ -3,13 +3,13 @@ @o = GenericExample.new end describe "basic" do it "should fail for insufficient arguments" do - expect { + expect do @o.hello - }.to raise_error + end.to raise_error end it "should fail for insufficient contracts" do expect { @o.bad_double(2) }.to raise_error(ContractError) end @@ -21,100 +21,110 @@ let(:expected_decorated_string) { "Hello, world!" } subject { PatternMatchingExample.new } it "should work as expected when there is no contract violation" do expect( - subject.process_request(PatternMatchingExample::Success[string_with_hello]) - ).to eq(PatternMatchingExample::Success[expected_decorated_string]) + subject.process_request(PatternMatchingExample::Success.new(string_with_hello)) + ).to eq(PatternMatchingExample::Success.new(expected_decorated_string)) expect( subject.process_request(PatternMatchingExample::Failure.new) ).to be_a(PatternMatchingExample::Failure) end it "should not fall through to next pattern when there is a deep contract violation" do expect(PatternMatchingExample::Failure).not_to receive(:is_a?) - expect { - subject.process_request(PatternMatchingExample::Success[string_without_hello]) - }.to raise_error(ContractError) + expect do + subject.process_request(PatternMatchingExample::Success.new(string_without_hello)) + end.to raise_error(ContractError) end it "should fail when the pattern-matched method's contract fails" do - expect { + expect do subject.process_request("bad input") - }.to raise_error(ContractError) + end.to raise_error(ContractError) end + it "should work for differing arities" do + expect( + subject.do_stuff(1, "abc", 2) + ).to eq("bar") + + expect( + subject.do_stuff(3, "def") + ).to eq("foo") + end + context "when failure_callback was overriden" do before do ::Contract.override_failure_callback do |_data| - raise RuntimeError, "contract violation" + fail "contract violation" end end it "calls a method when first pattern matches" do expect( - subject.process_request(PatternMatchingExample::Success[string_with_hello]) - ).to eq(PatternMatchingExample::Success[expected_decorated_string]) + subject.process_request(PatternMatchingExample::Success.new(string_with_hello)) + ).to eq(PatternMatchingExample::Success.new(expected_decorated_string)) end it "falls through to 2nd pattern when first pattern does not match" do expect( subject.process_request(PatternMatchingExample::Failure.new) ).to be_a(PatternMatchingExample::Failure) end it "uses overriden failure_callback when pattern matching fails" do - expect { + expect do subject.process_request("hello") - }.to raise_error(RuntimeError, /contract violation/) + end.to raise_error(RuntimeError, /contract violation/) end end end describe "usage in singleton class" do it "should work normally when there is no contract violation" do expect(SingletonClassExample.hoge("hoge")).to eq("superhoge") end it "should fail with proper error when there is contract violation" do - expect { + expect do SingletonClassExample.hoge(3) - }.to raise_error(ContractError, /Expected: String/) + end.to raise_error(ContractError, /Expected: String/) end context "when owner class does not include Contracts" do - let(:error) { + let(:error) do # NOTE Unable to support this user-friendly error for ruby # 1.8.7 and jruby 1.8, 1.9 it has much less support for # singleton inheritance hierarchy if Contracts::Support.eigenclass_hierarchy_supported? [Contracts::ContractsNotIncluded, Contracts::ContractsNotIncluded::DEFAULT_MESSAGE] else [NoMethodError, /undefined method `Contract'/] end - } + end it "fails with descriptive error" do - expect { + expect do Class.new(GenericExample) do class << self Contract String => String def hoge(name) "super#{name}" end end end - }.to raise_error(*error) + end.to raise_error(*error) end end describe "builtin contracts usage" do it "allows to use builtin contracts without namespacing and redundant Contracts inclusion" do - expect { + expect do SingletonClassExample.add("55", 5.6) - }.to raise_error(ContractError, /Expected: Contracts::Num/) + end.to raise_error(ContractError, /Expected: Num/) end end end describe "no contracts feature" do @@ -234,24 +244,24 @@ describe "instance methods" do it "should allow two classes to have the same method with different contracts" do a = A.new b = B.new - expect { + expect do a.triple(5) b.triple("a string") - }.to_not raise_error + end.to_not raise_error end end describe "instance and class methods" do it "should allow a class to have an instance method and a class method with the same name" do a = A.new - expect { + expect do a.instance_and_class_method(5) A.instance_and_class_method("a string") - }.to_not raise_error + end.to_not raise_error end end describe "class methods" do it "should pass for correct input" do @@ -311,93 +321,131 @@ end end describe "Hashes" do it "should pass for exact correct input" do - expect { @o.person({:name => "calvin", :age => 10}) }.to_not raise_error + expect { @o.person(:name => "calvin", :age => 10) }.to_not raise_error end it "should pass even if some keys don't have contracts" do - expect { @o.person({:name => "calvin", :age => 10, :foo => "bar"}) }.to_not raise_error + expect { @o.person(:name => "calvin", :age => 10, :foo => "bar") }.to_not raise_error end it "should fail if a key with a contract on it isn't provided" do - expect { @o.person({:name => "calvin"}) }.to raise_error(ContractError) + expect { @o.person(:name => "calvin") }.to raise_error(ContractError) end it "should fail for incorrect input" do - expect { @o.person({:name => 50, :age => 10}) }.to raise_error(ContractError) + expect { @o.person(:name => 50, :age => 10) }.to raise_error(ContractError) end end describe "blocks" do it "should pass for correct input" do - expect { @o.do_call { - 2 + 2 - }}.to_not raise_error + expect do + @o.do_call do + 2 + 2 + end + end.to_not raise_error end it "should fail for incorrect input" do - expect { @o.do_call(nil) }.to raise_error(ContractError) + expect do + @o.do_call(nil) + end.to raise_error(ContractError) end it "should handle properly lack of block when there are other arguments" do - expect { @o.double_with_proc(4) }.to raise_error(ContractError, /Actual: nil/) + expect do + @o.double_with_proc(4) + end.to raise_error(ContractError, /Actual: nil/) end end describe "varargs" do it "should pass for correct input" do - expect { @o.sum(1, 2, 3) }.to_not raise_error + expect do + @o.sum(1, 2, 3) + end.to_not raise_error end it "should fail for incorrect input" do - expect { @o.sum(1, 2, "bad") }.to raise_error(ContractError) + expect do + @o.sum(1, 2, "bad") + end.to raise_error(ContractError) end + + it "should work with arg before splat" do + expect do + @o.arg_then_splat(3, "hello", "world") + end.to_not raise_error + end end describe "varargs with block" do it "should pass for correct input" do - expect { @o.with_partial_sums(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error - expect { @o.with_partial_sums_contracted(1, 2, 3) { |partial_sum| 2 * partial_sum + 1 } }.not_to raise_error + expect do + @o.with_partial_sums(1, 2, 3) do |partial_sum| + 2 * partial_sum + 1 + end + end.not_to raise_error + expect do + @o.with_partial_sums_contracted(1, 2, 3) do |partial_sum| + 2 * partial_sum + 1 + end + end.not_to raise_error end it "should fail for incorrect input" do - expect { - @o.with_partial_sums(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 } - }.to raise_error(ContractError, /Actual: "bad"/) + expect do + @o.with_partial_sums(1, 2, "bad") do |partial_sum| + 2 * partial_sum + 1 + end + end.to raise_error(ContractError, /Actual: "bad"/) - expect { + expect do @o.with_partial_sums(1, 2, 3) - }.to raise_error(ContractError, /Actual: nil/) + end.to raise_error(ContractError, /Actual: nil/) - expect { + expect do @o.with_partial_sums(1, 2, 3, lambda { |x| x }) - }.to raise_error(ContractError, /Actual: #<Proc/) + end.to raise_error(ContractError, /Actual: nil/) end context "when block has Func contract" do it "should fail for incorrect input" do - expect { + expect do @o.with_partial_sums_contracted(1, 2, "bad") { |partial_sum| 2 * partial_sum + 1 } - }.to raise_error(ContractError, /Actual: "bad"/) + end.to raise_error(ContractError, /Actual: "bad"/) - expect { + expect do @o.with_partial_sums_contracted(1, 2, 3) - }.to raise_error(ContractError, /Actual: nil/) + end.to raise_error(ContractError, /Actual: nil/) end end end describe "contracts on functions" do it "should pass for a function that passes the contract" do expect { @o.map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error end + it "should pass for a function that passes the contract as in tutorial" do + expect { @o.tutorial_map([1, 2, 3], lambda { |x| x + 1 }) }.to_not raise_error + end + it "should fail for a function that doesn't pass the contract" do - expect { @o.map([1, 2, 3], lambda { |x| "bad return value" }) }.to raise_error(ContractError) + expect { @o.map([1, 2, 3], lambda { |_| "bad return value" }) }.to raise_error(ContractError) end + + it "should pass for a function that passes the contract with weak other args" do + expect { @o.map_plain(["hello", "joe"], lambda { |x| x.size }) }.to_not raise_error + end + + it "should fail for a function that doesn't pass the contract with weak other args" do + expect { @o.map_plain(["hello", "joe"], lambda { |_| nil }) }.to raise_error(ContractError) + end end describe "default args to functions" do it "should work for a function call that relies on default args" do expect { @o.default_args }.to_not raise_error @@ -441,10 +489,81 @@ expect(res).to eq("badbad") end end end + describe "Contracts to_s formatting in expected" do + def not_s(match) + Regexp.new "[^\"\']#{match}[^\"\']" + end + + def delim(match) + "(#{match})" + end + + it "should not stringify native types" do + expect do + @o.constanty("bad", nil) + end.to raise_error(ContractError, not_s(123)) + + expect do + @o.constanty(123, "bad") + end.to raise_error(ContractError, not_s(nil)) + end + + it "should contain to_s representation within a Hash contract" do + expect do + @o.hash_complex_contracts(:rigged => "bad") + end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass")) + end + + it "should contain to_s representation within a nested Hash contract" do + expect do + @o.nested_hash_complex_contracts(:rigged => true, + :contents => { + :kind => 0, + :total => 42 }) + end.to raise_error(ContractError, not_s(delim "String or Symbol")) + end + + it "should contain to_s representation within an Array contract" do + expect do + @o.array_complex_contracts(["bad"]) + end.to raise_error(ContractError, not_s(delim "TrueClass or FalseClass")) + end + + it "should contain to_s representation within a nested Array contract" do + expect do + @o.nested_array_complex_contracts([true, [0]]) + end.to raise_error(ContractError, not_s(delim "String or Symbol")) + end + + it "should not contain Contracts:: module prefix" do + expect do + @o.double("bad") + end.to raise_error(ContractError, /Expected: Num/) + end + + it "should still show nils, not just blank space" do + expect do + @o.no_args("bad") + end.to raise_error(ContractError, /Expected: nil/) + end + + it 'should show empty quotes as ""' do + expect do + @o.no_args("") + end.to raise_error(ContractError, /Actual: ""/) + end + + it "should not use custom to_s if empty string" do + expect do + @o.using_empty_contract("bad") + end.to raise_error(ContractError, /Expected: EmptyCont/) + end + end + describe "functype" do it "should correctly print out a instance method's type" do expect(@o.functype(:double)).not_to eq("") end @@ -453,14 +572,24 @@ end end describe "private methods" do it "should raise an error if you try to access a private method" do - expect { @o.a_private_method }.to raise_error + expect { @o.a_private_method }.to raise_error(NoMethodError, /private/) end it "should raise an error if you try to access a private method" do - expect { @o.a_really_private_method }.to raise_error + expect { @o.a_really_private_method }.to raise_error(NoMethodError, /private/) + end + end + + describe "protected methods" do + it "should raise an error if you try to access a protected method" do + expect { @o.a_protected_method }.to raise_error(NoMethodError, /protected/) + end + + it "should raise an error if you try to access a protected method" do + expect { @o.a_really_protected_method }.to raise_error(NoMethodError, /protected/) end end describe "inherited methods" do it "should apply the contract to an inherited method" do