Exemplo n.º 1
0
 def setUp(self):
     self.clock = Clock()
     self.fake_smsc = FakeSMSC()
     self.tx_helper = self.add_helper(
         TransportHelper(SmppTransceiverTransport))
     self.default_config = {
         'transport_name':
         self.tx_helper.transport_name,
         'twisted_endpoint':
         self.fake_smsc.endpoint,
         'deliver_short_message_processor':
         ('vumi.transports.smpp.processors.sixdee.'
          'DeliverShortMessageProcessor'),
         'submit_short_message_processor':
         ('vumi.transports.smpp.processors.sixdee.'
          'SubmitShortMessageProcessor'),
         'system_id':
         'foo',
         'password':
         '******',
         'deliver_short_message_processor_config': {
             'data_coding_overrides': {
                 0: 'utf-8',
             }
         },
         'submit_short_message_processor_config': {
             'submit_sm_encoding': 'utf-16be',
             'submit_sm_data_coding': 8,
             'send_multipart_udh': True,
         }
     }
Exemplo n.º 2
0
    def test_submit_sm_resp_explicit(self):
        """
        FakeSMSC can respond to a SubmitSM PDU that is explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        # No params.
        submit_sm_resp_d = fake_smsc.submit_sm_resp(SubmitSM(123).obj)
        self.assertNoResult(submit_sm_resp_d)

        client.handle_pdu_d.callback(None)
        self.successResultOf(submit_sm_resp_d)
        resp = SubmitSMResp(123, message_id="id123", command_status="ESME_ROK")
        self.assertEqual(client.pdus_handled, [resp.obj])

        client.pdus_handled[:] = []
        # Explicit message_id.
        submit_sm_resp_d = fake_smsc.submit_sm_resp(SubmitSM(124).obj,
                                                    message_id="foo")
        yield client.write(SubmitSM(124).get_bin())
        self.assertNoResult(submit_sm_resp_d)

        client.handle_pdu_d.callback(None)
        self.successResultOf(submit_sm_resp_d)
        resp = SubmitSMResp(124, message_id="foo", command_status="ESME_ROK")
        self.assertEqual(client.pdus_handled, [resp.obj])
Exemplo n.º 3
0
    def test_submit_sm_resp_explicit(self):
        """
        FakeSMSC can respond to a SubmitSM PDU that is explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        # No params.
        submit_sm_resp_d = fake_smsc.submit_sm_resp(SubmitSM(123).obj)
        self.assertNoResult(submit_sm_resp_d)

        client.handle_pdu_d.callback(None)
        self.successResultOf(submit_sm_resp_d)
        resp = SubmitSMResp(123, message_id="id123", command_status="ESME_ROK")
        self.assertEqual(client.pdus_handled, [resp.obj])

        client.pdus_handled[:] = []
        # Explicit message_id.
        submit_sm_resp_d = fake_smsc.submit_sm_resp(
            SubmitSM(124).obj, message_id="foo")
        yield client.write(SubmitSM(124).get_bin())
        self.assertNoResult(submit_sm_resp_d)

        client.handle_pdu_d.callback(None)
        self.successResultOf(submit_sm_resp_d)
        resp = SubmitSMResp(124, message_id="foo", command_status="ESME_ROK")
        self.assertEqual(client.pdus_handled, [resp.obj])
Exemplo n.º 4
0
    def test_test_submit_sm_resp_wrong_pdu(self):
        """
        FakeSMSC will raise an exception if asked to bind with a non-bind PDU.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        submit_sm_resp_d = fake_smsc.submit_sm_resp()
        yield client.write(EnquireLink(0).get_bin())
        self.failureResultOf(submit_sm_resp_d, ValueError)
Exemplo n.º 5
0
    def test_test_submit_sm_resp_wrong_pdu(self):
        """
        FakeSMSC will raise an exception if asked to bind with a non-bind PDU.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        submit_sm_resp_d = fake_smsc.submit_sm_resp()
        yield client.write(EnquireLink(0).get_bin())
        self.failureResultOf(submit_sm_resp_d, ValueError)
Exemplo n.º 6
0
    def test_respond_to_enquire_link_wrong_pdu(self):
        """
        FakeSMSC will raise an exception if asked to respond to an enquire_link
        that isn't an enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        rtel_d = fake_smsc.respond_to_enquire_link()
        yield client.write(DeliverSM(0).get_bin())
        self.failureResultOf(rtel_d, ValueError)
Exemplo n.º 7
0
    def test_respond_to_enquire_link_wrong_pdu(self):
        """
        FakeSMSC will raise an exception if asked to respond to an enquire_link
        that isn't an enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        rtel_d = fake_smsc.respond_to_enquire_link()
        yield client.write(DeliverSM(0).get_bin())
        self.failureResultOf(rtel_d, ValueError)
Exemplo n.º 8
0
 def setUp(self):
     self.clock = Clock()
     self.persistence_helper = self.add_helper(PersistenceHelper())
     self.redis = yield self.persistence_helper.get_redis_manager()
     self.fake_smsc = FakeSMSC(auto_accept=False)
     self.default_config = {
         'transport_name': 'sphex_transport',
         'twisted_endpoint': self.fake_smsc.endpoint,
         'system_id': 'system_id',
         'password': '******',
     }
Exemplo n.º 9
0
    def test_await_pdus_arrived(self):
        """
        The caller can wait for multiple PDU that have already arrived.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        yield client.write(EnquireLink(1).get_bin())
        yield client.write(EnquireLink(2).get_bin())

        self.assertEqual(self.successResultOf(fake_smsc.await_pdus(2)), [
            unpack_pdu(EnquireLink(1).get_bin()),
            unpack_pdu(EnquireLink(2).get_bin())])
Exemplo n.º 10
0
    def test_auto_unbind(self):
        """
        FakeSMSC will automatically respond to an unbind request by default.
        The unbind PDU remains in the queue.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)

        yield client.write(Unbind(7).get_bin())
        self.assertEqual(client.received, UnbindResp(7).get_bin())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 1)
Exemplo n.º 11
0
    def test_respond_to_enquire_link_explicit(self):
        """
        FakeSMSC can respond to an enquire_link PDU explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        rtel_d = fake_smsc.respond_to_enquire_link(EnquireLink(2).obj)
        yield wait0()
        # enquire_link response received.
        self.successResultOf(rtel_d)
        self.assertEqual(client.received, EnquireLinkResp(2).get_bin())
Exemplo n.º 12
0
    def test_auto_unbind(self):
        """
        FakeSMSC will automatically respond to an unbind request by default.
        The unbind PDU remains in the queue.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)

        yield client.write(Unbind(7).get_bin())
        self.assertEqual(client.received, UnbindResp(7).get_bin())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 1)
Exemplo n.º 13
0
    def test_respond_to_enquire_link_explicit(self):
        """
        FakeSMSC can respond to an enquire_link PDU explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        rtel_d = fake_smsc.respond_to_enquire_link(EnquireLink(2).obj)
        yield wait0()
        # enquire_link response received.
        self.successResultOf(rtel_d)
        self.assertEqual(client.received, EnquireLinkResp(2).get_bin())
Exemplo n.º 14
0
    def test_await_pdus_arrived(self):
        """
        The caller can wait for multiple PDU that have already arrived.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        yield client.write(EnquireLink(1).get_bin())
        yield client.write(EnquireLink(2).get_bin())

        self.assertEqual(self.successResultOf(fake_smsc.await_pdus(2)), [
            unpack_pdu(EnquireLink(1).get_bin()),
            unpack_pdu(EnquireLink(2).get_bin())
        ])
Exemplo n.º 15
0
 def setUp(self):
     self.clock = Clock()
     self.fake_smsc = FakeSMSC()
     self.tx_helper = self.add_helper(
         TransportHelper(SmppTransceiverTransport))
     self.default_config = {
         'transport_name': self.tx_helper.transport_name,
         'twisted_endpoint': self.fake_smsc.endpoint,
         'deliver_short_message_processor': (
             'vumi.transports.smpp.processors.sixdee.'
             'DeliverShortMessageProcessor'),
         'submit_short_message_processor': (
             'vumi.transports.smpp.processors.sixdee.'
             'SubmitShortMessageProcessor'),
         'system_id': 'foo',
         'password': '******',
         'deliver_short_message_processor_config': {
             'data_coding_overrides': {
                 0: 'utf-8',
             }
         },
         'submit_short_message_processor_config': {
             'submit_sm_encoding': 'utf-16be',
             'submit_sm_data_coding': 8,
             'send_multipart_udh': True,
         }
     }
Exemplo n.º 16
0
    def test_send_bytes(self):
        """
        Bytes can be sent to the client.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        send_d = fake_smsc.send_bytes(b"abc")
        # Bytes sent, not yet received.
        self.assertNoResult(send_d)
        self.assertEqual(client.received, b"")

        yield send_d
        # Bytes received.
        self.assertEqual(client.received, b"abc")
Exemplo n.º 17
0
    def test_respond_to_enquire_link(self):
        """
        FakeSMSC can respond to an enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        rtel_d = fake_smsc.respond_to_enquire_link()
        yield client.write(EnquireLink(2).get_bin())
        # enquire_link response received.
        self.assertNoResult(rtel_d)
        self.assertEqual(client.received, EnquireLinkResp(2).get_bin())

        yield wait0()
        self.successResultOf(rtel_d)
Exemplo n.º 18
0
    def test_send_bytes(self):
        """
        Bytes can be sent to the client.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        send_d = fake_smsc.send_bytes(b"abc")
        # Bytes sent, not yet received.
        self.assertNoResult(send_d)
        self.assertEqual(client.received, b"")

        yield send_d
        # Bytes received.
        self.assertEqual(client.received, b"abc")
Exemplo n.º 19
0
    def test_bind_mode_RX(self):
        """
        FakeSMSC can accept receiver bind requests.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind()
        yield client.write(BindReceiver(0).get_bin())
        yield client.write(EnquireLink(1).get_bin())
        self.assertEqual(client.received, b"".join([
            BindReceiverResp(0).get_bin(),
            EnquireLinkResp(1).get_bin()]))
        yield wait0()
        self.successResultOf(bind_d)
