1import unittest 2 3from supervisor.tests.base import DummySupervisor 4from supervisor.tests.base import DummyRequest 5from supervisor.tests.base import DummySupervisorRPCNamespace 6 7from supervisor.compat import xmlrpclib 8from supervisor.compat import httplib 9 10class GetFaultDescriptionTests(unittest.TestCase): 11 def test_returns_description_for_known_fault(self): 12 from supervisor import xmlrpc 13 desc = xmlrpc.getFaultDescription(xmlrpc.Faults.SHUTDOWN_STATE) 14 self.assertEqual(desc, 'SHUTDOWN_STATE') 15 16 def test_returns_unknown_for_unknown_fault(self): 17 from supervisor import xmlrpc 18 desc = xmlrpc.getFaultDescription(999999) 19 self.assertEqual(desc, 'UNKNOWN') 20 21class RPCErrorTests(unittest.TestCase): 22 def _getTargetClass(self): 23 from supervisor.xmlrpc import RPCError 24 return RPCError 25 26 def _makeOne(self, code, extra=None): 27 return self._getTargetClass()(code, extra) 28 29 def test_sets_text_with_fault_name_only(self): 30 from supervisor import xmlrpc 31 e = self._makeOne(xmlrpc.Faults.FAILED) 32 self.assertEqual(e.text, 'FAILED') 33 34 def test_sets_text_with_fault_name_and_extra(self): 35 from supervisor import xmlrpc 36 e = self._makeOne(xmlrpc.Faults.FAILED, 'oops') 37 self.assertEqual(e.text, 'FAILED: oops') 38 39 def test___str___shows_code_and_text(self): 40 from supervisor import xmlrpc 41 e = self._makeOne(xmlrpc.Faults.NO_FILE, '/nonexistent') 42 self.assertEqual(str(e), 43 "code=%r, text='NO_FILE: /nonexistent'" % xmlrpc.Faults.NO_FILE 44 ) 45 46class XMLRPCMarshallingTests(unittest.TestCase): 47 def test_xmlrpc_marshal(self): 48 from supervisor import xmlrpc 49 data = xmlrpc.xmlrpc_marshal(1) 50 self.assertEqual(data, xmlrpclib.dumps((1,), methodresponse=True)) 51 fault = xmlrpclib.Fault(1, 'foo') 52 data = xmlrpc.xmlrpc_marshal(fault) 53 self.assertEqual(data, xmlrpclib.dumps(fault)) 54 55class XMLRPCHandlerTests(unittest.TestCase): 56 def _getTargetClass(self): 57 from supervisor.xmlrpc import supervisor_xmlrpc_handler 58 return supervisor_xmlrpc_handler 59 60 def _makeOne(self, supervisord, subinterfaces): 61 return self._getTargetClass()(supervisord, subinterfaces) 62 63 def test_ctor(self): 64 supervisor = DummySupervisor() 65 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 66 handler = self._makeOne(supervisor, subinterfaces) 67 self.assertEqual(handler.supervisord, supervisor) 68 from supervisor.xmlrpc import RootRPCInterface 69 self.assertEqual(handler.rpcinterface.__class__, RootRPCInterface) 70 71 def test_match(self): 72 class DummyRequest2: 73 def __init__(self, uri): 74 self.uri = uri 75 supervisor = DummySupervisor() 76 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 77 handler = self._makeOne(supervisor, subinterfaces) 78 self.assertEqual(handler.match(DummyRequest2('/RPC2')), True) 79 self.assertEqual(handler.match(DummyRequest2('/nope')), False) 80 81 def test_continue_request_nosuchmethod(self): 82 supervisor = DummySupervisor() 83 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 84 handler = self._makeOne(supervisor, subinterfaces) 85 data = xmlrpclib.dumps(('a', 'b'), 'supervisor.noSuchMethod') 86 request = DummyRequest('/what/ever', None, None, None) 87 handler.continue_request(data, request) 88 logdata = supervisor.options.logger.data 89 self.assertEqual(len(logdata), 2) 90 self.assertEqual(logdata[-2], 91 'XML-RPC method called: supervisor.noSuchMethod()') 92 self.assertEqual(logdata[-1], 93 ('XML-RPC method supervisor.noSuchMethod() returned fault: ' 94 '[1] UNKNOWN_METHOD')) 95 self.assertEqual(len(request.producers), 1) 96 xml_response = request.producers[0] 97 self.assertRaises(xmlrpclib.Fault, xmlrpclib.loads, xml_response) 98 99 def test_continue_request_methodsuccess(self): 100 supervisor = DummySupervisor() 101 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 102 handler = self._makeOne(supervisor, subinterfaces) 103 data = xmlrpclib.dumps((), 'supervisor.getAPIVersion') 104 request = DummyRequest('/what/ever', None, None, None) 105 handler.continue_request(data, request) 106 logdata = supervisor.options.logger.data 107 self.assertEqual(len(logdata), 2) 108 self.assertEqual(logdata[-2], 109 'XML-RPC method called: supervisor.getAPIVersion()') 110 self.assertEqual(logdata[-1], 111 'XML-RPC method supervisor.getAPIVersion() returned successfully') 112 self.assertEqual(len(request.producers), 1) 113 xml_response = request.producers[0] 114 response = xmlrpclib.loads(xml_response) 115 from supervisor.rpcinterface import API_VERSION 116 self.assertEqual(response[0][0], API_VERSION) 117 self.assertEqual(request._done, True) 118 self.assertEqual(request.headers['Content-Type'], 'text/xml') 119 self.assertEqual(request.headers['Content-Length'], len(xml_response)) 120 121 def test_continue_request_no_params_in_request(self): 122 supervisor = DummySupervisor() 123 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 124 handler = self._makeOne(supervisor, subinterfaces) 125 data = '<?xml version="1.0" encoding="UTF-8"?>' \ 126 '<methodCall>' \ 127 '<methodName>supervisor.getAPIVersion</methodName>' \ 128 '</methodCall>' 129 request = DummyRequest('/what/ever', None, None, None) 130 handler.continue_request(data, request) 131 logdata = supervisor.options.logger.data 132 self.assertEqual(len(logdata), 2) 133 self.assertEqual(logdata[-2], 134 'XML-RPC method called: supervisor.getAPIVersion()') 135 self.assertEqual(logdata[-1], 136 'XML-RPC method supervisor.getAPIVersion() returned successfully') 137 self.assertEqual(len(request.producers), 1) 138 xml_response = request.producers[0] 139 response = xmlrpclib.loads(xml_response) 140 from supervisor.rpcinterface import API_VERSION 141 self.assertEqual(response[0][0], API_VERSION) 142 self.assertEqual(request._done, True) 143 self.assertEqual(request.headers['Content-Type'], 'text/xml') 144 self.assertEqual(request.headers['Content-Length'], len(xml_response)) 145 146 def test_continue_request_400_if_method_name_is_empty(self): 147 supervisor = DummySupervisor() 148 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 149 handler = self._makeOne(supervisor, subinterfaces) 150 data = '<?xml version="1.0" encoding="UTF-8"?>' \ 151 '<methodCall><methodName></methodName></methodCall>' 152 request = DummyRequest('/what/ever', None, None, None) 153 handler.continue_request(data, request) 154 logdata = supervisor.options.logger.data 155 self.assertEqual(len(logdata), 1) 156 self.assertTrue(logdata[0].startswith('XML-RPC request data')) 157 self.assertTrue(repr(data) in logdata[0]) 158 self.assertTrue(logdata[0].endswith('is invalid: no method name')) 159 self.assertEqual(request._error, 400) 160 161 def test_continue_request_400_if_loads_raises_not_xml(self): 162 supervisor = DummySupervisor() 163 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 164 handler = self._makeOne(supervisor, subinterfaces) 165 data = 'this is not an xml-rpc request body' 166 request = DummyRequest('/what/ever', None, None, None) 167 handler.continue_request(data, request) 168 logdata = supervisor.options.logger.data 169 self.assertEqual(len(logdata), 1) 170 self.assertTrue(logdata[0].startswith('XML-RPC request data')) 171 self.assertTrue(repr(data) in logdata[0]) 172 self.assertTrue(logdata[0].endswith('is invalid: unmarshallable')) 173 self.assertEqual(request._error, 400) 174 175 def test_continue_request_400_if_loads_raises_weird_xml(self): 176 supervisor = DummySupervisor() 177 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 178 handler = self._makeOne(supervisor, subinterfaces) 179 data = '<methodName></methodName><junk></junk>' 180 request = DummyRequest('/what/ever', None, None, None) 181 handler.continue_request(data, request) 182 logdata = supervisor.options.logger.data 183 self.assertEqual(len(logdata), 1) 184 self.assertTrue(logdata[0].startswith('XML-RPC request data')) 185 self.assertTrue(repr(data) in logdata[0]) 186 self.assertTrue(logdata[0].endswith('is invalid: unmarshallable')) 187 self.assertEqual(request._error, 400) 188 189 def test_continue_request_500_if_rpcinterface_method_call_raises(self): 190 supervisor = DummySupervisor() 191 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 192 handler = self._makeOne(supervisor, subinterfaces) 193 data = xmlrpclib.dumps((), 'supervisor.raiseError') 194 request = DummyRequest('/what/ever', None, None, None) 195 handler.continue_request(data, request) 196 logdata = supervisor.options.logger.data 197 self.assertEqual(len(logdata), 2) 198 self.assertEqual(logdata[0], 199 'XML-RPC method called: supervisor.raiseError()') 200 self.assertTrue("unexpected exception" in logdata[1]) 201 self.assertTrue(repr(data) in logdata[1]) 202 self.assertTrue("Traceback" in logdata[1]) 203 self.assertTrue("ValueError: error" in logdata[1]) 204 self.assertEqual(request._error, 500) 205 206 def test_continue_request_500_if_xmlrpc_dumps_raises(self): 207 supervisor = DummySupervisor() 208 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 209 handler = self._makeOne(supervisor, subinterfaces) 210 data = xmlrpclib.dumps((), 'supervisor.getXmlRpcUnmarshallable') 211 request = DummyRequest('/what/ever', None, None, None) 212 handler.continue_request(data, request) 213 logdata = supervisor.options.logger.data 214 self.assertEqual(len(logdata), 3) 215 self.assertEqual(logdata[0], 216 'XML-RPC method called: supervisor.getXmlRpcUnmarshallable()') 217 self.assertEqual(logdata[1], 218 'XML-RPC method supervisor.getXmlRpcUnmarshallable() ' 219 'returned successfully') 220 self.assertTrue("unexpected exception" in logdata[2]) 221 self.assertTrue(repr(data) in logdata[2]) 222 self.assertTrue("Traceback" in logdata[2]) 223 self.assertTrue("TypeError: cannot marshal" in logdata[2]) 224 self.assertEqual(request._error, 500) 225 226 def test_continue_request_value_is_function(self): 227 class DummyRPCNamespace(object): 228 def foo(self): 229 def inner(self): 230 return 1 231 inner.delay = .05 232 return inner 233 supervisor = DummySupervisor() 234 subinterfaces = [('supervisor', DummySupervisorRPCNamespace()), 235 ('ns1', DummyRPCNamespace())] 236 handler = self._makeOne(supervisor, subinterfaces) 237 data = xmlrpclib.dumps((), 'ns1.foo') 238 request = DummyRequest('/what/ever', None, None, None) 239 handler.continue_request(data, request) 240 logdata = supervisor.options.logger.data 241 self.assertEqual(len(logdata), 2) 242 self.assertEqual(logdata[-2], 243 'XML-RPC method called: ns1.foo()') 244 self.assertEqual(logdata[-1], 245 'XML-RPC method ns1.foo() returned successfully') 246 self.assertEqual(len(request.producers), 0) 247 self.assertEqual(request._done, False) 248 249 def test_iterparse_loads_methodcall(self): 250 s = """<?xml version="1.0"?> 251 <methodCall> 252 <methodName>examples.getStateName</methodName> 253 <params> 254 <param> 255 <value><i4>41</i4></value> 256 </param> 257 <param> 258 <value><string>foo</string></value> 259 </param> 260 <param> 261 <value><string></string></value> 262 </param> 263 <param> 264 <!-- xml-rpc spec allows strings without <string> tag --> 265 <value>bar</value> 266 </param> 267 <param> 268 <value></value> 269 </param> 270 <param> 271 <value><boolean>1</boolean></value> 272 </param> 273 <param> 274 <value><double>-12.214</double></value> 275 </param> 276 <param> 277 <value> 278 <dateTime.iso8601>19980717T14:08:55</dateTime.iso8601> 279 </value> 280 </param> 281 <param> 282 <value><base64>eW91IGNhbid0IHJlYWQgdGhpcyE=</base64></value> 283 </param> 284 <param> 285 <struct> 286 <member><name>j</name><value><i4>5</i4></value></member> 287 <member><name>k</name><value>abc</value></member> 288 </struct> 289 </param> 290 <param> 291 <array> 292 <data> 293 <value><i4>12</i4></value> 294 <value><string>abc</string></value> 295 <value>def</value> 296 <value><i4>34</i4></value> 297 </data> 298 </array> 299 </param> 300 <param> 301 <struct> 302 <member> 303 <name>k</name> 304 <value><array><data> 305 <value><i4>1</i4></value> 306 <struct></struct> 307 </data></array></value> 308 </member> 309 </struct> 310 </param> 311 </params> 312 </methodCall> 313 """ 314 supervisor = DummySupervisor() 315 subinterfaces = [('supervisor', DummySupervisorRPCNamespace())] 316 handler = self._makeOne(supervisor, subinterfaces) 317 result = handler.loads(s) 318 params, method = result 319 import datetime 320 self.assertEqual(method, 'examples.getStateName') 321 self.assertEqual(params[0], 41) 322 self.assertEqual(params[1], 'foo') 323 self.assertEqual(params[2], '') 324 self.assertEqual(params[3], 'bar') 325 self.assertEqual(params[4], '') 326 self.assertEqual(params[5], True) 327 self.assertEqual(params[6], -12.214) 328 self.assertEqual(params[7], datetime.datetime(1998, 7, 17, 14, 8, 55)) 329 self.assertEqual(params[8], "you can't read this!") 330 self.assertEqual(params[9], {'j': 5, 'k': 'abc'}) 331 self.assertEqual(params[10], [12, 'abc', 'def', 34]) 332 self.assertEqual(params[11], {'k': [1, {}]}) 333 334class TraverseTests(unittest.TestCase): 335 def test_security_disallows_underscore_methods(self): 336 from supervisor import xmlrpc 337 class Root: 338 pass 339 class A: 340 def _danger(self): 341 return True 342 root = Root() 343 root.a = A() 344 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 345 root, 'a._danger', []) 346 347 def test_security_disallows_object_traversal(self): 348 from supervisor import xmlrpc 349 class Root: 350 pass 351 class A: 352 pass 353 class B: 354 def danger(self): 355 return True 356 root = Root() 357 root.a = A() 358 root.a.b = B() 359 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 360 root, 'a.b.danger', []) 361 362 def test_namespace_name_not_found(self): 363 from supervisor import xmlrpc 364 class Root: 365 pass 366 root = Root() 367 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 368 root, 'notfound.hello', None) 369 370 def test_method_name_not_found(self): 371 from supervisor import xmlrpc 372 class Root: 373 pass 374 class A: 375 pass 376 root = Root() 377 root.a = A() 378 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 379 root, 'a.notfound', []) 380 381 def test_method_name_exists_but_is_not_a_method(self): 382 from supervisor import xmlrpc 383 class Root: 384 pass 385 class A: 386 pass 387 class B: 388 pass 389 root = Root() 390 root.a = A() 391 root.a.b = B() 392 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 393 root, 'a.b', []) # b is not a method 394 395 def test_bad_params(self): 396 from supervisor import xmlrpc 397 class Root: 398 pass 399 class A: 400 def hello(self, name): 401 return "Hello %s" % name 402 root = Root() 403 root.a = A() 404 self.assertRaises(xmlrpc.RPCError, xmlrpc.traverse, 405 root, 'a.hello', ["there", "extra"]) # too many params 406 407 def test_success(self): 408 from supervisor import xmlrpc 409 class Root: 410 pass 411 class A: 412 def hello(self, name): 413 return "Hello %s" % name 414 root = Root() 415 root.a = A() 416 result = xmlrpc.traverse(root, 'a.hello', ["there"]) 417 self.assertEqual(result, "Hello there") 418 419class SupervisorTransportTests(unittest.TestCase): 420 def _getTargetClass(self): 421 from supervisor.xmlrpc import SupervisorTransport 422 return SupervisorTransport 423 424 def _makeOne(self, *arg, **kw): 425 return self._getTargetClass()(*arg, **kw) 426 427 def test_ctor_unix(self): 428 from supervisor import xmlrpc 429 transport = self._makeOne('user', 'pass', 'unix:///foo/bar') 430 conn = transport._get_connection() 431 self.assertTrue(isinstance(conn, xmlrpc.UnixStreamHTTPConnection)) 432 self.assertEqual(conn.host, 'localhost') 433 self.assertEqual(conn.socketfile, '/foo/bar') 434 435 def test_ctor_unknown(self): 436 self.assertRaises(ValueError, 437 self._makeOne, 'user', 'pass', 'unknown:///foo/bar' 438 ) 439 440 def test__get_connection_http_9001(self): 441 transport = self._makeOne('user', 'pass', 'http://127.0.0.1:9001/') 442 conn = transport._get_connection() 443 self.assertTrue(isinstance(conn, httplib.HTTPConnection)) 444 self.assertEqual(conn.host, '127.0.0.1') 445 self.assertEqual(conn.port, 9001) 446 447 def test__get_connection_http_80(self): 448 transport = self._makeOne('user', 'pass', 'http://127.0.0.1/') 449 conn = transport._get_connection() 450 self.assertTrue(isinstance(conn, httplib.HTTPConnection)) 451 self.assertEqual(conn.host, '127.0.0.1') 452 self.assertEqual(conn.port, 80) 453 454 def test_request_non_200_response(self): 455 transport = self._makeOne('user', 'pass', 'http://127.0.0.1/') 456 dummy_conn = DummyConnection(400, '') 457 def getconn(): 458 return dummy_conn 459 transport._get_connection = getconn 460 self.assertRaises(xmlrpclib.ProtocolError, 461 transport.request, 'localhost', '/', '') 462 self.assertEqual(transport.connection, None) 463 self.assertEqual(dummy_conn.closed, True) 464 465 def test_request_400_response(self): 466 transport = self._makeOne('user', 'pass', 'http://127.0.0.1/') 467 dummy_conn = DummyConnection(400, '') 468 def getconn(): 469 return dummy_conn 470 transport._get_connection = getconn 471 self.assertRaises(xmlrpclib.ProtocolError, 472 transport.request, 'localhost', '/', '') 473 self.assertEqual(transport.connection, None) 474 self.assertEqual(dummy_conn.closed, True) 475 self.assertEqual(dummy_conn.requestargs[0], 'POST') 476 self.assertEqual(dummy_conn.requestargs[1], '/') 477 self.assertEqual(dummy_conn.requestargs[2], b'') 478 self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0') 479 self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml') 480 self.assertEqual(dummy_conn.requestargs[3]['Authorization'], 481 'Basic dXNlcjpwYXNz') 482 self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml') 483 484 def test_request_200_response(self): 485 transport = self._makeOne('user', 'pass', 'http://127.0.0.1/') 486 response = """<?xml version="1.0"?> 487 <methodResponse> 488 <params> 489 <param> 490 <value><string>South Dakota</string></value> 491 </param> 492 </params> 493 </methodResponse>""" 494 dummy_conn = DummyConnection(200, response) 495 def getconn(): 496 return dummy_conn 497 transport._get_connection = getconn 498 result = transport.request('localhost', '/', '') 499 self.assertEqual(transport.connection, dummy_conn) 500 self.assertEqual(dummy_conn.closed, False) 501 self.assertEqual(dummy_conn.requestargs[0], 'POST') 502 self.assertEqual(dummy_conn.requestargs[1], '/') 503 self.assertEqual(dummy_conn.requestargs[2], b'') 504 self.assertEqual(dummy_conn.requestargs[3]['Content-Length'], '0') 505 self.assertEqual(dummy_conn.requestargs[3]['Content-Type'], 'text/xml') 506 self.assertEqual(dummy_conn.requestargs[3]['Authorization'], 507 'Basic dXNlcjpwYXNz') 508 self.assertEqual(dummy_conn.requestargs[3]['Accept'], 'text/xml') 509 self.assertEqual(result, ('South Dakota',)) 510 511 def test_close(self): 512 transport = self._makeOne('user', 'pass', 'http://127.0.0.1/') 513 dummy_conn = DummyConnection(200, '''<?xml version="1.0"?> 514 <methodResponse><params/></methodResponse>''') 515 def getconn(): 516 return dummy_conn 517 transport._get_connection = getconn 518 transport.request('localhost', '/', '') 519 transport.close() 520 self.assertTrue(dummy_conn.closed) 521 522class TestDeferredXMLRPCResponse(unittest.TestCase): 523 def _getTargetClass(self): 524 from supervisor.xmlrpc import DeferredXMLRPCResponse 525 return DeferredXMLRPCResponse 526 527 def _makeOne(self, request=None, callback=None): 528 if request is None: 529 request = DummyRequest(None, None, None, None, None) 530 if callback is None: 531 callback = Dummy() 532 callback.delay = 1 533 return self._getTargetClass()(request, callback) 534 535 def test_ctor(self): 536 callback = Dummy() 537 callback.delay = 1 538 inst = self._makeOne(request='request', callback=callback) 539 self.assertEqual(inst.callback, callback) 540 self.assertEqual(inst.delay, 1.0) 541 self.assertEqual(inst.request, 'request') 542 self.assertEqual(inst.finished, False) 543 544 def test_more_finished(self): 545 inst = self._makeOne() 546 inst.finished = True 547 result = inst.more() 548 self.assertEqual(result, '') 549 550 def test_more_callback_returns_not_done_yet(self): 551 from supervisor.http import NOT_DONE_YET 552 def callback(): 553 return NOT_DONE_YET 554 callback.delay = 1 555 inst = self._makeOne(callback=callback) 556 self.assertEqual(inst.more(), NOT_DONE_YET) 557 558 def test_more_callback_raises_RPCError(self): 559 from supervisor.xmlrpc import RPCError, Faults 560 def callback(): 561 raise RPCError(Faults.UNKNOWN_METHOD) 562 callback.delay = 1 563 inst = self._makeOne(callback=callback) 564 self.assertEqual(inst.more(), None) 565 self.assertEqual(len(inst.request.producers), 1) 566 self.assertTrue('UNKNOWN_METHOD' in inst.request.producers[0]) 567 self.assertTrue(inst.finished) 568 569 def test_more_callback_returns_value(self): 570 def callback(): 571 return 'abc' 572 callback.delay = 1 573 inst = self._makeOne(callback=callback) 574 self.assertEqual(inst.more(), None) 575 self.assertEqual(len(inst.request.producers), 1) 576 self.assertTrue('abc' in inst.request.producers[0]) 577 self.assertTrue(inst.finished) 578 579 def test_more_callback_raises_unexpected_exception(self): 580 def callback(): 581 raise ValueError('foo') 582 callback.delay = 1 583 inst = self._makeOne(callback=callback) 584 self.assertEqual(inst.more(), None) 585 self.assertEqual(inst.request._error, 500) 586 self.assertTrue(inst.finished) 587 logged = inst.request.channel.server.logger.logged 588 self.assertEqual(len(logged), 1) 589 src, msg = logged[0] 590 self.assertEqual(src, 'XML-RPC response callback error') 591 self.assertTrue("Traceback" in msg) 592 593 def test_getresponse_http_10_with_keepalive(self): 594 inst = self._makeOne() 595 inst.request.version = '1.0' 596 inst.request.header.append('Connection: keep-alive') 597 inst.getresponse('abc') 598 self.assertEqual(len(inst.request.producers), 1) 599 self.assertEqual(inst.request.headers['Connection'], 'Keep-Alive') 600 601 def test_getresponse_http_10_no_keepalive(self): 602 inst = self._makeOne() 603 inst.request.version = '1.0' 604 inst.getresponse('abc') 605 self.assertEqual(len(inst.request.producers), 1) 606 self.assertEqual(inst.request.headers['Connection'], 'close') 607 608 def test_getresponse_http_11_without_close(self): 609 inst = self._makeOne() 610 inst.request.version = '1.1' 611 inst.getresponse('abc') 612 self.assertEqual(len(inst.request.producers), 1) 613 self.assertTrue('Connection' not in inst.request.headers) 614 615 def test_getresponse_http_11_with_close(self): 616 inst = self._makeOne() 617 inst.request.header.append('Connection: close') 618 inst.request.version = '1.1' 619 inst.getresponse('abc') 620 self.assertEqual(len(inst.request.producers), 1) 621 self.assertEqual(inst.request.headers['Connection'], 'close') 622 623 def test_getresponse_http_unknown(self): 624 inst = self._makeOne() 625 inst.request.version = None 626 inst.getresponse('abc') 627 self.assertEqual(len(inst.request.producers), 1) 628 self.assertEqual(inst.request.headers['Connection'], 'close') 629 630class TestSystemNamespaceRPCInterface(unittest.TestCase): 631 def _makeOne(self, namespaces=()): 632 from supervisor.xmlrpc import SystemNamespaceRPCInterface 633 return SystemNamespaceRPCInterface(namespaces) 634 635 def test_listMethods_gardenpath(self): 636 inst = self._makeOne() 637 result = inst.listMethods() 638 self.assertEqual( 639 result, 640 ['system.listMethods', 641 'system.methodHelp', 642 'system.methodSignature', 643 'system.multicall', 644 ] 645 ) 646 647 def test_listMethods_omits_underscore_attrs(self): 648 class DummyNamespace(object): 649 def foo(self): pass 650 def _bar(self): pass 651 ns1 = DummyNamespace() 652 inst = self._makeOne([('ns1', ns1)]) 653 result = inst.listMethods() 654 self.assertEqual( 655 result, 656 ['ns1.foo', 657 'system.listMethods', 658 'system.methodHelp', 659 'system.methodSignature', 660 'system.multicall' 661 ] 662 ) 663 664 def test_methodHelp_known_method(self): 665 inst = self._makeOne() 666 result = inst.methodHelp('system.listMethods') 667 self.assertTrue('array' in result) 668 669 def test_methodHelp_unknown_method(self): 670 from supervisor.xmlrpc import RPCError 671 inst = self._makeOne() 672 self.assertRaises(RPCError, inst.methodHelp, 'wont.be.found') 673 674 def test_methodSignature_known_method(self): 675 inst = self._makeOne() 676 result = inst.methodSignature('system.methodSignature') 677 self.assertEqual(result, ['array', 'string']) 678 679 def test_methodSignature_unknown_method(self): 680 from supervisor.xmlrpc import RPCError 681 inst = self._makeOne() 682 self.assertRaises(RPCError, inst.methodSignature, 'wont.be.found') 683 684 def test_methodSignature_with_bad_sig(self): 685 from supervisor.xmlrpc import RPCError 686 class DummyNamespace(object): 687 def foo(self): 688 """ @param string name The thing""" 689 ns1 = DummyNamespace() 690 inst = self._makeOne([('ns1', ns1)]) 691 self.assertRaises(RPCError, inst.methodSignature, 'ns1.foo') 692 693 def test_multicall_faults_for_recursion(self): 694 from supervisor.xmlrpc import Faults 695 inst = self._makeOne() 696 calls = [{'methodName':'system.multicall'}] 697 results = inst.multicall(calls) 698 self.assertEqual( 699 results, 700 [{'faultCode': Faults.INCORRECT_PARAMETERS, 701 'faultString': ('INCORRECT_PARAMETERS: Recursive ' 702 'system.multicall forbidden')}] 703 ) 704 705 def test_multicall_faults_for_missing_methodName(self): 706 from supervisor.xmlrpc import Faults 707 inst = self._makeOne() 708 calls = [{}] 709 results = inst.multicall(calls) 710 self.assertEqual( 711 results, 712 [{'faultCode': Faults.INCORRECT_PARAMETERS, 713 'faultString': 'INCORRECT_PARAMETERS: No methodName'}] 714 ) 715 716 def test_multicall_faults_for_methodName_bad_namespace(self): 717 from supervisor.xmlrpc import Faults 718 inst = self._makeOne() 719 calls = [{'methodName': 'bad.stopProcess'}] 720 results = inst.multicall(calls) 721 self.assertEqual( 722 results, 723 [{'faultCode': Faults.UNKNOWN_METHOD, 724 'faultString': 'UNKNOWN_METHOD'}] 725 ) 726 727 def test_multicall_faults_for_methodName_good_ns_bad_method(self): 728 from supervisor.xmlrpc import Faults 729 class DummyNamespace(object): 730 pass 731 ns1 = DummyNamespace() 732 inst = self._makeOne([('ns1', ns1)]) 733 calls = [{'methodName': 'ns1.bad'}] 734 results = inst.multicall(calls) 735 self.assertEqual( 736 results, 737 [{'faultCode': Faults.UNKNOWN_METHOD, 738 'faultString': 'UNKNOWN_METHOD'}] 739 ) 740 741 def test_multicall_returns_empty_results_for_empty_calls(self): 742 inst = self._makeOne() 743 calls = [] 744 results = inst.multicall(calls) 745 self.assertEqual(results, []) 746 747 def test_multicall_performs_noncallback_functions_serially(self): 748 class DummyNamespace(object): 749 def say(self, name): 750 """ @param string name Process name""" 751 return name 752 ns1 = DummyNamespace() 753 inst = self._makeOne([('ns1', ns1)]) 754 calls = [ 755 {'methodName': 'ns1.say', 'params': ['Alvin']}, 756 {'methodName': 'ns1.say', 'params': ['Simon']}, 757 {'methodName': 'ns1.say', 'params': ['Theodore']} 758 ] 759 results = inst.multicall(calls) 760 self.assertEqual(results, ['Alvin', 'Simon', 'Theodore']) 761 762 def test_multicall_catches_noncallback_exceptions(self): 763 import errno 764 from supervisor.xmlrpc import RPCError, Faults 765 class DummyNamespace(object): 766 def bad_name(self): 767 raise RPCError(Faults.BAD_NAME, 'foo') 768 def os_error(self): 769 raise OSError(errno.ENOENT) 770 ns1 = DummyNamespace() 771 inst = self._makeOne([('ns1', ns1)]) 772 calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}] 773 results = inst.multicall(calls) 774 775 bad_name = {'faultCode': Faults.BAD_NAME, 776 'faultString': 'BAD_NAME: foo'} 777 os_error = {'faultCode': Faults.FAILED, 778 'faultString': "FAILED: %s:2" % OSError} 779 self.assertEqual(results, [bad_name, os_error]) 780 781 def test_multicall_catches_callback_exceptions(self): 782 import errno 783 from supervisor.xmlrpc import RPCError, Faults 784 from supervisor.http import NOT_DONE_YET 785 class DummyNamespace(object): 786 def bad_name(self): 787 def inner(): 788 raise RPCError(Faults.BAD_NAME, 'foo') 789 return inner 790 def os_error(self): 791 def inner(): 792 raise OSError(errno.ENOENT) 793 return inner 794 ns1 = DummyNamespace() 795 inst = self._makeOne([('ns1', ns1)]) 796 calls = [{'methodName': 'ns1.bad_name'}, {'methodName': 'ns1.os_error'}] 797 callback = inst.multicall(calls) 798 results = NOT_DONE_YET 799 while results is NOT_DONE_YET: 800 results = callback() 801 802 bad_name = {'faultCode': Faults.BAD_NAME, 803 'faultString': 'BAD_NAME: foo'} 804 os_error = {'faultCode': Faults.FAILED, 805 'faultString': "FAILED: %s:2" % OSError} 806 self.assertEqual(results, [bad_name, os_error]) 807 808 def test_multicall_performs_callback_functions_serially(self): 809 from supervisor.http import NOT_DONE_YET 810 class DummyNamespace(object): 811 def __init__(self): 812 self.stop_results = [NOT_DONE_YET, NOT_DONE_YET, 813 NOT_DONE_YET, 'stop result'] 814 self.start_results = ['start result'] 815 def stopProcess(self, name): 816 def inner(): 817 result = self.stop_results.pop(0) 818 if result is not NOT_DONE_YET: 819 self.stopped = True 820 return result 821 return inner 822 def startProcess(self, name): 823 def inner(): 824 if not self.stopped: 825 raise Exception("This should not raise") 826 return self.start_results.pop(0) 827 return inner 828 ns1 = DummyNamespace() 829 inst = self._makeOne([('ns1', ns1)]) 830 calls = [{'methodName': 'ns1.stopProcess', 831 'params': {'name': 'foo'}}, 832 {'methodName': 'ns1.startProcess', 833 'params': {'name': 'foo'}}] 834 callback = inst.multicall(calls) 835 results = NOT_DONE_YET 836 while results is NOT_DONE_YET: 837 results = callback() 838 self.assertEqual(results, ['stop result', 'start result']) 839 840class Test_gettags(unittest.TestCase): 841 def _callFUT(self, comment): 842 from supervisor.xmlrpc import gettags 843 return gettags(comment) 844 845 def test_one_atpart(self): 846 lines = '@foo' 847 result = self._callFUT(lines) 848 self.assertEqual( 849 result, 850 [(0, None, None, None, ''), (0, 'foo', '', '', '')] 851 ) 852 853 def test_two_atparts(self): 854 lines = '@foo array' 855 result = self._callFUT(lines) 856 self.assertEqual( 857 result, 858 [(0, None, None, None, ''), (0, 'foo', 'array', '', '')] 859 ) 860 861 def test_three_atparts(self): 862 lines = '@foo array name' 863 result = self._callFUT(lines) 864 self.assertEqual( 865 result, 866 [(0, None, None, None, ''), (0, 'foo', 'array', 'name', '')] 867 ) 868 869 def test_four_atparts(self): 870 lines = '@foo array name text' 871 result = self._callFUT(lines) 872 self.assertEqual( 873 result, 874 [(0, None, None, None, ''), (0, 'foo', 'array', 'name', 'text')] 875 ) 876 877class Test_capped_int(unittest.TestCase): 878 def _callFUT(self, value): 879 from supervisor.xmlrpc import capped_int 880 return capped_int(value) 881 882 def test_converts_value_to_integer(self): 883 self.assertEqual(self._callFUT('42'), 42) 884 885 def test_caps_value_below_minint(self): 886 from supervisor.compat import xmlrpclib 887 self.assertEqual(self._callFUT(xmlrpclib.MININT - 1), xmlrpclib.MININT) 888 889 def test_caps_value_above_maxint(self): 890 from supervisor.compat import xmlrpclib 891 self.assertEqual(self._callFUT(xmlrpclib.MAXINT + 1), xmlrpclib.MAXINT) 892 893 894class DummyResponse: 895 def __init__(self, status=200, body='', reason='reason'): 896 self.status = status 897 self.body = body 898 self.reason = reason 899 900 def read(self): 901 return self.body 902 903class Dummy(object): 904 pass 905 906class DummyConnection: 907 closed = False 908 def __init__(self, status=200, body='', reason='reason'): 909 self.response = DummyResponse(status, body, reason) 910 911 def getresponse(self): 912 return self.response 913 914 def request(self, *arg, **kw): 915 self.requestargs = arg 916 self.requestkw = kw 917 918 def close(self): 919 self.closed = True 920 921