smpp dependency cleanup and test case prep

pull/1354/head
Chris Caron 2025-06-29 15:26:13 -04:00
parent 875c8349fe
commit 52a53d6f33
3 changed files with 63 additions and 60 deletions

View File

@ -14,3 +14,6 @@ paho-mqtt != 2.0.*
# Pretty Good Privacy (PGP) Provides mailto:// and deltachat:// support
PGPy
# Provides smpp:// support
python-smpp

View File

@ -28,9 +28,18 @@
from itertools import chain
import smpplib
import smpplib.consts
import smpplib.gsm
try:
import smpplib
import smpplib.consts
import smpplib.gsm
# We're good to go!
NOTIFY_SMPP_ENABLED = True
except ImportError:
# cryptography is required in order for this package to work
NOTIFY_SMPP_ENABLED = False
from .base import NotifyBase
from ..common import NotifyType
@ -38,18 +47,29 @@ from ..locale import gettext_lazy as _
from ..utils.parse import is_phone_no, parse_phone_no
class NotifySmpp(NotifyBase):
class NotifySMPP(NotifyBase):
"""
A wrapper for SMPP Notifications
"""
# Set our global enabled flag
enabled = NOTIFY_SMPP_ENABLED
requirements = {
# Define our required packaging in order to work
'packages_required': 'python-snpp'
}
# The default descriptive name associated with the Notification
service_name = _('SMPP')
# The services URL
service_url = 'https://smpp.org/'
# The default protocol
protocol = 'smpp'
# The default secure protocol
secure_protocol = 'smpps'
# A URL that takes you to the setup/help of the specific protocol
@ -175,7 +195,7 @@ class NotifySmpp(NotifyBase):
port=self.port,
source=self.source,
targets='/'.join(
[NotifySmpp.quote(t, safe='')
[NotifySMPP.quote(t, safe='')
for t in chain(self.targets, self._invalid_targets)]),
params=self.urlencode(params),
)
@ -249,27 +269,27 @@ class NotifySmpp(NotifyBase):
# We're done early as we couldn't load the results
return results
results['targets'] = NotifySmpp.split_path(results['fullpath'])
results['targets'] = NotifySMPP.split_path(results['fullpath'])
# Support the 'to' variable so that we can support targets this way too
# 'to' makes it easier to use yaml configuration
if 'to' in results['qsd'] and len(results['qsd']['to']):
results['targets'] += \
NotifySmpp.parse_phone_no(results['qsd']['to'])
NotifySMPP.parse_phone_no(results['qsd']['to'])
# store any additional payload extras defined
results['payload'] = {NotifySmpp.unquote(x): NotifySmpp.unquote(y)
results['payload'] = {NotifySMPP.unquote(x): NotifySMPP.unquote(y)
for x, y in results['qsd:'].items()}
# Add our GET parameters in the event the user wants to pass them
results['params'] = {NotifySmpp.unquote(x): NotifySmpp.unquote(y)
results['params'] = {NotifySMPP.unquote(x): NotifySMPP.unquote(y)
for x, y in results['qsd-'].items()}
# Support the 'from' and 'source' variable so that we can support
# targets this way too.
# 'from' makes it easier to use yaml configuration
if 'from' in results['qsd'] and len(results['qsd']['from']):
results['source'] = NotifySmpp.unquote(results['qsd']['from'])
results['source'] = NotifySMPP.unquote(results['qsd']['from'])
elif results['targets']:
# from phone number is the first entry in the list otherwise
results['source'] = results['targets'].pop(0)

View File

@ -27,13 +27,11 @@
# POSSIBILITY OF SUCH DAMAGE.
import logging
from json import dumps
from unittest import mock
import sys
import pytest
import requests
from apprise.plugins.smpp import NotifySmpp
from apprise import Apprise
from apprise.plugins.smpp import NotifySMPP
from helpers import AppriseURLTester
logging.disable(logging.CRITICAL)
@ -66,32 +64,32 @@ apprise_url_tests = (
}),
('smpp://user:pass@host:port/{}/{}'.format('1' * 10, 'a' * 32), {
# valid everything but target numbers
'instance': NotifySmpp,
'instance': NotifySMPP,
# We have no one to notify
'notify_response': False,
}),
('smpp://user:pass@host:port/{}'.format('1' * 10), {
# everything valid
'instance': NotifySmpp,
'instance': NotifySMPP,
# We have no one to notify
'notify_response': False,
}),
('smpp://user:pass@host:port/{}/{}'.format('1' * 10, '1' * 10), {
'instance': NotifySmpp,
'instance': NotifySMPP,
}),
('smpp://_?&from={}&to={},{}'.format(
'1' * 10, '1' * 10, '1' * 10), {
# use get args to accomplish the same thing
'instance': NotifySmpp,
}),
# use get args to accomplish the same thing
'instance': NotifySMPP,
}),
('smpp://user:pass@host:port/{}/{}'.format('1' * 10, '1' * 10), {
'instance': NotifySmpp,
'instance': NotifySMPP,
# throw a bizarre code forcing us to fail to look it up
'response': False,
'requests_response_code': 999,
}),
('smpp://user:pass@host:port/{}/{}'.format('1' * 10, '1' * 10), {
'instance': NotifySmpp,
'instance': NotifySMPP,
# Throws a series of connection and transfer exceptions when this flag
# is set and tests that we gracefully handle them
'test_requests_exceptions': True,
@ -99,46 +97,28 @@ apprise_url_tests = (
)
@pytest.mark.skipif(
'python-smpp' in sys.modules,
reason="Requires that python-smpp NOT be installed")
def test_plugin_fcm_cryptography_import_error():
"""
NotifySimplePush() python-smpp loading failure
"""
# Attempt to instantiate our object
obj = Apprise.instantiate(
'smpp://user:pass@host:port/{}/{}'.format('1' * 10, '1' * 10))
# It's not possible because our cryptography depedancy is missing
assert obj is None
@pytest.mark.skipif(
'python-smpp' not in sys.modules, reason="Requires python-smpp")
def test_plugin_smpp_urls():
"""
NotifySmpp() Apprise URLs
NotifySMPP() Apprise URLs
"""
# Run our general tests
AppriseURLTester(tests=apprise_url_tests).run_all()
@mock.patch('requests.post')
def test_plugin_smpp_edge_cases(mock_post):
"""
NotifySmpp() Edge Cases
"""
# Prepare our response
response = requests.Request()
response.status_code = requests.codes.ok
# Prepare Mock
mock_post.return_value = response
# Initialize some generic (but valid) apikeys
apikey = 'b' * 32
source = '+1 (555) 123-3456'
# No apikey specified
with pytest.raises(TypeError):
NotifySmpp(source=source)
# a error response
response.status_code = 400
response.content = dumps({
'code': 21211,
'message': "The 'To' number +1234567 is not a valid phone number.",
})
mock_post.return_value = response
# Initialize our object
obj = NotifySmpp(source=source)
# We will fail with the above error code
assert obj.notify('title', 'body', 'info') is False