Exemplo n.º 20
0
    def test_respond_to_enquire_link(self):
        """
        FakeSMSC can respond to an enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        rtel_d = fake_smsc.respond_to_enquire_link()
        yield client.write(EnquireLink(2).get_bin())
        # enquire_link response received.
        self.assertNoResult(rtel_d)
        self.assertEqual(client.received, EnquireLinkResp(2).get_bin())

        yield wait0()
        self.successResultOf(rtel_d)
Exemplo n.º 21
0
    def test_bind_mode_RX(self):
        """
        FakeSMSC can accept receiver bind requests.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind()
        yield client.write(BindReceiver(0).get_bin())
        yield client.write(EnquireLink(1).get_bin())
        self.assertEqual(
            client.received, b"".join(
                [BindReceiverResp(0).get_bin(),
                 EnquireLinkResp(1).get_bin()]))
        yield wait0()
        self.successResultOf(bind_d)
Exemplo n.º 22
0
    def test_await_connected(self):
        """
        The caller can wait for a client to connect.
        """
        fake_smsc = FakeSMSC(auto_accept=True)
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connected_d)
        self.assertEqual(self.client_factory.proto, None)
        self.assertEqual(fake_smsc._client_protocol, None)

        self.connect(fake_smsc)
        # The client has connected.
        self.successResultOf(await_connected_d)
        client = self.client_factory.proto
        self.assertNotEqual(client, None)
        self.assertEqual(fake_smsc._client_protocol, client)
        self.assertEqual(client.connected, True)
Exemplo n.º 23
0
    def test_await_connected(self):
        """
        The caller can wait for a client to connect.
        """
        fake_smsc = FakeSMSC(auto_accept=True)
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connected_d)
        self.assertEqual(self.client_factory.proto, None)
        self.assertEqual(fake_smsc._client_protocol, None)

        self.connect(fake_smsc)
        # The client has connected.
        self.successResultOf(await_connected_d)
        client = self.client_factory.proto
        self.assertNotEqual(client, None)
        self.assertEqual(fake_smsc._client_protocol, client)
        self.assertEqual(client.connected, True)
Exemplo n.º 24
0
    def test_await_disconnect(self):
        """
        FakeSMSC can wait for the connection to close.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        disconnect_d = fake_smsc.await_disconnect()
        yield wait0()
        self.assertNoResult(disconnect_d)

        client.transport.loseConnection()
        # Disconnect triggered, but not completed.
        self.assertNoResult(disconnect_d)

        yield wait0()
        # Disconnect completed.
        self.successResultOf(disconnect_d)
Exemplo n.º 25
0
    def test_await_disconnect(self):
        """
        FakeSMSC can wait for the connection to close.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        disconnect_d = fake_smsc.await_disconnect()
        yield wait0()
        self.assertNoResult(disconnect_d)

        client.transport.loseConnection()
        # Disconnect triggered, but not completed.
        self.assertNoResult(disconnect_d)

        yield wait0()
        # Disconnect completed.
        self.successResultOf(disconnect_d)
Exemplo n.º 26
0
    def test_send_pdu(self):
        """
        A PDU can be sent to the client over the wire.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [])

        pdu = DeliverSM(0)
        send_d = fake_smsc.send_pdu(pdu)
        # PDU sent, not yet received.
        self.assertNoResult(send_d)
        self.assertNotEqual(client.received, pdu.get_bin())

        yield send_d
        # PDU received.
        self.assertEqual(client.received, pdu.get_bin())
        self.assertEqual(client.pdus_handled, [])
Exemplo n.º 27
0
    def test_send_pdu(self):
        """
        A PDU can be sent to the client over the wire.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [])

        pdu = DeliverSM(0)
        send_d = fake_smsc.send_pdu(pdu)
        # PDU sent, not yet received.
        self.assertNoResult(send_d)
        self.assertNotEqual(client.received, pdu.get_bin())

        yield send_d
        # PDU received.
        self.assertEqual(client.received, pdu.get_bin())
        self.assertEqual(client.pdus_handled, [])
Exemplo n.º 28
0
    def test_send_mo(self):
        """
        FakeSMSC can send a DeliverSM PDU.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        yield fake_smsc.send_mo(5, "hello")
        # First MO received.
        self.assertEqual(client.received, DeliverSM(
            5, short_message="hello", data_coding=1).get_bin())
        client.received = b""

        yield fake_smsc.send_mo(6, "hello again", 8, destination_addr="123")
        # Second MO received.
        self.assertEqual(client.received, DeliverSM(
            6, short_message="hello again", data_coding=8,
            destination_addr="123").get_bin())
Exemplo n.º 29
0
    def test_await_connecting(self):
        """
        The caller can wait for a client connection attempt.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        self.assertNoResult(await_connecting_d)
        self.assertEqual(self.client_factory.proto, None)
        self.assertEqual(fake_smsc._client_protocol, None)

        connect_d = self.connect(fake_smsc)
        # The client connection has started ...
        self.successResultOf(await_connecting_d)
        client = self.client_factory.proto
        self.assertNotEqual(client, None)
        self.assertEqual(fake_smsc._client_protocol, client)
        # ... but has not yet been accepted.
        self.assertNoResult(connect_d)
        self.assertEqual(client.connected, False)
Exemplo n.º 30
0
    def test_await_pdu(self):
        """
        The caller can wait for a PDU to arrive.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        pdu_d = fake_smsc.await_pdu()
        # No PDU yet.
        self.assertNoResult(pdu_d)

        client.write(EnquireLink(1).get_bin())  # No yield.
        # PDU sent, not yet received.
        self.assertNoResult(pdu_d)

        yield wait0()
        # PDU received.
        self.assertEqual(self.successResultOf(pdu_d),
                         unpack_pdu(EnquireLink(1).get_bin()))
Exemplo n.º 31
0
    def test_await_connecting(self):
        """
        The caller can wait for a client connection attempt.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        self.assertNoResult(await_connecting_d)
        self.assertEqual(self.client_factory.proto, None)
        self.assertEqual(fake_smsc._client_protocol, None)

        connect_d = self.connect(fake_smsc)
        # The client connection has started ...
        self.successResultOf(await_connecting_d)
        client = self.client_factory.proto
        self.assertNotEqual(client, None)
        self.assertEqual(fake_smsc._client_protocol, client)
        # ... but has not yet been accepted.
        self.assertNoResult(connect_d)
        self.assertEqual(client.connected, False)
Exemplo n.º 32
0
    def test_await_pdu(self):
        """
        The caller can wait for a PDU to arrive.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        pdu_d = fake_smsc.await_pdu()
        # No PDU yet.
        self.assertNoResult(pdu_d)

        client.write(EnquireLink(1).get_bin())  # No yield.
        # PDU sent, not yet received.
        self.assertNoResult(pdu_d)

        yield wait0()
        # PDU received.
        self.assertEqual(
            self.successResultOf(pdu_d), unpack_pdu(EnquireLink(1).get_bin()))
Exemplo n.º 33
0
    def test_handle_pdu(self):
        """
        A PDU can be sent to the client for direct processing.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [])

        pdu = DeliverSM(0)
        handle_d = fake_smsc.handle_pdu(pdu)
        # PDU sent, not yet processed.
        self.assertNoResult(handle_d)
        self.assertEqual(client.pdus_handled, [])

        client.handle_pdu_d.callback(None)
        # PDU processed.
        self.successResultOf(handle_d)
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [pdu.obj])
Exemplo n.º 34
0
    def test_await_pdus(self):
        """
        The caller can wait for multiple PDUs to arrive.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        pdus_d = fake_smsc.await_pdus(2)
        # No PDUs yet.
        self.assertNoResult(pdus_d)

        yield client.write(EnquireLink(1).get_bin())
        # One PDU received, no result.
        self.assertNoResult(pdus_d)

        yield client.write(EnquireLink(2).get_bin())
        # Both PDUs received.
        self.assertEqual(self.successResultOf(pdus_d), [
            unpack_pdu(EnquireLink(1).get_bin()),
            unpack_pdu(EnquireLink(2).get_bin())])
Exemplo n.º 35
0
    def test_handle_pdu(self):
        """
        A PDU can be sent to the client for direct processing.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [])

        pdu = DeliverSM(0)
        handle_d = fake_smsc.handle_pdu(pdu)
        # PDU sent, not yet processed.
        self.assertNoResult(handle_d)
        self.assertEqual(client.pdus_handled, [])

        client.handle_pdu_d.callback(None)
        # PDU processed.
        self.successResultOf(handle_d)
        self.assertEqual(client.received, b"")
        self.assertEqual(client.pdus_handled, [pdu.obj])
