Files
ad_trident/tests/test_notificationsmanager_logging.py
T
2026-11-13 13:10:37 +01:00

79 lines
3.9 KiB
Python

"""Notification logging must avoid stack inspection without changing delivery."""
import logging
from pathlib import Path
import sys
from types import SimpleNamespace
import unittest
from unittest.mock import Mock, call, patch
sys.path[:0] = [str(Path(__file__).resolve().parents[1] / 'apps'),
str(Path(__file__).resolve().parents[1] / 'apps/ad_toolbox')]
from appdaemon.adapi import ADAPI
from notificationsmanager import NotificationsManager, Notification, SmartCondition
class NotificationLoggingTests(unittest.TestCase):
def setUp(self):
self.api = ADAPI.__new__(ADAPI)
self.api.AD = SimpleNamespace(config=SimpleNamespace(ascii_encode=False))
self.api.logger = Mock()
self.sinks = {'main_log': Mock(), 'error_log': Mock()}
self.api.get_user_log = Mock(side_effect=self.sinks.__getitem__)
self.app = NotificationsManager.__new__(NotificationsManager)
self.app.get_ad_api = Mock(return_value=self.api)
self.app.args = {}
self.app.call_service = Mock()
self.app.run_in = Mock()
self.app.stop_app = Mock()
self.app._config_model = SimpleNamespace(name='notifications_manager')
self.app.initialize()
self.config = {
'message': {'title': 'Alarme', 'content': 'Activée', 'timeout': 10,
'data': {'tag': 'alarm'}},
'recipients': {'phones': {'services': ['notify/one', 'notify/two']}}
}
self.notification = Notification(self.app, {'message': {}, 'recipients': {}}, 'alarm')
self.notification.config = self.config
def test_send_and_clear_keep_delivery_without_inspecting_stack(self):
with patch('appdaemon.adapi.inspect.stack', side_effect=AssertionError('stack inspection')):
self.notification.send_notification('phones')
self.notification.clear_notification('phones', 'alarm')
self.app.call_service.assert_has_calls([
call('notify/one', title='Alarme', message='Activée', data={'tag': 'alarm'}),
call('notify/two', title='Alarme', message='Activée', data={'tag': 'alarm'}),
call('notify/one', message='clear_notification', data={'tag': 'alarm'}),
call('notify/two', message='clear_notification', data={'tag': 'alarm'}),
])
self.app.run_in.assert_called_once_with(self.notification.on_timeout_expired, 10,
tag='alarm', recipient='phones')
self.assertEqual(self.api.logger.log.call_count, 4)
self.api.get_user_log.assert_not_called()
def test_errors_use_app_logger_and_error_log_and_stop(self):
with patch('appdaemon.adapi.inspect.stack', side_effect=AssertionError('stack inspection')):
self.app.log_error('Échec', stop_app=True)
for sink in (self.api.logger, self.sinks['error_log']):
self.assertEqual(sink.log.call_args.args, (logging.ERROR, 'Échec'))
self.sinks['main_log'].log.assert_not_called()
self.app.stop_app.assert_called_once_with('notifications_manager')
def test_smartcondition_fallback_uses_initialized_interface(self):
self.config['recipients']['phones']['conditions'] = True
self.app.args = {'notifications': {'alarm': self.config}}
real_class = SmartCondition.Evaluator
def factory(api, *args, **kwargs):
condition = real_class.__new__(real_class)
condition._Evaluator__logger_interface = None
condition._Evaluator__appdaemon_api = api
condition._Evaluator__log_info('Condition évaluée')
return condition
with patch('appdaemon.adapi.inspect.stack', side_effect=AssertionError('stack inspection')):
with patch.object(SmartCondition, 'Evaluator', side_effect=factory):
self.app.initialize()
self.assertEqual(self.api.logger.log.call_args.args, (logging.INFO, 'Condition évaluée'))
if __name__ == '__main__':
unittest.main()