module RSpec::Mocks
  RSpec.describe "PartialDoubleUsingMocksDirectly" do
    let(:klass) do
      Class.new do
        module MethodMissing
          remove_method :method_missing rescue nil
          def method_missing(m, *a, &b)
            if m == :captured_by_method_missing
              "response generated by method missing"
            else
              super(m, *a, &b)
            end
          end
        end

        extend MethodMissing
        include MethodMissing

        def existing_method
          :original_value
        end

      end
    end

    let(:obj) { klass.new }

    it "fails when expected message is not received" do
      expect(obj).to receive(:msg)
      expect {
        verify obj
      }.to fail
    end

    it "fails when message is received with incorrect args" do
      expect(obj).to receive(:msg).with(:correct_arg)
      expect {
        obj.msg(:incorrect_arg)
      }.to fail
      obj.msg(:correct_arg)
    end

    it "passes when expected message is received" do
      expect(obj).to receive(:msg)
      obj.msg
      verify obj
    end

    it "passes when message is received with correct args" do
      expect(obj).to receive(:msg).with(:correct_arg)
      obj.msg(:correct_arg)
      verify obj
    end

    it "restores the original method if it existed" do
      expect(obj.existing_method).to equal(:original_value)
      expect(obj).to receive(:existing_method).and_return(:mock_value)
      expect(obj.existing_method).to equal(:mock_value)
      verify obj
      expect(obj.existing_method).to equal(:original_value)
    end

    context "with an instance method handled by method_missing" do
      it "restores the original behavior" do
        expect(obj.captured_by_method_missing).to eq("response generated by method missing")
        allow(obj).to receive(:captured_by_method_missing) { "foo" }
        expect(obj.captured_by_method_missing).to eq("foo")
        reset obj
        expect(obj.captured_by_method_missing).to eq("response generated by method missing")
      end
    end

    context "with a class method handled by method_missing" do
      it "restores the original behavior" do
        expect(klass.captured_by_method_missing).to eq("response generated by method missing")
        allow(klass).to receive(:captured_by_method_missing) { "foo" }
        expect(klass.captured_by_method_missing).to eq("foo")
        reset klass
        expect(klass.captured_by_method_missing).to eq("response generated by method missing")
      end
    end
  end
end