Exemplo n.º 36
0
    def test_has_pending_connection(self):
        """
        FakeSMSC knows if there's a pending connection.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        self.assertEqual(fake_smsc.has_pending_connection(), False)

        # Pending connection we reject.
        connect_d = self.connect(fake_smsc)
        self.assertEqual(fake_smsc.has_pending_connection(), True)
        fake_smsc.reject_connection()
        self.assertEqual(fake_smsc.has_pending_connection(), False)
        self.failureResultOf(connect_d)

        # Pending connection we accept.
        connected_d = self.connect(fake_smsc)
        self.assertEqual(fake_smsc.has_pending_connection(), True)
        fake_smsc.accept_connection()
        self.assertEqual(fake_smsc.has_pending_connection(), False)
        self.successResultOf(connected_d)
Exemplo n.º 37
0
 def setUp(self):
     self.clock = Clock()
     self.persistence_helper = self.add_helper(PersistenceHelper())
     self.redis = yield self.persistence_helper.get_redis_manager()
     self.fake_smsc = FakeSMSC(auto_accept=False)
     self.default_config = {
         'transport_name': 'sphex_transport',
         'twisted_endpoint': self.fake_smsc.endpoint,
         'system_id': 'system_id',
         'password': '******',
     }
Exemplo n.º 38
0
    def test_await_pdus(self):
        """
        The caller can wait for multiple PDUs to arrive.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        pdus_d = fake_smsc.await_pdus(2)
        # No PDUs yet.
        self.assertNoResult(pdus_d)

        yield client.write(EnquireLink(1).get_bin())
        # One PDU received, no result.
        self.assertNoResult(pdus_d)

        yield client.write(EnquireLink(2).get_bin())
        # Both PDUs received.
        self.assertEqual(self.successResultOf(pdus_d), [
            unpack_pdu(EnquireLink(1).get_bin()),
            unpack_pdu(EnquireLink(2).get_bin())
        ])
Exemplo n.º 39
0
    def test_bind_explicit(self):
        """
        FakeSMSC can bind using a PDU explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind(BindTransceiver(0).obj)
        yield wait0()
        # Bind response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, BindTransceiverResp(0).get_bin())
        client.received = b""

        yield client.write(EnquireLink(1).get_bin())
        # enquire_link response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, EnquireLinkResp(1).get_bin())

        yield wait0()
        # Bind complete.
        self.successResultOf(bind_d)
Exemplo n.º 40
0
    def test_bind_explicit(self):
        """
        FakeSMSC can bind using a PDU explicitly passed in.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind(BindTransceiver(0).obj)
        yield wait0()
        # Bind response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, BindTransceiverResp(0).get_bin())
        client.received = b""

        yield client.write(EnquireLink(1).get_bin())
        # enquire_link response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, EnquireLinkResp(1).get_bin())

        yield wait0()
        # Bind complete.
        self.successResultOf(bind_d)
Exemplo n.º 41
0
    def test_send_mo(self):
        """
        FakeSMSC can send a DeliverSM PDU.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        yield fake_smsc.send_mo(5, "hello")
        # First MO received.
        self.assertEqual(
            client.received,
            DeliverSM(5, short_message="hello", data_coding=1).get_bin())
        client.received = b""

        yield fake_smsc.send_mo(6, "hello again", 8, destination_addr="123")
        # Second MO received.
        self.assertEqual(
            client.received,
            DeliverSM(6,
                      short_message="hello again",
                      data_coding=8,
                      destination_addr="123").get_bin())
Exemplo n.º 42
0
    def test_reject_connection(self):
        """
        With auto-accept disabled, a connection may be rejected.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connecting_d)
        self.assertNoResult(await_connected_d)

        connect_d = self.connect(fake_smsc)
        # The client connection is pending.
        self.successResultOf(await_connecting_d)
        self.assertNoResult(await_connected_d)
        self.assertNoResult(connect_d)
        client = self.client_factory.proto
        self.assertEqual(client.connected, False)

        fake_smsc.reject_connection()
        # The client is not connected.
        self.failureResultOf(connect_d, ConnectionRefusedError)
        self.assertNoResult(await_connected_d)
        self.assertEqual(client.connected, False)
Exemplo n.º 43
0
    def test_bind(self):
        """
        FakeSMSC can accept a bind request and respond to the first
        enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind()
        yield client.write(BindTransceiver(0).get_bin())
        # Bind response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, BindTransceiverResp(0).get_bin())
        client.received = b""

        yield client.write(EnquireLink(1).get_bin())
        # enquire_link response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, EnquireLinkResp(1).get_bin())

        yield wait0()
        # Bind complete.
        self.successResultOf(bind_d)
Exemplo n.º 44
0
    def test_bind(self):
        """
        FakeSMSC can accept a bind request and respond to the first
        enquire_link.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(client.received, b"")

        bind_d = fake_smsc.bind()
        yield client.write(BindTransceiver(0).get_bin())
        # Bind response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, BindTransceiverResp(0).get_bin())
        client.received = b""

        yield client.write(EnquireLink(1).get_bin())
        # enquire_link response received.
        self.assertNoResult(bind_d)
        self.assertEqual(client.received, EnquireLinkResp(1).get_bin())

        yield wait0()
        # Bind complete.
        self.successResultOf(bind_d)
Exemplo n.º 45
0
    def test_accept_connection(self):
        """
        With auto-accept disabled, a connection must be manually accepted.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connecting_d)
        self.assertNoResult(await_connected_d)

        connect_d = self.connect(fake_smsc)
        # The client connection is pending.
        self.successResultOf(await_connecting_d)
        self.assertNoResult(await_connected_d)
        self.assertNoResult(connect_d)
        client = self.client_factory.proto
        self.assertEqual(client.connected, False)

        accept_d = fake_smsc.accept_connection()
        # The client is connected.
        self.successResultOf(await_connected_d)
        self.successResultOf(accept_d)
        self.assertEqual(client.connected, True)
        self.assertEqual(self.successResultOf(connect_d), client)
Exemplo n.º 46
0
    def test_disconnect(self):
        """
        FakeSMSC can disconnect from the client.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(fake_smsc.connected, True)
        self.assertEqual(client.connected, True)

        disconnect_d = fake_smsc.disconnect()
        # Disconnect triggered, but not completed.
        self.assertNoResult(disconnect_d)
        self.assertEqual(client.connected, True)
        self.assertEqual(fake_smsc.connected, True)

        yield wait0()
        # Disconnect completed.
        self.successResultOf(disconnect_d)
        self.assertEqual(client.connected, False)
        self.assertEqual(fake_smsc.connected, False)
        self.assertEqual(fake_smsc.protocol, None)
        self.assertEqual(fake_smsc._client_protocol, None)
        self.assertNoResult(fake_smsc._listen_d)
        self.assertNoResult(fake_smsc._connected_d)
Exemplo n.º 47
0
    def test_accept_connection(self):
        """
        With auto-accept disabled, a connection must be manually accepted.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connecting_d)
        self.assertNoResult(await_connected_d)

        connect_d = self.connect(fake_smsc)
        # The client connection is pending.
        self.successResultOf(await_connecting_d)
        self.assertNoResult(await_connected_d)
        self.assertNoResult(connect_d)
        client = self.client_factory.proto
        self.assertEqual(client.connected, False)

        accept_d = fake_smsc.accept_connection()
        # The client is connected.
        self.successResultOf(await_connected_d)
        self.successResultOf(accept_d)
        self.assertEqual(client.connected, True)
        self.assertEqual(self.successResultOf(connect_d), client)
Exemplo n.º 48
0
    def test_disconnect(self):
        """
        FakeSMSC can disconnect from the client.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))
        self.assertEqual(fake_smsc.connected, True)
        self.assertEqual(client.connected, True)

        disconnect_d = fake_smsc.disconnect()
        # Disconnect triggered, but not completed.
        self.assertNoResult(disconnect_d)
        self.assertEqual(client.connected, True)
        self.assertEqual(fake_smsc.connected, True)

        yield wait0()
        # Disconnect completed.
        self.successResultOf(disconnect_d)
        self.assertEqual(client.connected, False)
        self.assertEqual(fake_smsc.connected, False)
        self.assertEqual(fake_smsc.protocol, None)
        self.assertEqual(fake_smsc._client_protocol, None)
        self.assertNoResult(fake_smsc._listen_d)
        self.assertNoResult(fake_smsc._connected_d)
Exemplo n.º 49
0
 def setUp(self):
     self.clock = Clock()
     self.fake_smsc = FakeSMSC()
     self.tx_helper = self.add_helper(TransportHelper(SmppTransceiverTransport))
     self.default_config = {
         "transport_name": self.tx_helper.transport_name,
         "twisted_endpoint": self.fake_smsc.endpoint,
         "deliver_short_message_processor": ("vumi.transports.smpp.processors.mica." "DeliverShortMessageProcessor"),
         "submit_short_message_processor": ("vumi.transports.smpp.processors.mica." "SubmitShortMessageProcessor"),
         "system_id": "foo",
         "password": "******",
         "deliver_short_message_processor_config": {"data_coding_overrides": {0: "utf-8"}},
         "submit_short_message_processor_config": {
             "submit_sm_encoding": "utf-16be",
             "submit_sm_data_coding": 8,
             "send_multipart_udh": True,
         },
     }
