def test_simpleReprs(self): """ Verify that the various Box objects repr properly, for debugging. """ self.assertEquals(type(repr(amp._TLSBox())), str) self.assertEquals(type(repr(amp._SwitchBox('a'))), str) self.assertEquals(type(repr(amp.QuitBox())), str) self.assertEquals(type(repr(amp.AmpBox())), str) self.failUnless("AmpBox" in repr(amp.AmpBox()))
def formatError(error): if error.check(amp.RemoteAmpError): code = error.value.errorCode desc = error.value.description # Evennia extra logging desc += " (error logged on other side)" _get_logger().log_err( f"AMP caught exception ({desc}):\n{error.value}") if isinstance(desc, str): desc = desc.encode("utf-8", "replace") if error.value.fatal: errorBox = amp.QuitBox() else: errorBox = amp.AmpBox() else: errorBox = amp.QuitBox() _get_logger().log_err( error) # server-side logging if unhandled error code = UNKNOWN_ERROR_CODE desc = b"Unknown Error" errorBox[ERROR] = box[ASK] errorBox[ERROR_DESCRIPTION] = desc errorBox[ERROR_CODE] = code return errorBox
class TestRPCProtocol_UnhandledErrorsWhenHandlingResponses(MAASTestCase): answer_seq = b"%d" % random.randrange(0, 2**32) answer_box = amp.AmpBox(_answer=answer_seq) error_seq = b"%d" % random.randrange(0, 2**32) error_box = amp.AmpBox( _error=error_seq, _error_code=amp.UNHANDLED_ERROR_CODE, _error_description=factory.make_string(), ) scenarios = ( ("_answerReceived", { "seq": answer_seq, "box": answer_box }), ("_errorReceived", { "seq": error_seq, "box": error_box }), ) def test_unhandled_errors_logged_and_do_not_cause_disconnection(self): self.patch(common.log, "debug") protocol = common.RPCProtocol() protocol.makeConnection(StringTransport()) # Poke a request into the dispatcher that will always fail. d = Deferred().addCallback(lambda _: 0 / 0) protocol._outstandingRequests[self.seq] = d # Push a box in response to the request. with TwistedLoggerFixture() as logger: protocol.ampBoxReceived(self.box) # The Deferred does not have a dangling error. self.assertThat(extract_result(d), Is(None)) # The transport is still connected. self.assertThat(protocol.transport.disconnecting, Is(False)) # The error has been logged. self.assertDocTestMatches( """\ Unhandled failure during AMP request. This is probably a bug. Please ensure that this error is handled within application code. Traceback (most recent call last): ... """, logger.output, )
def test__replaces_missing_ask_with_none(self): box = amp.AmpBox(_command=b"command") self.patch(common, "gethostname").return_value = "host" self.patch(common, "getpid").return_value = 1234 self.assertThat(common.make_command_ref(box), Equals( "host:pid=1234:cmd=command:ask=none"))
def test_unhandled_errors_do_not_cause_disconnection(self): self.patch(common.log, "debug") protocol = common.RPCProtocol() protocol.makeConnection(StringTransport()) # Ensure that the superclass dispatchCommand() will fail. dispatchCommand = self.patch(amp.AMP, "dispatchCommand") dispatchCommand.side_effect = always_fail_with(ZeroDivisionError()) # Push a command box into the protocol. seq = b"%d" % random.randrange(0, 2**32) cmd = factory.make_string().encode("ascii") box = amp.AmpBox(_ask=seq, _command=cmd) with TwistedLoggerFixture() as logger: protocol.ampBoxReceived(box) # The transport is still connected. self.expectThat(protocol.transport.disconnecting, Is(False)) # The error has been logged on the originating side of the AMP # session, along with an explanatory message. The message includes a # command reference. cmd_ref = common.make_command_ref(box) self.assertDocTestMatches( """\ Unhandled failure dispatching AMP command. This is probably a bug. Please ensure that this error is handled within application code or declared in the signature of the %s command. [%s] Traceback (most recent call last): ... """ % (cmd, cmd_ref), logger.output, ) # A simpler error message has been transmitted over the wire. It # includes the same command reference as logged locally. protocol.transport.io.seek(0) observed_boxes_sent = amp.parse(protocol.transport.io) expected_boxes_sent = [ amp.AmpBox( _error=seq, _error_code=amp.UNHANDLED_ERROR_CODE, _error_description=(b"Unknown Error [%s]" % cmd_ref.encode("ascii")), ) ] self.assertThat(observed_boxes_sent, Equals(expected_boxes_sent))
def test__command_ref_includes_host_pid_command_and_ask_sequence(self): host = factory.make_name("hostname") pid = random.randint(99, 9999999) cmd = factory.make_name("command").encode("ascii") ask = str(random.randint(99, 9999999)).encode("ascii") box = amp.AmpBox(_command=cmd, _ask=ask) self.patch(common, "gethostname").return_value = host self.patch(common, "getpid").return_value = pid self.assertThat( common.make_command_ref(box), Equals("%s:pid=%s:cmd=%s:ask=%s" % ( host, pid, cmd.decode("ascii"), ask.decode("ascii"))))
def test_brokenReturnValue(self): """ It can be very confusing if you write some code which responds to a command, but gets the return value wrong. Most commonly you end up returning None instead of a dictionary. Verify that if that happens, the framework logs a useful error. """ L = [] SimpleSymmetricCommandProtocol().dispatchCommand( amp.AmpBox(_command=BrokenReturn.commandName)).addErrback(L.append) blr = L[0].trap(amp.BadLocalReturn) self.failUnlessIn('None', repr(L[0].value))