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, } }
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])
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])
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)
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)
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 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())])
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)
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())
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()) ])
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, } }
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")
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)
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)
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)
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)
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)
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, [])
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())
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)
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()))
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()))
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])
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())])
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)
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()) ])
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)
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())
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)
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)
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)
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)
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, }, }
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)
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)
def setUp(self): self.fake_smsc = FakeSMSC() self.tx_helper = self.add_helper( TransportHelper(SmppTransceiverTransport)) self.clock = Clock()
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])
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)
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])
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)
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)