Exemplo n.º 50
0
    def test_reject_connection(self):
        """
        With auto-accept disabled, a connection may be rejected.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        await_connecting_d = fake_smsc.await_connecting()
        await_connected_d = fake_smsc.await_connected()
        self.assertNoResult(await_connecting_d)
        self.assertNoResult(await_connected_d)

        connect_d = self.connect(fake_smsc)
        # The client connection is pending.
        self.successResultOf(await_connecting_d)
        self.assertNoResult(await_connected_d)
        self.assertNoResult(connect_d)
        client = self.client_factory.proto
        self.assertEqual(client.connected, False)

        fake_smsc.reject_connection()
        # The client is not connected.
        self.failureResultOf(connect_d, ConnectionRefusedError)
        self.assertNoResult(await_connected_d)
        self.assertEqual(client.connected, False)
Exemplo n.º 51
0
class SixDeeProcessorTestCase(VumiTestCase):

    transport_class = SmppTransceiverTransport

    def setUp(self):
        self.clock = Clock()
        self.fake_smsc = FakeSMSC()
        self.tx_helper = self.add_helper(
            TransportHelper(SmppTransceiverTransport))
        self.default_config = {
            'transport_name': self.tx_helper.transport_name,
            'twisted_endpoint': self.fake_smsc.endpoint,
            'deliver_short_message_processor': (
                'vumi.transports.smpp.processors.sixdee.'
                'DeliverShortMessageProcessor'),
            'submit_short_message_processor': (
                'vumi.transports.smpp.processors.sixdee.'
                'SubmitShortMessageProcessor'),
            'system_id': 'foo',
            'password': '******',
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    0: 'utf-8',
                }
            },
            'submit_short_message_processor_config': {
                'submit_sm_encoding': 'utf-16be',
                'submit_sm_data_coding': 8,
                'send_multipart_udh': True,
            }
        }

    @inlineCallbacks
    def get_transport(self, deliver_config={}, submit_config={}, bind=True):
        cfg = self.default_config.copy()
        cfg['deliver_short_message_processor_config'].update(deliver_config)
        cfg['submit_short_message_processor_config'].update(submit_config)
        transport = yield self.tx_helper.get_transport(cfg, start=False)
        transport.clock = self.clock
        yield transport.startWorker()
        self.clock.advance(0)
        if bind:
            yield self.fake_smsc.bind()
        returnValue(transport)

    def assert_udh_parts(self, pdus, texts, encoding):
        def pdu_header(pdu):
            return short_message(pdu)[:6]

        def pdu_text(pdu):
            return short_message(pdu)[6:].decode(encoding)

        def udh_header(i):
            return '\x05\x00\x03\x03\x07' + chr(i)

        self.assertEqual(
            [(pdu_header(pdu), pdu_text(pdu)) for pdu in pdus],
            [(udh_header(i + 1), text) for i, text in enumerate(texts)])

    @inlineCallbacks
    def test_submit_sm_multipart_udh_ucs2(self):
        message = (
            "A cup is a small, open container used for carrying and "
            "drinking drinks. It may be made of wood, plastic, glass, "
            "clay, metal, stone, china or other materials, and may have "
            "a stem, handles or other adornments. Cups are used for "
            "drinking across a wide range of cultures and social classes, "
            "and different styles of cups may be used for different liquids "
            "or in different situations. Cups have been used for thousands "
            "of years for the ...Reply 1 for more")

        yield self.get_transport()
        yield self.tx_helper.make_dispatch_outbound(message, to_addr='msisdn')
        pdus = yield self.fake_smsc.await_pdus(7)
        self.assert_udh_parts(pdus, [
            ("A cup is a small, open container used"
             " for carrying and drinking d"),
            ("rinks. It may be made of wood, plastic,"
             " glass, clay, metal, stone"),
            (", china or other materials, and may have"
             " a stem, handles or other"),
            (" adornments. Cups are used for drinking"
             " across a wide range of cu"),
            ("ltures and social classes, and different"
             " styles of cups may be us"),
            ("ed for different liquids or in different"
             " situations. Cups have be"),
            ("en used for thousands of years for the ...Reply 1 for more"),
        ], encoding='utf-16be')  # utf-16be is close enough to UCS2
        for pdu in pdus:
            self.assertTrue(len(short_message(pdu)) < 140)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_new(self):
        session = SessionInfo()
        yield self.get_transport()

        # Server delivers a USSD message to the Client
        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        [mess] = yield self.tx_helper.wait_for_dispatched_inbound(1)

        self.assertEqual(mess['content'], None)
        self.assertEqual(mess['to_addr'], '*123#')
        self.assertEqual(mess['transport_type'], "ussd")
        self.assertEqual(mess['session_event'],
                         TransportUserMessage.SESSION_NEW)
        self.assertEqual(
            mess['transport_metadata'],
            {
                'session_info': {
                    'session_identifier': session.sixdee_id,
                    'ussd_service_op': '01',
                }
            })

    @inlineCallbacks
    def test_submit_and_deliver_ussd_new_custom_ussd_code_field(self):
        session = SessionInfo()
        yield self.get_transport(deliver_config={
            'ussd_code_pdu_field': 'destination_addr',
        })

        # Server delivers a USSD message to the Client
        pdu = DeliverSM(1, short_message="*IGNORE#", destination_addr="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        [mess] = yield self.tx_helper.wait_for_dispatched_inbound(1)

        self.assertEqual(mess['content'], None)
        self.assertEqual(mess['to_addr'], '*123#')
        self.assertEqual(mess['transport_type'], "ussd")
        self.assertEqual(mess['session_event'],
                         TransportUserMessage.SESSION_NEW)
        self.assertEqual(
            mess['transport_metadata'],
            {
                'session_info': {
                    'session_identifier': session.sixdee_id,
                    'ussd_service_op': '01',
                }
            })

    @inlineCallbacks
    def test_deliver_sm_op_codes_new(self):
        session = SessionInfo()
        yield self.get_transport()
        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [start] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(start['session_event'],
                         TransportUserMessage.SESSION_NEW)

    @inlineCallbacks
    def test_deliver_sm_op_codes_resume(self):
        session = SessionInfo()
        transport = yield self.get_transport()
        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager

        yield session_manager.create_session(
            session.vumi_id, ussd_code='*123#')

        pdu = DeliverSM(1, short_message="", source_addr=session.addr)
        pdu.add_optional_parameter('ussd_service_op', '12')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [resume] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(resume['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_deliver_sm_op_codes_end(self):
        session = SessionInfo()
        transport = yield self.get_transport()
        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager

        yield session_manager.create_session(
            session.vumi_id, ussd_code='*123#')

        pdu = DeliverSM(1, short_message="", source_addr=session.addr)
        pdu.add_optional_parameter('ussd_service_op', '81')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [end] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(end['session_event'],
                         TransportUserMessage.SESSION_CLOSE)

    @inlineCallbacks
    def test_deliver_sm_unknown_op_code(self):
        session = SessionInfo()
        yield self.get_transport()

        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '99')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)
        [start, unknown] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(unknown['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_submit_sm_op_codes_resume(self):
        session = SessionInfo()
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_RESUME,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            }, to_addr=session.addr)
        resume = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(resume, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(resume, 'its_session_info'), session.its_info)

    @inlineCallbacks
    def test_submit_sm_op_codes_close(self):
        session = SessionInfo(continue_session=False)
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_CLOSE,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            }, to_addr=session.addr)

        close = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(close, 'ussd_service_op'), '17')
        self.assertEqual(pdu_tlv(close, 'its_session_info'), session.its_info)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_continue(self):
        session = SessionInfo()
        transport = yield self.get_transport()

        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager
        yield session_manager.create_session(
            session.vumi_id, ussd_code='*123#')

        yield self.tx_helper.make_dispatch_outbound(
            "hello world", transport_type="ussd", transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            }, to_addr=session.addr)

        submit_sm_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'its_session_info'),
                         session.its_info)

        # Server delivers a USSD message to the Client
        pdu = DeliverSM(seq_no(submit_sm_pdu) + 1, short_message="reply!",
                        source_addr=session.addr)
        # 0x12 is 'continue'
        pdu.add_optional_parameter('ussd_service_op', '12')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        [mess] = yield self.tx_helper.wait_for_dispatched_inbound(1)

        self.assertEqual(mess['content'], "reply!")
        self.assertEqual(mess['transport_type'], "ussd")
        self.assertEqual(mess['to_addr'], '*123#')
        self.assertEqual(mess['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_close(self):
        session = SessionInfo(continue_session=False)
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world", transport_type="ussd",
            session_event=TransportUserMessage.SESSION_CLOSE,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            })

        submit_sm_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'ussd_service_op'), '17')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'its_session_info'),
                         session.its_info)

    @inlineCallbacks
    def test_submit_sm_null_message(self):
        """
        We can successfully send a message with null content.
        """
        session = SessionInfo()
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            None,
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_RESUME,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            }, to_addr=session.addr)
        resume = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(resume, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(resume, 'its_session_info'), session.its_info)
Exemplo n.º 52
0
class SixDeeProcessorTestCase(VumiTestCase):

    transport_class = SmppTransceiverTransport

    def setUp(self):
        self.clock = Clock()
        self.fake_smsc = FakeSMSC()
        self.tx_helper = self.add_helper(
            TransportHelper(SmppTransceiverTransport))
        self.default_config = {
            'transport_name':
            self.tx_helper.transport_name,
            'twisted_endpoint':
            self.fake_smsc.endpoint,
            'deliver_short_message_processor':
            ('vumi.transports.smpp.processors.sixdee.'
             'DeliverShortMessageProcessor'),
            'submit_short_message_processor':
            ('vumi.transports.smpp.processors.sixdee.'
             'SubmitShortMessageProcessor'),
            'system_id':
            'foo',
            'password':
            '******',
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    0: 'utf-8',
                }
            },
            'submit_short_message_processor_config': {
                'submit_sm_encoding': 'utf-16be',
                'submit_sm_data_coding': 8,
                'send_multipart_udh': True,
            }
        }

    @inlineCallbacks
    def get_transport(self, config={}, bind=True):
        cfg = self.default_config.copy()
        transport = yield self.tx_helper.get_transport(cfg, start=False)
        transport.clock = self.clock
        yield transport.startWorker()
        self.clock.advance(0)
        if bind:
            yield self.fake_smsc.bind()
        returnValue(transport)

    def assert_udh_parts(self, pdus, texts, encoding):
        pdu_header = lambda pdu: short_message(pdu)[:6]
        pdu_text = lambda pdu: short_message(pdu)[6:].decode(encoding)
        udh_header = lambda i: '\x05\x00\x03\x03\x07' + chr(i)
        self.assertEqual([(pdu_header(pdu), pdu_text(pdu)) for pdu in pdus],
                         [(udh_header(i + 1), text)
                          for i, text in enumerate(texts)])

    @inlineCallbacks
    def test_submit_sm_multipart_udh_ucs2(self):
        message = (
            "A cup is a small, open container used for carrying and "
            "drinking drinks. It may be made of wood, plastic, glass, "
            "clay, metal, stone, china or other materials, and may have "
            "a stem, handles or other adornments. Cups are used for "
            "drinking across a wide range of cultures and social classes, "
            "and different styles of cups may be used for different liquids "
            "or in different situations. Cups have been used for thousands "
            "of years for the ...Reply 1 for more")

        yield self.get_transport()
        yield self.tx_helper.make_dispatch_outbound(message, to_addr='msisdn')
        pdus = yield self.fake_smsc.await_pdus(7)
        self.assert_udh_parts(
            pdus, [
                ("A cup is a small, open container used"
                 " for carrying and drinking d"),
                ("rinks. It may be made of wood, plastic,"
                 " glass, clay, metal, stone"),
                (", china or other materials, and may have"
                 " a stem, handles or other"),
                (" adornments. Cups are used for drinking"
                 " across a wide range of cu"),
                ("ltures and social classes, and different"
                 " styles of cups may be us"),
                ("ed for different liquids or in different"
                 " situations. Cups have be"),
                ("en used for thousands of years for the ...Reply 1 for more"),
            ],
            encoding='utf-16be')  # utf-16be is close enough to UCS2
        for pdu in pdus:
            self.assertTrue(len(short_message(pdu)) < 140)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_new(self):
        session = SessionInfo()
        yield self.get_transport()

        # Server delivers a USSD message to the Client
        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        [mess] = yield self.tx_helper.wait_for_dispatched_inbound(1)

        self.assertEqual(mess['content'], None)
        self.assertEqual(mess['to_addr'], '*123#')
        self.assertEqual(mess['transport_type'], "ussd")
        self.assertEqual(mess['session_event'],
                         TransportUserMessage.SESSION_NEW)
        self.assertEqual(
            mess['transport_metadata'], {
                'session_info': {
                    'session_identifier': session.sixdee_id,
                    'ussd_service_op': '01',
                }
            })

    @inlineCallbacks
    def test_deliver_sm_op_codes_new(self):
        session = SessionInfo()
        yield self.get_transport()
        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [start] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(start['session_event'],
                         TransportUserMessage.SESSION_NEW)

    @inlineCallbacks
    def test_deliver_sm_op_codes_resume(self):
        session = SessionInfo()
        transport = yield self.get_transport()
        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager

        yield session_manager.create_session(session.vumi_id,
                                             ussd_code='*123#')

        pdu = DeliverSM(1, short_message="", source_addr=session.addr)
        pdu.add_optional_parameter('ussd_service_op', '12')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [resume] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(resume['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_deliver_sm_op_codes_end(self):
        session = SessionInfo()
        transport = yield self.get_transport()
        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager

        yield session_manager.create_session(session.vumi_id,
                                             ussd_code='*123#')

        pdu = DeliverSM(1, short_message="", source_addr=session.addr)
        pdu.add_optional_parameter('ussd_service_op', '81')
        pdu.add_optional_parameter('its_session_info', session.its_info)
        yield self.fake_smsc.handle_pdu(pdu)
        [end] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(end['session_event'],
                         TransportUserMessage.SESSION_CLOSE)

    @inlineCallbacks
    def test_deliver_sm_unknown_op_code(self):
        session = SessionInfo()
        yield self.get_transport()

        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '01')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        pdu = DeliverSM(1, short_message="*123#")
        pdu.add_optional_parameter('ussd_service_op', '99')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)
        [start, unknown] = yield self.tx_helper.wait_for_dispatched_inbound(1)
        self.assertEqual(unknown['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_submit_sm_op_codes_resume(self):
        session = SessionInfo()
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_RESUME,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            },
            to_addr=session.addr)
        resume = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(resume, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(resume, 'its_session_info'), session.its_info)

    @inlineCallbacks
    def test_submit_sm_op_codes_close(self):
        session = SessionInfo(continue_session=False)
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_CLOSE,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            },
            to_addr=session.addr)

        close = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(close, 'ussd_service_op'), '17')
        self.assertEqual(pdu_tlv(close, 'its_session_info'), session.its_info)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_continue(self):
        session = SessionInfo()
        transport = yield self.get_transport()

        deliver_sm_processor = transport.deliver_sm_processor
        session_manager = deliver_sm_processor.session_manager
        yield session_manager.create_session(session.vumi_id,
                                             ussd_code='*123#')

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            },
            to_addr=session.addr)

        submit_sm_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'its_session_info'),
                         session.its_info)

        # Server delivers a USSD message to the Client
        pdu = DeliverSM(seq_no(submit_sm_pdu) + 1,
                        short_message="reply!",
                        source_addr=session.addr)
        # 0x12 is 'continue'
        pdu.add_optional_parameter('ussd_service_op', '12')
        pdu.add_optional_parameter('its_session_info', session.its_info)

        yield self.fake_smsc.handle_pdu(pdu)

        [mess] = yield self.tx_helper.wait_for_dispatched_inbound(1)

        self.assertEqual(mess['content'], "reply!")
        self.assertEqual(mess['transport_type'], "ussd")
        self.assertEqual(mess['to_addr'], '*123#')
        self.assertEqual(mess['session_event'],
                         TransportUserMessage.SESSION_RESUME)

    @inlineCallbacks
    def test_submit_and_deliver_ussd_close(self):
        session = SessionInfo(continue_session=False)
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            "hello world",
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_CLOSE,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            })

        submit_sm_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'ussd_service_op'), '17')
        self.assertEqual(pdu_tlv(submit_sm_pdu, 'its_session_info'),
                         session.its_info)

    @inlineCallbacks
    def test_submit_sm_null_message(self):
        """
        We can successfully send a message with null content.
        """
        session = SessionInfo()
        yield self.get_transport()

        yield self.tx_helper.make_dispatch_outbound(
            None,
            transport_type="ussd",
            session_event=TransportUserMessage.SESSION_RESUME,
            transport_metadata={
                'session_info': {
                    'session_identifier': session.sixdee_id,
                }
            },
            to_addr=session.addr)
        resume = yield self.fake_smsc.await_pdu()
        self.assertEqual(pdu_tlv(resume, 'ussd_service_op'), '02')
        self.assertEqual(pdu_tlv(resume, 'its_session_info'), session.its_info)
Exemplo n.º 53
0
 def setUp(self):
     self.fake_smsc = FakeSMSC()
     self.tx_helper = self.add_helper(
         TransportHelper(SmppTransceiverTransport))
     self.clock = Clock()
Exemplo n.º 54
0
class DefaultProcessorTestCase(VumiTestCase):
    def setUp(self):
        self.fake_smsc = FakeSMSC()
        self.tx_helper = self.add_helper(
            TransportHelper(SmppTransceiverTransport))
        self.clock = Clock()

    @inlineCallbacks
    def get_transport(self, config):
        transport = yield self.tx_helper.get_transport(config, start=False)
        transport.clock = self.clock
        yield transport.startWorker()
        self.clock.advance(0)
        yield self.fake_smsc.bind()
        returnValue(transport)

    @inlineCallbacks
    def test_data_coding_override_keys_ints(self):
        """
        If the keys of the data coding overrides config dictionary are not
        integers, they should be cast to integers.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    '0': 'utf-8'
                },
            },
        }
        transport = yield self.tx_helper.get_transport(config)
        self.assertEqual(transport.deliver_sm_processor.data_coding_map.get(0),
                         'utf-8')

    @inlineCallbacks
    def test_data_coding_override_keys_invalid(self):
        """
        If the keys of the data coding overrides config dictionary can not be
        cast to integers, a config error with an appropriate message should
        be raised.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    'not-an-int': 'utf-8'
                },
            },
        }
        try:
            yield self.tx_helper.get_transport(config)
        except ConfigError as e:
            self.assertEqual(
                str(e), "data_coding_overrides keys must be castable to ints. "
                "invalid literal for int() with base 10: 'not-an-int'")
        else:
            raise FailTest("Expected ConfigError to be raised")

    @inlineCallbacks
    def test_multipart_sar_reference_rollover(self):
        """
        If the multipart_sar_reference_rollover config value is set, then for
        multipart messages, the reference should rollover at that value.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'submit_short_message_processor_config': {
                'send_multipart_sar': True,
                'multipart_sar_reference_rollover': 0xFF,
            },
        }
        transport = yield self.get_transport(config)
        transport.service.sequence_generator.redis.set(
            'smpp_last_sequence_number', 0xFF)

        yield transport.submit_sm_processor.send_short_message(
            transport.service,
            'test-id',
            '+1234',
            'test message ' * 20,
            optional_parameters={})
        pdus = yield self.fake_smsc.await_pdus(2)

        msg_refs = [unpacked_pdu_opts(p)['sar_msg_ref_num'] for p in pdus]
        self.assertEqual(msg_refs, [1, 1])
