summaryrefslogtreecommitdiffstats
path: root/spec/unit/network/xmlrpc/client_spec.rb
blob: 1b97583a7d16b7772560a64b01147f557f30a598 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
#!/usr/bin/env ruby

Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f) : Dir.chdir("..") { s.call(f) } }).call("spec/spec_helper.rb") }

describe Puppet::Network::XMLRPCClient do
    describe "when performing the rpc call" do
        before do
            Puppet::SSL::Host.any_instance.stubs(:certificate_matches_key?).returns true
            @client = Puppet::Network::Client.report.xmlrpc_client.new
            @client.stubs(:call).returns "foo"
        end

        it "should call the specified namespace and method, with the specified arguments" do
            @client.expects(:call).with("puppetreports.report", "eh").returns "foo"
            @client.report("eh")
        end

        it "should return the results from the call" do
            @client.expects(:call).returns "foo"
            @client.report("eh").should == "foo"
        end

        it "should always close the http connection if it is still open after the call" do
            http = mock 'http'
            @client.stubs(:http).returns http

            http.expects(:started?).returns true
            http.expects(:finish)

            @client.report("eh").should == "foo"
        end

        it "should always close the http connection if it is still open after a call that raises an exception" do
            http = mock 'http'
            @client.stubs(:http).returns http

            @client.expects(:call).raises RuntimeError

            http.expects(:started?).returns true
            http.expects(:finish)

            lambda { @client.report("eh") }.should raise_error
        end

        describe "when returning the http instance" do
            it "should use the http pool to create the instance" do
                @client.instance_variable_set("@http", nil)
                @client.expects(:host).returns "myhost"
                @client.expects(:port).returns "myport"
                Puppet::Network::HttpPool.expects(:http_instance).with("myhost", "myport", true).returns "http"

                @client.http.should == "http"
            end

            it "should reuse existing instances" do
                @client.http.should equal(@client.http)
            end
        end

        describe "when recycling the connection" do
            it "should close the existing instance if it's open" do
                http = mock 'http'
                @client.stubs(:http).returns http

                http.expects(:started?).returns true
                http.expects(:finish)

                @client.recycle_connection
            end

            it "should force creation of a new instance" do
                Puppet::Network::HttpPool.expects(:http_instance).returns "second_http"

                @client.recycle_connection

                @client.http.should == "second_http"
            end
        end

        describe "and an exception is raised" do
            it "should raise XMLRPCClientError if XMLRPC::FaultException is raised" do
                error = XMLRPC::FaultException.new("foo", "bar")

                @client.expects(:call).raises(error)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should raise XMLRPCClientError if Errno::ECONNREFUSED is raised" do
                @client.expects(:call).raises(Errno::ECONNREFUSED)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should log and raise XMLRPCClientError if Timeout::Error is raised" do
                Puppet.expects(:err)
                @client.expects(:call).raises(Timeout::Error)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should log and raise XMLRPCClientError if SocketError is raised" do
                Puppet.expects(:err)
                @client.expects(:call).raises(SocketError)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should log, recycle the connection, and retry if Errno::EPIPE is raised" do
                @client.expects(:call).times(2).raises(Errno::EPIPE).then.returns "eh"

                Puppet.expects(:info)
                @client.expects(:recycle_connection)

                @client.report("eh")
            end

            it "should log, recycle the connection, and retry if EOFError is raised" do
                @client.expects(:call).times(2).raises(EOFError).then.returns "eh"

                Puppet.expects(:info)
                @client.expects(:recycle_connection)

                @client.report("eh")
            end

            it "should log and retry if an exception containing 'Wrong size' is raised" do
                error = RuntimeError.new("Wrong size. Was 15, should be 30")
                @client.expects(:call).times(2).raises(error).then.returns "eh"

                Puppet.expects(:warning)

                @client.report("eh")
            end

            it "should raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised" do
                @client.expects(:call).raises(OpenSSL::SSL::SSLError)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should log and raise XMLRPCClientError if OpenSSL::SSL::SSLError is raised with certificate issues" do
                error = OpenSSL::SSL::SSLError.new("hostname was not match")
                @client.expects(:call).raises(error)

                Puppet.expects(:warning)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end

            it "should log, recycle the connection, and retry if OpenSSL::SSL::SSLError is raised containing 'bad write retry'" do
                error = OpenSSL::SSL::SSLError.new("bad write retry")
                @client.expects(:call).times(2).raises(error).then.returns "eh"

                @client.expects(:recycle_connection)

                Puppet.expects(:warning)

                @client.report("eh")
            end

            it "should log and raise XMLRPCClientError if any other exception is raised" do
                @client.expects(:call).raises(RuntimeError)

                Puppet.expects(:err)

                lambda { @client.report("eh") }.should raise_error(Puppet::Network::XMLRPCClientError)
            end
        end
    end
end