def test_carbon_success_value_metric_from_topic_with_leading_slash( srv, caplog): item = Item( target="test", addrs=["localhost", 2003], message="42.42", data={"topic": "/foo/bar"}, ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/carbon.py") socket_mock = mock.MagicMock() module.socket.socket = socket_mock outcome = module.plugin(srv, item) assert socket_mock.mock_calls == [ call(), call().connect(("localhost", 2003)), call().sendall(mock.ANY), call().close(), ] assert outcome is True assert "Sending to carbon: foo.bar 42.42" in caplog.text
def test_carbon_success_metric_value_timestamp(srv, caplog): item = Item( target="test", addrs=["localhost", 2003], message="foo 42.42 1623887596", data={}, ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/carbon.py") socket_mock = mock.MagicMock() module.socket.socket = socket_mock outcome = module.plugin(srv, item) assert socket_mock.mock_calls == [ call(), call().connect(("localhost", 2003)), call().sendall("foo 42.42 1623887596\n"), call().close(), ] assert outcome is True assert "Sending to carbon: foo 42.42 1623887596" in caplog.text
def test_pushover_success_with_imagebase64(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") image = open("./assets/pushover.png", "rb").read() item = Item( config={}, target="test", addrs=["userkey2", "appkey2"], message="⚽ Notification message ⚽", data={"imagebase64": base64.encodebytes(image)}, ) with caplog.at_level(logging.DEBUG): add_successful_mock_response() outcome = module.plugin(srv, item) # Check response status. assert responses.calls[0].response.status_code == 200 assert responses.calls[0].response.text == '{"status": 1}' # Decode multipart request. request = responses.calls[0].request decoder = MultipartDecoder(request.body, request.headers["Content-Type"]) content_disposition_headers = [] contents = {} for part in decoder.parts: content_disposition_headers.append(part.headers[b"Content-Disposition"]) key = part.headers[b"Content-Disposition"] contents[key] = part.content # Proof request has all body parts. assert content_disposition_headers == [ b'form-data; name="user"', b'form-data; name="token"', b'form-data; name="retry"', b'form-data; name="expire"', b'form-data; name="message"', b'form-data; name="attachment"; filename="image.jpg"', ] # Proof parameter body parts, modulo image content, have correct values. assert list(contents.values())[:-1] == [ b"userkey2", b"appkey2", b"60", b"3600", b"\xe2\x9a\xbd Notification message \xe2\x9a\xbd", ] # Proof image has content. assert len(decoder.parts[-1].content) == 45628 assert outcome is True assert "Sending pushover notification to test" in caplog.text assert "Successfully sent pushover notification" in caplog.text
def test_carbon_failure_invalid_configuration(srv, caplog): item = Item(target="test", addrs=["172.16.153.110", "foobar"]) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/carbon.py") outcome = module.plugin(srv, item) assert outcome is False assert "Configuration for target `carbon' is incorrect" in caplog.text
def test_alexa_notify_me_real_auth_failure(srv, caplog): module = load_module_from_file("mqttwarn/services/alexa-notify-me.py") accessCode = "myToken" item = Item(addrs=[accessCode], message="⚽ Notification message ⚽") with caplog.at_level(logging.DEBUG): outcome = module.plugin(srv, item) assert outcome is False assert "Sending to NotifyMe service" in caplog.text assert "Failed to send message to NotifyMe service" in caplog.text
def test_carbon_failure_empty_message(srv, caplog): item = Item(target="test", addrs=["172.16.153.110", 2003]) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/carbon.py") outcome = module.plugin(srv, item) assert outcome is False assert "target `carbon': cannot split string" in caplog.text
def test_pushover_failure_invalid_configuration(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") item = Item( config={}, target="test", addrs=[None], ) with caplog.at_level(logging.DEBUG): outcome = module.plugin(srv, item) assert outcome is False assert "Invalid address configuration for target `test'" in caplog.text
def test_apprise_error(srv, caplog): with caplog.at_level(logging.DEBUG): mock_connection = mock.MagicMock() # Make the call to `notify` raise an exception. def error(*args, **kwargs): raise Exception("something failed") mock_connection.notify = error with mock.patch("apprise.Apprise", side_effect=[mock_connection], create=True) as mock_client: with mock.patch("apprise.AppriseAsset", create=True) as mock_asset: module = load_module_from_file( "mqttwarn/services/apprise_single.py") item = Item( config={ "baseuri": "mailtos://*****:*****@mail.example.org" }, target="test", addrs=["*****@*****.**", "*****@*****.**"], title="⚽ Message title ⚽", message="⚽ Notification message ⚽", ) outcome = module.plugin(srv, item) assert mock_client.mock_calls == [ mock.call(asset=mock.ANY), ] assert mock_connection.mock_calls == [ call.add( "mailtos://*****:*****@mail.example.org?to=foo%40example.org%2Cbar%40example.org" ), ] assert outcome is False assert ( "Sending notification to Apprise. target=test, addresses=['*****@*****.**', '*****@*****.**']" in caplog.messages) assert ( "Sending message using Apprise failed. target=test, error=something failed" in caplog.messages)
def test_pushover_failure_missing_credentials(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") item = Item( config={}, target="test", addrs=[None, None], data={}, ) with caplog.at_level(logging.DEBUG): outcome = module.plugin(srv, item) assert outcome is False assert "No pushover credentials configured for target `test'" in caplog.text
def test_apns_failure_invalid_config(mock_apns_payload, mock_apns, srv, caplog): with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/apns.py") item = Item( target="test", addrs=[None], message="⚽ Notification message ⚽", data={"apns_token": "foobar", "payload": "{}"}, ) outcome = module.plugin(srv, item) assert outcome is False assert "Incorrect service configuration" in caplog.text
def test_azure_iot_failure_wrong_qos(srv, caplog): item = Item( config={"iothubname": "acmehub", "qos": 999}, target="test", addrs=["device-id", "SharedAccessSignature sr=..."], message="⚽ Notification message ⚽", ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/azure_iot.py") outcome = module.plugin(srv, item) assert outcome is False assert "Only QoS 0 or 1 allowed for Azure IoT Hub, not '999'" in caplog.text
def test_pushover_success_with_imageurl_and_digest_authentication(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") item = Item( config={}, target="test", addrs=["userkey2", "appkey2"], message="⚽ Notification message ⚽", data={ "imageurl": "https://example.org/image", "auth": "digest", "user": "******", "password": "******", }, ) with caplog.at_level(logging.DEBUG): add_successful_mock_response() image = open("./assets/pushover.png", "rb").read() responses.add( responses.GET, "https://example.org/image", body=image, stream=True, status=200, ) outcome = module.plugin(srv, item) # Proof authentication on image request. # FIXME: Currently not possible because Digest auth will only work if # the server answers with 4xx. # assert ( # responses.calls[0].request.headers["Authorization"] == "Digest something" # ) # Check response status. assert responses.calls[1].response.status_code == 200 assert responses.calls[1].response.text == '{"status": 1}' assert outcome is True assert "Sending pushover notification to test" in caplog.text assert "Successfully sent pushover notification" in caplog.text
def test_asterisk_success_with_broken_close(asterisk_mock, srv, caplog): with caplog.at_level(logging.DEBUG): attrs = { "login.return_value": 42, "originate.return_value": 42, "close.side_effect": ManagerSocketException("something failed"), } asterisk_mock.return_value = mock.MagicMock(**attrs) module = load_module_from_file("mqttwarn/services/asterisk.py") item = Item( config={ "host": "asterisk.example.org", "port": 5038, "username": "******", "password": "******", "extension": 2222, "context": "default", }, target="test", addrs=["SIP/avaya/", "0123456789"], message="⚽ Notification message ⚽", ) outcome = module.plugin(srv, item) assert asterisk_mock.mock_calls == [ call(), call().connect("asterisk.example.org", 5038), call().login("foobar", "bazqux"), call().originate( "SIP/avaya/0123456789", 2222, context="default", priority="1", caller_id=2222, variables={"text": "⚽ Notification message ⚽"}, ), call().logoff(), call().close(), ] assert outcome is True
def test_carbon_failure_invalid_message_format(srv, caplog): item = Item( target="test", addrs=["172.16.153.110", 2003], message="foo bar baz qux", data={}, ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/carbon.py") outcome = module.plugin(srv, item) assert outcome is False assert "target `carbon': error decoding message" in caplog.text
def test_azure_iot_success_bytes(srv, caplog): item = Item( config={"iothubname": "acmehub"}, target="test", addrs=["device-id", "SharedAccessSignature sr=..."], message=b"### Notification message ###", ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/azure_iot.py") mqtt_publish_mock = mock.MagicMock() module.mqtt = mqtt_publish_mock outcome = module.plugin(srv, item) mqtt_publish_mock.single.assert_called_once_with( "devices/device-id/messages/events/", bytearray(b"### Notification message ###"), auth={ "username": "******", "password": "******", }, tls={ "ca_certs": None, "certfile": None, "keyfile": None, "tls_version": mock.ANY, "ciphers": None, "cert_reqs": mock.ANY, }, hostname="acmehub.azure-devices.net", port=8883, protocol=4, qos=0, retain=False, client_id="device-id", ) assert outcome is True assert ( "Publishing to Azure IoT Hub for target=test (device-id): devices/device-id/messages/events/ 'b'### Notification message ###''" in caplog.text )
def test_apns_failure_apns_token_missing(mock_apns_payload, mock_apns, srv, caplog): with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/apns.py") cert_file, key_file = ["cert_file", "key_file"] item = Item( target="test", addrs=[cert_file, key_file], message="⚽ Notification message ⚽", data={}, ) outcome = module.plugin(srv, item) assert outcome is False assert "Cannot notify via APNS: apns_token is missing" in caplog.text
def test_alexa_notify_me_success(srv, caplog): module = load_module_from_file("mqttwarn/services/alexa-notify-me.py") accessCode = "myToken" item = Item(addrs=[accessCode], message="⚽ Notification message ⚽") with caplog.at_level(logging.DEBUG): with mock.patch("requests.post") as requests_mock: outcome = module.plugin(srv, item) requests_mock.assert_called_once_with( url="https://api.notifymyecho.com/v1/NotifyMe", data= '{"notification": "\\u26bd Notification message \\u26bd", "accessCode": "myToken"}', ) assert outcome is True assert "Sending to NotifyMe service" in caplog.text assert "Successfully sent to NotifyMe service" in caplog.text
def test_azure_iot_failure_invalid_message(srv, caplog): item = Item( config={"iothubname": "acmehub"}, target="test", addrs=["device-id", "SharedAccessSignature sr=..."], ) with mock.patch.object(Item, "message", new_callable=PropertyMock) as msg_mock: msg_mock.side_effect = Exception("something failed") with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/azure_iot.py") outcome = module.plugin(srv, item) assert outcome is False assert "Unable to prepare message for target=test: something failed" in caplog.text
def test_apprise_multi_error(srv, caplog): with caplog.at_level(logging.DEBUG): mock_connection = mock.MagicMock() # Make the call to `notify` raise an exception. def error(*args, **kwargs): raise Exception("something failed") mock_connection.notify = error with mock.patch("apprise.Apprise", side_effect=[mock_connection], create=True) as mock_client: with mock.patch("apprise.AppriseAsset", create=True) as mock_asset: module = load_module_by_name("mqttwarn.services.apprise_multi") item = Item( addrs=[{ "baseuri": "json://localhost:1234/mqtthook" }], title="⚽ Message title ⚽", message="⚽ Notification message ⚽", ) outcome = module.plugin(srv, item) assert mock_client.mock_calls == [ mock.call(asset=mock.ANY), ] assert mock_connection.mock_calls == [ call.add("json://localhost:1234/mqtthook"), ] assert outcome is False assert ( "Sending notification to Apprise. target=None, addresses=[{'baseuri': 'json://localhost:1234/mqtthook'}]" in caplog.messages) assert ( "Sending message using Apprise failed. target=None, error=something failed" in caplog.messages)
def test_amqp_failure(srv, caplog): module = load_module_by_name("mqttwarn.services.amqp") exchange, routing_key = ["name_of_exchange", "my_routing_key"] item = Item( config={"uri": "amqp://*****:*****@localhost:5672/"}, target="test", addrs=[exchange, routing_key], message="⚽ Notification message ⚽", ) with caplog.at_level(logging.DEBUG): mock_connection = mock.MagicMock() # Make the call to `basic_publish` raise an exception. def error(*args, **kwargs): raise Exception("something failed") mock_connection.basic_publish = error with mock.patch("puka.Client", side_effect=[mock_connection], create=True) as mock_client: outcome = module.plugin(srv, item) assert mock_client.mock_calls == [ mock.call("amqp://*****:*****@localhost:5672/"), ] assert mock_connection.mock_calls == [ call.connect(), call.wait(mock.ANY), ] assert outcome is False assert ("AMQP publish to test [name_of_exchange/my_routing_key]" in caplog.text) assert ( "Error on AMQP publish to test [name_of_exchange/my_routing_key]: something failed" in caplog.text)
def test_irccat_config_invalid(srv, caplog): item = Item( target="test", addrs=["localhost", 12345], message="⚽ Notification message ⚽", data={}, ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/irccat.py") socket_mock = mock.MagicMock() module.socket.socket = socket_mock outcome = module.plugin(srv, item) assert socket_mock.mock_calls == [] assert outcome is False assert "Incorrect target configuration" in caplog.messages
def test_file_failure(fake_filesystem, srv, caplog): """ When `io.open` fails, prove that the corresponding error code path is invoked. """ item = Item( target="test", addrs=["/tmp/testdrive.log"], message="⚽ Notification message ⚽", data={}, ) with caplog.at_level(logging.DEBUG): module = mqttwarn.services.file outcome = module.plugin(srv, item) assert not os.path.exists("/tmp/testdrive.log") assert outcome is False assert "Cannot write to file `/tmp/testdrive.log': something failed" in caplog.messages
def test_pushover_failure_request_error(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") item = Item( config={}, target="test", addrs=["userkey2", "appkey2"], message="⚽ Notification message ⚽", data={}, ) with caplog.at_level(logging.DEBUG): # Make remote call bail out. module.pushover = mock.MagicMock(side_effect=Exception("something failed")) outcome = module.plugin(srv, item) assert outcome is False assert "Sending pushover notification to test" in caplog.text assert "Error sending pushover notification: something failed" in caplog.text
def test_azure_iot_failure_mqtt_publish(srv, caplog): item = Item( config={"iothubname": "acmehub"}, target="test", addrs=["device-id", "SharedAccessSignature sr=..."], message="⚽ Notification message ⚽", ) with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/azure_iot.py") mqtt_publish_mock = mock.MagicMock(side_effect=Exception("something failed")) module.mqtt.single = mqtt_publish_mock outcome = module.plugin(srv, item) assert outcome is False assert ( "Unable to publish to Azure IoT Hub for target=test (device-id): something failed" in caplog.text )
def test_apns_success_custom_payload(mock_apns_payload, mock_apns, srv, caplog): with caplog.at_level(logging.DEBUG): module = load_module_from_file("mqttwarn/services/apns.py") cert_file, key_file = ["cert_file", "key_file"] item = Item( target="test", addrs=[cert_file, key_file], message="⚽ Notification message ⚽", data={ "apns_token": "foobar", "payload": '{"custom": {"baz": "qux"}}', }, ) outcome = module.plugin(srv, item) assert mock_apns_payload.mock_calls == [ call( alert="⚽ Notification message ⚽", custom={"baz": "qux"}, sound="default", badge=1, ), ] assert mock_apns.mock_calls == [ mock.call(use_sandbox=False, cert_file="cert_file", key_file="key_file"), call().gateway_server.send_notification("foobar", mock.ANY), ] assert outcome is True assert "Successfully published APNS notification to foobar" in caplog.text
def test_pushover_failure_response_error(srv, caplog): module = load_module_from_file("mqttwarn/services/pushover.py") item = Item( config={}, target="test", addrs=["userkey2", "appkey2"], message="⚽ Notification message ⚽", data={}, ) with caplog.at_level(logging.DEBUG): add_failed_mock_response() outcome = module.plugin(srv, item) assert len(responses.calls) == 1 assert ( responses.calls[0].request.url == "https://api.pushover.net/1/messages.json" ) assert ( responses.calls[0].request.body == "user=userkey2&token=appkey2&retry=60&expire=3600&message=%E2%9A%BD+Notification+message+%E2%9A%BD" ) assert responses.calls[0].request.headers["User-Agent"] == "mqttwarn" assert responses.calls[0].response.status_code == 400 assert responses.calls[0].response.text == '{"status": 999}' assert outcome is False assert "Sending pushover notification to test" in caplog.text assert ( "Error sending pushover notification: b'{\"status\": 999}'" in caplog.text )
def test_apprise_multi_mailto_success(apprise_asset, apprise_mock, srv, caplog): with caplog.at_level(logging.DEBUG): module = load_module_by_name("mqttwarn.services.apprise_multi") item = Item( addrs=[{ "baseuri": "mailtos://*****:*****@mail.example.org", "recipients": ["*****@*****.**", "*****@*****.**"], "sender": "*****@*****.**", "sender_name": "Example Monitoring", }], title="⚽ Message title ⚽", message="⚽ Notification message ⚽", ) outcome = module.plugin(srv, item) assert apprise_mock.mock_calls == [ call(asset=mock.ANY), call().add( "mailtos://*****:*****@mail.example.org?to=foo%40example.org%2Cbar%40example.org&from=monitoring%40example.org&name=Example+Monitoring" ), call().notify(body="⚽ Notification message ⚽", title="⚽ Message title ⚽"), call().notify().__bool__(), ] assert outcome is True assert ( "Sending notification to Apprise. target=None, addresses=[{'baseuri': 'mailtos://*****:*****@mail.example.org', 'recipients': ['*****@*****.**', '*****@*****.**'], 'sender': '*****@*****.**', 'sender_name': 'Example Monitoring'}]" in caplog.messages) assert "Successfully sent message using Apprise" in caplog.messages
def test_amqp_success(mock_puka_client, srv, caplog): module = load_module_by_name("mqttwarn.services.amqp") exchange, routing_key = ["name_of_exchange", "my_routing_key"] item = Item( config={"uri": "amqp://*****:*****@localhost:5672/"}, target="test", addrs=[exchange, routing_key], message="⚽ Notification message ⚽", ) with caplog.at_level(logging.DEBUG): outcome = module.plugin(srv, item) assert mock_puka_client.mock_calls == [ mock.call("amqp://*****:*****@localhost:5672/"), call().connect(), call().wait(mock.ANY), call().basic_publish( exchange="name_of_exchange", routing_key="my_routing_key", headers={ "content_type": "text/plain", "x-agent": "mqttwarn", "delivery_mode": 1, }, body="⚽ Notification message ⚽", ), call().wait(mock.ANY), call().close(), ] assert outcome is True assert "AMQP publish to test [name_of_exchange/my_routing_key]" in caplog.text assert "Successfully published AMQP notification" in caplog.text
# (c) 2021 The mqttwarn developers import logging import os from unittest import mock import mqttwarn.services.file import pytest as pytest from mqttwarn.model import ProcessorItem as Item @pytest.mark.parametrize( "item", [ Item( target="test", addrs=["/tmp/testdrive.log"], message="⚽ Notification message ⚽", data={}, ), Item( target="test", addrs={"path": "/tmp/testdrive.log"}, message="⚽ Notification message ⚽", data={}, ), ], ids=["basic", "advanced"], ) def test_file_success(fake_filesystem, srv, caplog, item): """ Dispatch a single message and prove it is stored in the designated file. """