Exemplo n.º 55
0
    def test_has_pending_connection(self):
        """
        FakeSMSC knows if there's a pending connection.
        """
        fake_smsc = FakeSMSC(auto_accept=False)
        self.assertEqual(fake_smsc.has_pending_connection(), False)

        # Pending connection we reject.
        connect_d = self.connect(fake_smsc)
        self.assertEqual(fake_smsc.has_pending_connection(), True)
        fake_smsc.reject_connection()
        self.assertEqual(fake_smsc.has_pending_connection(), False)
        self.failureResultOf(connect_d)

        # Pending connection we accept.
        connected_d = self.connect(fake_smsc)
        self.assertEqual(fake_smsc.has_pending_connection(), True)
        fake_smsc.accept_connection()
        self.assertEqual(fake_smsc.has_pending_connection(), False)
        self.successResultOf(connected_d)
Exemplo n.º 56
0
class TestSmppService(VumiTestCase):
    @inlineCallbacks
    def setUp(self):
        self.clock = Clock()
        self.persistence_helper = self.add_helper(PersistenceHelper())
        self.redis = yield self.persistence_helper.get_redis_manager()
        self.fake_smsc = FakeSMSC(auto_accept=False)
        self.default_config = {
            'transport_name': 'sphex_transport',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'system_id': 'system_id',
            'password': '******',
        }

    def get_service(self, config={}, bind_type='TRX', start=True):
        """
        Create and optionally start a new service object.
        """
        cfg = self.default_config.copy()
        cfg.update(config)
        dummy_transport = DummySmppTransport(self.clock, self.redis, cfg)
        service = SmppService(self.fake_smsc.endpoint, bind_type,
                              dummy_transport)
        service.clock = self.clock

        d = succeed(service)
        if start:
            d.addCallback(self.start_service)
        return d

    def start_service(self, service, accept_connection=True):
        """
        Start the given service.
        """
        service.startService()
        self.clock.advance(0)
        d = self.fake_smsc.await_connecting()
        if accept_connection:
            d.addCallback(lambda _: self.fake_smsc.accept_connection())
        return d.addCallback(lambda _: service)

    def lookup_message_ids(self, service, seq_nums):
        """
        Find vumi message ids associated with SMPP sequence numbers.
        """
        lookup_func = service.message_stash.get_sequence_number_message_id
        return gatherResults([lookup_func(seq_num) for seq_num in seq_nums])

    def set_sequence_number(self, service, seq_nr):
        return service.sequence_generator.redis.set(
            'smpp_last_sequence_number', seq_nr)

    @inlineCallbacks
    def test_start_sequence(self):
        """
        The service goes through several states while starting.
        """
        # New service, never started.
        service = yield self.get_service(start=False)
        self.assertEqual(service.running, False)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.CLOSED_STATE)

        # Start, but don't connect.
        yield self.start_service(service, accept_connection=False)
        self.assertEqual(service.running, True)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.CLOSED_STATE)

        # Connect, but don't bind.
        yield self.fake_smsc.accept_connection()
        self.assertEqual(service.running, True)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.OPEN_STATE)
        bind_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(bind_pdu), 'bind_transceiver')

        # Bind.
        yield self.fake_smsc.bind(bind_pdu)
        self.assertEqual(service.running, True)
        self.assertEqual(service.get_bind_state(),
                         EsmeProtocol.BOUND_STATE_TRX)

    @inlineCallbacks
    def test_connect_retries(self):
        """
        If we fail to connect, we retry.
        """
        service = yield self.get_service(start=False)
        self.assertEqual(self.fake_smsc.has_pending_connection(), False)

        # Start, but don't connect.
        yield self.start_service(service, accept_connection=False)
        self.assertEqual(self.fake_smsc.has_pending_connection(), True)
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 1)

        # Reject the connection.
        yield self.fake_smsc.reject_connection()
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 2)

        # Advance to the next connection attempt.
        self.clock.advance(service.delay)
        self.assertEqual(self.fake_smsc.has_pending_connection(), True)
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 2)

        # Accept the connection.
        yield self.fake_smsc.accept_connection()
        self.assertEqual(service.running, True)
        self.assertNotEqual(service._protocol, None)

    @inlineCallbacks
    def test_submit_sm(self):
        """
        When bound, we can send a message.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        seq_nums = yield service.submit_sm('abc123',
                                           'dest_addr',
                                           short_message='foo')
        submit_sm = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm), 'submit_sm')
        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'], stored_ids)

    @inlineCallbacks
    def test_submit_sm_unbound(self):
        """
        When unbound, we can't send a message.
        """
        service = yield self.get_service()

        self.assertRaises(EsmeProtocolError,
                          service.submit_sm,
                          'abc123',
                          'dest_addr',
                          short_message='foo')

    @inlineCallbacks
    def test_submit_sm_not_connected(self):
        """
        When not connected, we can't send a message.
        """
        service = yield self.get_service(start=False)
        yield self.start_service(service, accept_connection=False)

        self.assertRaises(EsmeProtocolError,
                          service.submit_sm,
                          'abc123',
                          'dest_addr',
                          short_message='foo')

    @skiptest("FIXME: We don't actually unbind and disconnect yet.")
    @inlineCallbacks
    def test_handle_unbind(self):
        """
        If the SMSC sends an unbind command, we respond and disconnect.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        self.assertEqual(service.is_bound(), True)
        self.fake_smsc.send_pdu(Unbind(7))
        unbind_resp_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(unbind_resp_pdu), 'unbind_resp')
        self.assertEqual(service.is_bound(), False)

    @inlineCallbacks
    def test_csm_split_message(self):
        """
        A multipart message is split into chunks such that the smallest number
        of message parts are required.
        """
        service = yield self.get_service()

        split = lambda msg: service.csm_split_message(msg.encode('utf-8'))

        # these are fine because they're in the 7-bit character set
        self.assertEqual(1, len(split(u'&' * 140)))
        self.assertEqual(1, len(split(u'&' * 160)))
        # ± is not in the 7-bit character set so it should utf-8 encode it
        # which bumps it over the 140 bytes
        self.assertEqual(2, len(split(u'±' + u'1' * 139)))

    @inlineCallbacks
    def test_submit_sm_long(self):
        """
        A long message can be sent in a single PDU using the optional
        `message_payload` PDU field.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_sm_long('abc123', 'dest_addr',
                                                long_message)
        submit_sm = yield self.fake_smsc.await_pdu()
        pdu_opts = unpacked_pdu_opts(submit_sm)

        self.assertEqual('submit_sm', submit_sm['header']['command_id'])
        self.assertEqual(
            None, submit_sm['body']['mandatory_parameters']['short_message'])
        self.assertEqual(''.join('%02x' % ord(c) for c in long_message),
                         pdu_opts['message_payload'])
        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'], stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar(self):
        """
        A long message can be sent in multiple PDUs with SAR fields set to
        instruct the SMSC to build user data headers.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_csm_sar('abc123',
                                                'dest_addr',
                                                short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        # seq no 1 == bind_transceiver, 2 == enquire_link, 3 == sar_msg_ref_num
        self.assertEqual([4, 5, 6, 7], seq_nums)
        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            pdu_opts = unpacked_pdu_opts(sm)
            mandatory_parameters = sm['body']['mandatory_parameters']

            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg_parts.append(mandatory_parameters['short_message'])
            self.assertTrue(len(mandatory_parameters['short_message']) <= 130)
            msg_refs.append(pdu_opts['sar_msg_ref_num'])
            self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
            self.assertEqual(4, pdu_opts['sar_total_segments'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual([3, 3, 3, 3], msg_refs)

        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'] * len(seq_nums), stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar_ref_num_limit(self):
        """
        The SAR reference number is set correctly when the generated reference
        number is larger than 0xFFFF.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()
        # forward until we go past 0xFFFF
        yield self.set_sequence_number(service, 0x10000)

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_csm_sar('abc123',
                                                'dest_addr',
                                                short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            pdu_opts = unpacked_pdu_opts(sm)
            mandatory_parameters = sm['body']['mandatory_parameters']

            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg_parts.append(mandatory_parameters['short_message'])
            self.assertTrue(len(mandatory_parameters['short_message']) <= 130)
            msg_refs.append(pdu_opts['sar_msg_ref_num'])
            self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
            self.assertEqual(4, pdu_opts['sar_total_segments'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual([1, 1, 1, 1], msg_refs)

        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'] * len(seq_nums), stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar_ref_num_custom_limit(self):
        """
        The SAR reference number is set correctly when the generated reference
        number is larger than the configured limit.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()
        # forward until we go past 0xFF
        yield self.set_sequence_number(service, 0x100)

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_csm_sar('abc123',
                                                'dest_addr',
                                                short_message=long_message,
                                                reference_rollover=0x100)
        pdus = yield self.fake_smsc.await_pdus(4)
        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            pdu_opts = unpacked_pdu_opts(sm)
            mandatory_parameters = sm['body']['mandatory_parameters']

            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg_parts.append(mandatory_parameters['short_message'])
            self.assertTrue(len(mandatory_parameters['short_message']) <= 130)
            msg_refs.append(pdu_opts['sar_msg_ref_num'])
            self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
            self.assertEqual(4, pdu_opts['sar_total_segments'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual([1, 1, 1, 1], msg_refs)

        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'] * len(seq_nums), stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar_single_part(self):
        """
        If the content fits in a single message, all the multipart madness is
        avoided.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()

        content = 'a' * 160
        seq_numbers = yield service.submit_csm_sar('abc123',
                                                   'dest_addr',
                                                   short_message=content)
        self.assertEqual(len(seq_numbers), 1)
        submit_sm_pdu = yield self.fake_smsc.await_pdu()

        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(short_message(submit_sm_pdu), content)
        self.assertEqual(unpacked_pdu_opts(submit_sm_pdu), {})

    @inlineCallbacks
    def test_submit_csm_udh(self):
        """
        A long message can be sent in multiple PDUs with carefully handcrafted
        user data headers.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_numbers = yield service.submit_csm_udh('abc123',
                                                   'dest_addr',
                                                   short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        self.assertEqual(len(seq_numbers), 4)

        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            mandatory_parameters = sm['body']['mandatory_parameters']
            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg = mandatory_parameters['short_message']

            udh_hlen, udh_tag, udh_len, udh_ref, udh_tot, udh_seq = [
                ord(octet) for octet in msg[:6]
            ]
            self.assertEqual(5, udh_hlen)
            self.assertEqual(0, udh_tag)
            self.assertEqual(3, udh_len)
            msg_refs.append(udh_ref)
            self.assertEqual(4, udh_tot)
            self.assertEqual(i + 1, udh_seq)
            self.assertTrue(len(msg) <= 136)
            msg_parts.append(msg[6:])
            self.assertEqual(0x40, mandatory_parameters['esm_class'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual(1, len(set(msg_refs)))

        stored_ids = yield self.lookup_message_ids(service, seq_numbers)
        self.assertEqual(['abc123'] * len(seq_numbers), stored_ids)

    @inlineCallbacks
    def test_submit_csm_udh_ref_num_limit(self):
        """
        User data headers are crafted correctly when the generated reference
        number is larger than 0xFF.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()
        # forward until we go past 0xFF
        yield self.set_sequence_number(service, 0x100)

        long_message = 'This is a long message.' * 20
        seq_numbers = yield service.submit_csm_udh('abc123',
                                                   'dest_addr',
                                                   short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        self.assertEqual(len(seq_numbers), 4)

        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            mandatory_parameters = sm['body']['mandatory_parameters']
            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg = mandatory_parameters['short_message']

            udh_hlen, udh_tag, udh_len, udh_ref, udh_tot, udh_seq = [
                ord(octet) for octet in msg[:6]
            ]
            self.assertEqual(5, udh_hlen)
            self.assertEqual(0, udh_tag)
            self.assertEqual(3, udh_len)
            msg_refs.append(udh_ref)
            self.assertEqual(4, udh_tot)
            self.assertEqual(i + 1, udh_seq)
            self.assertTrue(len(msg) <= 136)
            msg_parts.append(msg[6:])
            self.assertEqual(0x40, mandatory_parameters['esm_class'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual(1, len(set(msg_refs)))

        stored_ids = yield self.lookup_message_ids(service, seq_numbers)
        self.assertEqual(['abc123'] * len(seq_numbers), stored_ids)

    @inlineCallbacks
    def test_submit_csm_udh_single_part(self):
        """
        If the content fits in a single message, all the multipart madness is
        avoided.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()

        content = 'a' * 160
        seq_numbers = yield service.submit_csm_udh('abc123',
                                                   'dest_addr',
                                                   short_message=content)
        self.assertEqual(len(seq_numbers), 1)
        submit_sm_pdu = yield self.fake_smsc.await_pdu()

        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(short_message(submit_sm_pdu), content)
        self.assertEqual(
            submit_sm_pdu['body']['mandatory_parameters']['esm_class'], 0)

    @inlineCallbacks
    def test_pdu_cache_persistence(self):
        """
        A cached PDU has an appropriate TTL and can be deleted.
        """
        service = yield self.get_service()

        message_stash = service.message_stash
        config = service.get_config()

        pdu = SubmitSM(1337, short_message="foo")
        yield message_stash.cache_pdu("vumi0", pdu)

        ttl = yield message_stash.redis.ttl(pdu_key(1337))
        self.assertTrue(0 < ttl <= config.submit_sm_expiry)

        pdu_data = yield message_stash.get_cached_pdu(1337)
        self.assertEqual(pdu_data.vumi_message_id, "vumi0")
        self.assertEqual(pdu_data.pdu.get_hex(), pdu.get_hex())

        yield message_stash.delete_cached_pdu(1337)
        deleted_pdu_data = yield message_stash.get_cached_pdu(1337)
        self.assertEqual(deleted_pdu_data, None)
Exemplo n.º 57
0
 def setUp(self):
     self.fake_smsc = FakeSMSC()
     self.tx_helper = self.add_helper(
         TransportHelper(SmppTransceiverTransport))
     self.clock = Clock()
Exemplo n.º 58
0
class DefaultProcessorTestCase(VumiTestCase):
    def setUp(self):
        self.fake_smsc = FakeSMSC()
        self.tx_helper = self.add_helper(
            TransportHelper(SmppTransceiverTransport))
        self.clock = Clock()

    @inlineCallbacks
    def get_transport(self, config):
        transport = yield self.tx_helper.get_transport(config, start=False)
        transport.clock = self.clock
        yield transport.startWorker()
        self.clock.advance(0)
        yield self.fake_smsc.bind()
        returnValue(transport)

    @inlineCallbacks
    def test_data_coding_override_keys_ints(self):
        """
        If the keys of the data coding overrides config dictionary are not
        integers, they should be cast to integers.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    '0': 'utf-8'
                },
            },
        }
        transport = yield self.tx_helper.get_transport(config)
        self.assertEqual(
            transport.deliver_sm_processor.data_coding_map.get(0), 'utf-8')

    @inlineCallbacks
    def test_data_coding_override_keys_invalid(self):
        """
        If the keys of the data coding overrides config dictionary can not be
        cast to integers, a config error with an appropriate message should
        be raised.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'deliver_short_message_processor_config': {
                'data_coding_overrides': {
                    'not-an-int': 'utf-8'
                },
            },
        }
        try:
            yield self.tx_helper.get_transport(config)
        except ConfigError as e:
            self.assertEqual(
                str(e),
                "data_coding_overrides keys must be castable to ints. "
                "invalid literal for int() with base 10: 'not-an-int'"
            )
        else:
            raise FailTest("Expected ConfigError to be raised")

    @inlineCallbacks
    def test_multipart_sar_reference_rollover(self):
        """
        If the multipart_sar_reference_rollover config value is set, then for
        multipart messages, the reference should rollover at that value.
        """
        config = {
            'system_id': 'foo',
            'password': '******',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'submit_short_message_processor_config': {
                'send_multipart_sar': True,
                'multipart_sar_reference_rollover': 0xFF,
            },
        }
        transport = yield self.get_transport(config)
        transport.service.sequence_generator.redis.set(
            'smpp_last_sequence_number', 0xFF)

        yield transport.submit_sm_processor.send_short_message(
            transport.service, 'test-id', '+1234', 'test message ' * 20,
            optional_parameters={})
        pdus = yield self.fake_smsc.await_pdus(2)

        msg_refs = [unpacked_pdu_opts(p)['sar_msg_ref_num'] for p in pdus]
        self.assertEqual(msg_refs, [1, 1])
Exemplo n.º 59
0
    def test_waiting_pdu_count(self):
        """
        FakeSMSC knows how many received PDUs are waiting.
        """
        fake_smsc = FakeSMSC()
        client = self.successResultOf(self.connect(fake_smsc))

        # Nothing received yet.
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)

        # Some PDUs received.
        yield client.write(EnquireLink(1).get_bin())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 1)
        yield client.write(EnquireLink(2).get_bin())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 2)

        # Some PDUs returned.
        self.successResultOf(fake_smsc.await_pdu())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 1)
        self.successResultOf(fake_smsc.await_pdu())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)

        # Wait for a PDU that arrives later.
        pdu_d = fake_smsc.await_pdu()
        self.assertNoResult(pdu_d)
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)
        yield client.write(EnquireLink(3).get_bin())
        self.assertEqual(fake_smsc.waiting_pdu_count(), 0)
        self.successResultOf(pdu_d)
Exemplo n.º 60
0
class TestSmppService(VumiTestCase):

    @inlineCallbacks
    def setUp(self):
        self.clock = Clock()
        self.persistence_helper = self.add_helper(PersistenceHelper())
        self.redis = yield self.persistence_helper.get_redis_manager()
        self.fake_smsc = FakeSMSC(auto_accept=False)
        self.default_config = {
            'transport_name': 'sphex_transport',
            'twisted_endpoint': self.fake_smsc.endpoint,
            'system_id': 'system_id',
            'password': '******',
        }

    def get_service(self, config={}, bind_type='TRX', start=True):
        """
        Create and optionally start a new service object.
        """
        cfg = self.default_config.copy()
        cfg.update(config)
        dummy_transport = DummySmppTransport(self.clock, self.redis, cfg)
        service = SmppService(
            self.fake_smsc.endpoint, bind_type, dummy_transport)
        service.clock = self.clock

        d = succeed(service)
        if start:
            d.addCallback(self.start_service)
        return d

    def start_service(self, service, accept_connection=True):
        """
        Start the given service.
        """
        service.startService()
        self.clock.advance(0)
        d = self.fake_smsc.await_connecting()
        if accept_connection:
            d.addCallback(lambda _: self.fake_smsc.accept_connection())
        return d.addCallback(lambda _: service)

    def lookup_message_ids(self, service, seq_nums):
        """
        Find vumi message ids associated with SMPP sequence numbers.
        """
        lookup_func = service.message_stash.get_sequence_number_message_id
        return gatherResults([lookup_func(seq_num) for seq_num in seq_nums])

    def set_sequence_number(self, service, seq_nr):
        return service.sequence_generator.redis.set(
            'smpp_last_sequence_number', seq_nr)

    @inlineCallbacks
    def test_start_sequence(self):
        """
        The service goes through several states while starting.
        """
        # New service, never started.
        service = yield self.get_service(start=False)
        self.assertEqual(service.running, False)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.CLOSED_STATE)

        # Start, but don't connect.
        yield self.start_service(service, accept_connection=False)
        self.assertEqual(service.running, True)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.CLOSED_STATE)

        # Connect, but don't bind.
        yield self.fake_smsc.accept_connection()
        self.assertEqual(service.running, True)
        self.assertEqual(service.get_bind_state(), EsmeProtocol.OPEN_STATE)
        bind_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(bind_pdu), 'bind_transceiver')

        # Bind.
        yield self.fake_smsc.bind(bind_pdu)
        self.assertEqual(service.running, True)
        self.assertEqual(
            service.get_bind_state(), EsmeProtocol.BOUND_STATE_TRX)

    @inlineCallbacks
    def test_connect_retries(self):
        """
        If we fail to connect, we retry.
        """
        service = yield self.get_service(start=False)
        self.assertEqual(self.fake_smsc.has_pending_connection(), False)

        # Start, but don't connect.
        yield self.start_service(service, accept_connection=False)
        self.assertEqual(self.fake_smsc.has_pending_connection(), True)
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 1)

        # Reject the connection.
        yield self.fake_smsc.reject_connection()
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 2)

        # Advance to the next connection attempt.
        self.clock.advance(service.delay)
        self.assertEqual(self.fake_smsc.has_pending_connection(), True)
        self.assertEqual(service._protocol, None)
        self.assertEqual(service.retries, 2)

        # Accept the connection.
        yield self.fake_smsc.accept_connection()
        self.assertEqual(service.running, True)
        self.assertNotEqual(service._protocol, None)

    @inlineCallbacks
    def test_submit_sm(self):
        """
        When bound, we can send a message.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        seq_nums = yield service.submit_sm(
            'abc123', 'dest_addr', short_message='foo')
        submit_sm = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(submit_sm), 'submit_sm')
        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'], stored_ids)

    @inlineCallbacks
    def test_submit_sm_unbound(self):
        """
        When unbound, we can't send a message.
        """
        service = yield self.get_service()

        self.assertRaises(
            EsmeProtocolError,
            service.submit_sm, 'abc123', 'dest_addr', short_message='foo')

    @inlineCallbacks
    def test_submit_sm_not_connected(self):
        """
        When not connected, we can't send a message.
        """
        service = yield self.get_service(start=False)
        yield self.start_service(service, accept_connection=False)

        self.assertRaises(
            EsmeProtocolError,
            service.submit_sm, 'abc123', 'dest_addr', short_message='foo')

    @skiptest("FIXME: We don't actually unbind and disconnect yet.")
    @inlineCallbacks
    def test_handle_unbind(self):
        """
        If the SMSC sends an unbind command, we respond and disconnect.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        self.assertEqual(service.is_bound(), True)
        self.fake_smsc.send_pdu(Unbind(7))
        unbind_resp_pdu = yield self.fake_smsc.await_pdu()
        self.assertEqual(command_id(unbind_resp_pdu), 'unbind_resp')
        self.assertEqual(service.is_bound(), False)

    @inlineCallbacks
    def test_csm_split_message(self):
        """
        A multipart message is split into chunks such that the smallest number
        of message parts are required.
        """
        service = yield self.get_service()

        split = lambda msg: service.csm_split_message(msg.encode('utf-8'))

        # these are fine because they're in the 7-bit character set
        self.assertEqual(1, len(split(u'&' * 140)))
        self.assertEqual(1, len(split(u'&' * 160)))
        # ± is not in the 7-bit character set so it should utf-8 encode it
        # which bumps it over the 140 bytes
        self.assertEqual(2, len(split(u'±' + u'1' * 139)))

    @inlineCallbacks
    def test_submit_sm_long(self):
        """
        A long message can be sent in a single PDU using the optional
        `message_payload` PDU field.
        """
        service = yield self.get_service()
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_sm_long(
            'abc123', 'dest_addr', long_message)
        submit_sm = yield self.fake_smsc.await_pdu()
        pdu_opts = unpacked_pdu_opts(submit_sm)

        self.assertEqual('submit_sm', submit_sm['header']['command_id'])
        self.assertEqual(
            None, submit_sm['body']['mandatory_parameters']['short_message'])
        self.assertEqual(''.join('%02x' % ord(c) for c in long_message),
                         pdu_opts['message_payload'])
        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'], stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar(self):
        """
        A long message can be sent in multiple PDUs with SAR fields set to
        instruct the SMSC to build user data headers.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_csm_sar(
            'abc123', 'dest_addr', short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        # seq no 1 == bind_transceiver, 2 == enquire_link, 3 == sar_msg_ref_num
        self.assertEqual([4, 5, 6, 7], seq_nums)
        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            pdu_opts = unpacked_pdu_opts(sm)
            mandatory_parameters = sm['body']['mandatory_parameters']

            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg_parts.append(mandatory_parameters['short_message'])
            self.assertTrue(len(mandatory_parameters['short_message']) <= 130)
            msg_refs.append(pdu_opts['sar_msg_ref_num'])
            self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
            self.assertEqual(4, pdu_opts['sar_total_segments'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual([3, 3, 3, 3], msg_refs)

        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'] * len(seq_nums), stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar_ref_num_limit(self):
        """
        The SAR reference number is set correctly when the generated reference
        number is larger than 0xFFFF.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()
        # forward until we go past 0xFFFF
        yield self.set_sequence_number(service, 0x10000)

        long_message = 'This is a long message.' * 20
        seq_nums = yield service.submit_csm_sar(
            'abc123', 'dest_addr', short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            pdu_opts = unpacked_pdu_opts(sm)
            mandatory_parameters = sm['body']['mandatory_parameters']

            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg_parts.append(mandatory_parameters['short_message'])
            self.assertTrue(len(mandatory_parameters['short_message']) <= 130)
            msg_refs.append(pdu_opts['sar_msg_ref_num'])
            self.assertEqual(i + 1, pdu_opts['sar_segment_seqnum'])
            self.assertEqual(4, pdu_opts['sar_total_segments'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual([2, 2, 2, 2], msg_refs)

        stored_ids = yield self.lookup_message_ids(service, seq_nums)
        self.assertEqual(['abc123'] * len(seq_nums), stored_ids)

    @inlineCallbacks
    def test_submit_csm_sar_single_part(self):
        """
        If the content fits in a single message, all the multipart madness is
        avoided.
        """
        service = yield self.get_service({'send_multipart_sar': True})
        yield self.fake_smsc.bind()

        content = 'a' * 160
        seq_numbers = yield service.submit_csm_sar(
            'abc123', 'dest_addr', short_message=content)
        self.assertEqual(len(seq_numbers), 1)
        submit_sm_pdu = yield self.fake_smsc.await_pdu()

        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(short_message(submit_sm_pdu), content)
        self.assertEqual(unpacked_pdu_opts(submit_sm_pdu), {})

    @inlineCallbacks
    def test_submit_csm_udh(self):
        """
        A long message can be sent in multiple PDUs with carefully handcrafted
        user data headers.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()

        long_message = 'This is a long message.' * 20
        seq_numbers = yield service.submit_csm_udh(
            'abc123', 'dest_addr', short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        self.assertEqual(len(seq_numbers), 4)

        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            mandatory_parameters = sm['body']['mandatory_parameters']
            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg = mandatory_parameters['short_message']

            udh_hlen, udh_tag, udh_len, udh_ref, udh_tot, udh_seq = [
                ord(octet) for octet in msg[:6]]
            self.assertEqual(5, udh_hlen)
            self.assertEqual(0, udh_tag)
            self.assertEqual(3, udh_len)
            msg_refs.append(udh_ref)
            self.assertEqual(4, udh_tot)
            self.assertEqual(i + 1, udh_seq)
            self.assertTrue(len(msg) <= 136)
            msg_parts.append(msg[6:])
            self.assertEqual(0x40, mandatory_parameters['esm_class'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual(1, len(set(msg_refs)))

        stored_ids = yield self.lookup_message_ids(service, seq_numbers)
        self.assertEqual(['abc123'] * len(seq_numbers), stored_ids)

    @inlineCallbacks
    def test_submit_csm_udh_ref_num_limit(self):
        """
        User data headers are crafted correctly when the generated reference
        number is larger than 0xFF.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()
        # forward until we go past 0xFF
        yield self.set_sequence_number(service, 0x100)

        long_message = 'This is a long message.' * 20
        seq_numbers = yield service.submit_csm_udh(
            'abc123', 'dest_addr', short_message=long_message)
        pdus = yield self.fake_smsc.await_pdus(4)
        self.assertEqual(len(seq_numbers), 4)

        msg_parts = []
        msg_refs = []

        for i, sm in enumerate(pdus):
            mandatory_parameters = sm['body']['mandatory_parameters']
            self.assertEqual('submit_sm', sm['header']['command_id'])
            msg = mandatory_parameters['short_message']

            udh_hlen, udh_tag, udh_len, udh_ref, udh_tot, udh_seq = [
                ord(octet) for octet in msg[:6]]
            self.assertEqual(5, udh_hlen)
            self.assertEqual(0, udh_tag)
            self.assertEqual(3, udh_len)
            msg_refs.append(udh_ref)
            self.assertEqual(4, udh_tot)
            self.assertEqual(i + 1, udh_seq)
            self.assertTrue(len(msg) <= 136)
            msg_parts.append(msg[6:])
            self.assertEqual(0x40, mandatory_parameters['esm_class'])

        self.assertEqual(long_message, ''.join(msg_parts))
        self.assertEqual(1, len(set(msg_refs)))

        stored_ids = yield self.lookup_message_ids(service, seq_numbers)
        self.assertEqual(['abc123'] * len(seq_numbers), stored_ids)

    @inlineCallbacks
    def test_submit_csm_udh_single_part(self):
        """
        If the content fits in a single message, all the multipart madness is
        avoided.
        """
        service = yield self.get_service({'send_multipart_udh': True})
        yield self.fake_smsc.bind()

        content = 'a' * 160
        seq_numbers = yield service.submit_csm_udh(
            'abc123', 'dest_addr', short_message=content)
        self.assertEqual(len(seq_numbers), 1)
        submit_sm_pdu = yield self.fake_smsc.await_pdu()

        self.assertEqual(command_id(submit_sm_pdu), 'submit_sm')
        self.assertEqual(short_message(submit_sm_pdu), content)
        self.assertEqual(
            submit_sm_pdu['body']['mandatory_parameters']['esm_class'], 0)

    @inlineCallbacks
    def test_pdu_cache_persistence(self):
        """
        A cached PDU has an appropriate TTL and can be deleted.
        """
        service = yield self.get_service()

        message_stash = service.message_stash
        config = service.get_config()

        pdu = SubmitSM(1337, short_message="foo")
        yield message_stash.cache_pdu("vumi0", pdu)

        ttl = yield message_stash.redis.ttl(pdu_key(1337))
        self.assertTrue(0 < ttl <= config.submit_sm_expiry)

        pdu_data = yield message_stash.get_cached_pdu(1337)
        self.assertEqual(pdu_data.vumi_message_id, "vumi0")
        self.assertEqual(pdu_data.pdu.get_hex(), pdu.get_hex())

        yield message_stash.delete_cached_pdu(1337)
        deleted_pdu_data = yield message_stash.get_cached_pdu(1337)
        self.assertEqual(deleted_pdu_data, None)