add more tests

This commit is contained in:
HgO 2024-04-17 14:59:37 +02:00
parent 27828ec3c7
commit 6ae3355f3c
9 changed files with 925 additions and 77 deletions

View file

@ -5,7 +5,6 @@ import logging
from diskcache import Cache from diskcache import Cache
from nio.client import AsyncClient from nio.client import AsyncClient
from nio.events import ( from nio.events import (
Event,
InviteMemberEvent, InviteMemberEvent,
KeyVerificationCancel, KeyVerificationCancel,
KeyVerificationKey, KeyVerificationKey,
@ -470,10 +469,3 @@ class Callbacks:
raise SendRetryError( raise SendRetryError(
f"{response_event.status_code} - {response_event.message}" f"{response_event.status_code} - {response_event.message}"
) )
async def debug(self, room: MatrixRoom, event: Event) -> None:
logger.debug(
f"Bot {self.matrix_client.user_id} | Room ID {room.room_id} | "
f"Event ID {event.event_id} | Sender {event.sender} | "
f"Received some event: {event.source}"
)

View file

@ -1,2 +1,3 @@
[pytest] [pytest]
asyncio_mode=strict asyncio_mode=strict
addopts=--cov=matrix_alertbot --cov-report=lcov:lcov.info --cov-report=term

View file

@ -50,7 +50,8 @@ test =
flake8-comprehensions>=3.10.0 flake8-comprehensions>=3.10.0
isort>=5.10.1 isort>=5.10.1
mypy>=0.961 mypy>=0.961
pytest>=7.1.2 pytest>=7.4.0
pytest-cov>=4.1.0
pytest-asyncio>=0.18.3 pytest-asyncio>=0.18.3
freezegun>=1.2.1 freezegun>=1.2.1
types-PyYAML>=6.0.9 types-PyYAML>=6.0.9

View file

@ -24,12 +24,6 @@ from matrix_alertbot.errors import (
) )
async def update_silence_raise_silence_not_found(
fingerprint: str, user: str, duration_seconds: int, *, force: bool = False
) -> str:
raise SilenceNotFoundError
class FakeCache: class FakeCache:
def __init__(self, cache_dict: Optional[Dict] = None) -> None: def __init__(self, cache_dict: Optional[Dict] = None) -> None:
if cache_dict is None: if cache_dict is None:
@ -533,14 +527,20 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
) )
self.assertEqual({"fingerprint1": ("silence2", 864000)}, fake_cache.cache) self.assertEqual({"fingerprint1": ("silence2", 864000)}, fake_cache.cache)
@patch.object(matrix_alertbot.alertmanager.AlertmanagerClient, "update_silence") @patch.object(
@patch.object(matrix_alertbot.alertmanager.AlertmanagerClient, "create_silence") matrix_alertbot.alertmanager.AlertmanagerClient,
"update_silence",
side_effect=SilenceNotFoundError,
)
@patch.object(
matrix_alertbot.alertmanager.AlertmanagerClient,
"create_silence",
return_value="silence1",
)
async def test_create_or_update_silence_with_duration_and_silence_not_found( async def test_create_or_update_silence_with_duration_and_silence_not_found(
self, fake_create_silence: Mock, fake_update_silence: Mock self, fake_create_silence: Mock, fake_update_silence: Mock
) -> None: ) -> None:
fake_cache = Mock(spec=Cache) fake_cache = Mock(spec=Cache)
fake_update_silence.side_effect = update_silence_raise_silence_not_found
fake_create_silence.return_value = "silence1"
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache) alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
async with aiotools.closing_async(alertmanager_client): async with aiotools.closing_async(alertmanager_client):
@ -651,14 +651,20 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
) )
self.assertEqual({"fingerprint1": ("silence2", None)}, fake_cache.cache) self.assertEqual({"fingerprint1": ("silence2", None)}, fake_cache.cache)
@patch.object(matrix_alertbot.alertmanager.AlertmanagerClient, "update_silence") @patch.object(
@patch.object(matrix_alertbot.alertmanager.AlertmanagerClient, "create_silence") matrix_alertbot.alertmanager.AlertmanagerClient,
"update_silence",
side_effect=SilenceNotFoundError,
)
@patch.object(
matrix_alertbot.alertmanager.AlertmanagerClient,
"create_silence",
return_value="silence1",
)
async def test_create_or_update_silence_without_duration_and_silence_not_found( async def test_create_or_update_silence_without_duration_and_silence_not_found(
self, fake_create_silence: Mock, fake_update_silence: Mock self, fake_create_silence: Mock, fake_update_silence: Mock
) -> None: ) -> None:
fake_cache = Mock(spec=Cache) fake_cache = Mock(spec=Cache)
fake_update_silence.side_effect = update_silence_raise_silence_not_found
fake_create_silence.return_value = "silence1"
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache) alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
async with aiotools.closing_async(alertmanager_client): async with aiotools.closing_async(alertmanager_client):

View file

@ -2,7 +2,7 @@ from __future__ import annotations
import unittest import unittest
from typing import Dict from typing import Dict
from unittest.mock import MagicMock, Mock, patch from unittest.mock import MagicMock, Mock, call, patch
import nio import nio
import nio.crypto import nio.crypto
@ -14,10 +14,6 @@ import matrix_alertbot.command
import matrix_alertbot.matrix import matrix_alertbot.matrix
def key_verification_get_mac_raise_protocol_error():
raise nio.LocalProtocolError
class CallbacksTestCase(unittest.IsolatedAsyncioTestCase): class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None: def setUp(self) -> None:
# Create a Callbacks object and give it some Mock'd objects to use # Create a Callbacks object and give it some Mock'd objects to use
@ -67,6 +63,42 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
# Check that we attempted to join the room # Check that we attempted to join the room
self.fake_matrix_client.join.assert_called_once_with(self.fake_room.room_id) self.fake_matrix_client.join.assert_called_once_with(self.fake_room.room_id)
async def test_invite_in_unauthorized_room(self) -> None:
"""Tests the callback for InviteMemberEvents"""
# Tests that the bot attempts to join a room after being invited to it
fake_invite_event = Mock(spec=nio.InviteMemberEvent)
fake_invite_event.sender = "@some_other_fake_user:example.com"
self.fake_room.room_id = "!unauthorizedroom@example.com"
# Pretend that we received an invite event
await self.callbacks.invite(self.fake_room, fake_invite_event)
# Check that we attempted to join the room
self.fake_matrix_client.join.assert_not_called()
async def test_invite_raise_join_error(self) -> None:
"""Tests the callback for InviteMemberEvents"""
# Tests that the bot attempts to join a room after being invited to it
fake_invite_event = Mock(spec=nio.InviteMemberEvent)
fake_invite_event.sender = "@some_other_fake_user:example.com"
fake_join_error = Mock(spec=nio.JoinError)
fake_join_error.message = "error message"
self.fake_matrix_client.join.return_value = fake_join_error
# Pretend that we received an invite event
await self.callbacks.invite(self.fake_room, fake_invite_event)
# Check that we attempted to join the room
self.fake_matrix_client.join.assert_has_calls(
[
call("!abcdefg:example.com"),
call("!abcdefg:example.com"),
call("!abcdefg:example.com"),
]
)
@patch.object(matrix_alertbot.callback.CommandFactory, "create", autospec=True) @patch.object(matrix_alertbot.callback.CommandFactory, "create", autospec=True)
async def test_message_without_prefix(self, fake_command_create: Mock) -> None: async def test_message_without_prefix(self, fake_command_create: Mock) -> None:
"""Tests the callback for RoomMessageText without any command prefix""" """Tests the callback for RoomMessageText without any command prefix"""
@ -82,6 +114,24 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
# Check that the command was not executed # Check that the command was not executed
fake_command_create.assert_not_called() fake_command_create.assert_not_called()
@patch.object(matrix_alertbot.command, "HelpCommand", autospec=True)
async def test_message_help_client_not_in_pool(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText without any command prefix"""
# Tests that the bot process messages in the room
fake_message_event = Mock(spec=nio.RoomMessageText)
fake_message_event.event_id = "some event id"
fake_message_event.sender = "@some_other_fake_user:example.com"
fake_message_event.body = "!alert help"
fake_message_event.source = {"content": {}}
self.fake_matrix_client_pool.matrix_client = None
# Pretend that we received a text message event
await self.callbacks.message(self.fake_room, fake_message_event)
# Check that the command was not executed
fake_command.assert_not_called()
@patch.object(matrix_alertbot.command, "HelpCommand", autospec=True) @patch.object(matrix_alertbot.command, "HelpCommand", autospec=True)
async def test_message_help_not_in_reply_with_prefix( async def test_message_help_not_in_reply_with_prefix(
self, fake_command: Mock self, fake_command: Mock
@ -271,6 +321,72 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
) )
fake_command.return_value.process.assert_called_once() fake_command.return_value.process.assert_called_once()
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
@patch.object(matrix_alertbot.command, "AckAlertCommand", autospec=True)
async def test_message_raise_exception(
self, fake_command: Mock, fake_logger
) -> None:
"""Tests the callback for RoomMessageText with the command prefix"""
# Tests that the bot process messages in the room that contain a command
fake_message_event = Mock(spec=nio.RoomMessageText)
fake_message_event.event_id = "some event id"
fake_message_event.sender = "@some_other_fake_user:example.com"
fake_message_event.body = "!alert ack"
fake_message_event.source = {
"content": {
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
}
}
fake_command.return_value.process.side_effect = (
nio.exceptions.LocalProtocolError
)
# Pretend that we received a text message event
await self.callbacks.message(self.fake_room, fake_message_event)
# Check that the command was not executed
fake_command.assert_called_once_with(
self.fake_matrix_client,
self.fake_cache,
self.fake_alertmanager_client,
self.fake_config,
self.fake_room,
fake_message_event.sender,
fake_message_event.event_id,
"some alert event id",
(),
)
fake_command.return_value.process.assert_called_once()
fake_logger.exception.assert_called_once()
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
async def test_reaction_client_not_in_pool(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix"""
# Tests that the bot process messages in the room that contain a command
fake_alert_event = Mock(spec=nio.RoomMessageText)
fake_alert_event.event_id = "some alert event id"
fake_alert_event.sender = self.fake_matrix_client.user_id
fake_reaction_event = Mock(spec=nio.ReactionEvent)
fake_reaction_event.event_id = "some event id"
fake_reaction_event.sender = "@some_other_fake_user:example.com"
fake_reaction_event.reacts_to = fake_alert_event.event_id
fake_reaction_event.key = "🤫"
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
fake_event_response.event = fake_alert_event
self.fake_matrix_client.room_get_event.return_value = fake_event_response
self.fake_matrix_client_pool.matrix_client = None
# Pretend that we received a text message event
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
# Check that we attempted to execute the command
fake_command.assert_not_called()
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True) @patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
async def test_reaction_to_existing_alert(self, fake_command: Mock) -> None: async def test_reaction_to_existing_alert(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix""" """Tests the callback for RoomMessageText with the command prefix"""
@ -365,6 +481,52 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
self.fake_room.room_id, fake_alert_event.event_id self.fake_room.room_id, fake_alert_event.event_id
) )
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
async def test_reaction_raise_exception(
self, fake_command: Mock, fake_logger: Mock
) -> None:
"""Tests the callback for RoomMessageText with the command prefix"""
# Tests that the bot process messages in the room that contain a command
fake_alert_event = Mock(spec=nio.RoomMessageText)
fake_alert_event.event_id = "some alert event id"
fake_alert_event.sender = self.fake_matrix_client.user_id
fake_reaction_event = Mock(spec=nio.ReactionEvent)
fake_reaction_event.event_id = "some event id"
fake_reaction_event.sender = "@some_other_fake_user:example.com"
fake_reaction_event.reacts_to = fake_alert_event.event_id
fake_reaction_event.key = "🤫"
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
fake_event_response.event = fake_alert_event
self.fake_matrix_client.room_get_event.return_value = fake_event_response
fake_command.return_value.process.side_effect = (
nio.exceptions.LocalProtocolError
)
# Pretend that we received a text message event
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
# Check that we attempted to execute the command
fake_command.assert_called_once_with(
self.fake_matrix_client,
self.fake_cache,
self.fake_alertmanager_client,
self.fake_config,
self.fake_room,
fake_reaction_event.sender,
fake_reaction_event.event_id,
"some alert event id",
)
fake_command.return_value.process.assert_called_once()
self.fake_matrix_client.room_get_event.assert_called_once_with(
self.fake_room.room_id, fake_alert_event.event_id
)
fake_logger.exception.assert_called_once()
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True) @patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
async def test_reaction_unknown(self, fake_command: Mock) -> None: async def test_reaction_unknown(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix""" """Tests the callback for RoomMessageText with the command prefix"""
@ -429,6 +591,28 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
fake_command.assert_not_called() fake_command.assert_not_called()
self.fake_matrix_client.room_get_event.assert_not_called() self.fake_matrix_client.room_get_event.assert_not_called()
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
async def test_redaction_client_not_in_pool(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix"""
# Tests that the bot process messages in the room that contain a command
fake_alert_event_id = "some alert event id"
fake_redaction_event = Mock(spec=nio.RedactionEvent)
fake_redaction_event.redacts = "some other event id"
fake_redaction_event.event_id = "some event id"
fake_redaction_event.sender = "@some_other_fake_user:example.com"
fake_cache_dict = {fake_redaction_event.redacts: fake_alert_event_id}
self.fake_cache.__getitem__.side_effect = fake_cache_dict.__getitem__
self.fake_matrix_client_pool.matrix_client = None
# Pretend that we received a text message event
await self.callbacks.redaction(self.fake_room, fake_redaction_event)
# Check that we attempted to execute the command
fake_command.assert_not_called()
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True) @patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
async def test_redaction(self, fake_command: Mock) -> None: async def test_redaction(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix""" """Tests the callback for RoomMessageText with the command prefix"""
@ -459,6 +643,45 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
) )
fake_command.return_value.process.assert_called_once() fake_command.return_value.process.assert_called_once()
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
async def test_redaction_raise_exception(
self, fake_command: Mock, fake_logger
) -> None:
"""Tests the callback for RoomMessageText with the command prefix"""
# Tests that the bot process messages in the room that contain a command
fake_alert_event_id = "some alert event id"
fake_redaction_event = Mock(spec=nio.RedactionEvent)
fake_redaction_event.redacts = "some other event id"
fake_redaction_event.event_id = "some event id"
fake_redaction_event.sender = "@some_other_fake_user:example.com"
fake_cache_dict = {fake_redaction_event.redacts: fake_alert_event_id}
self.fake_cache.__getitem__.side_effect = fake_cache_dict.__getitem__
fake_command.return_value.process.side_effect = (
nio.exceptions.LocalProtocolError
)
# Pretend that we received a text message event
await self.callbacks.redaction(self.fake_room, fake_redaction_event)
# Check that we attempted to execute the command
fake_command.assert_called_once_with(
self.fake_matrix_client,
self.fake_cache,
self.fake_alertmanager_client,
self.fake_config,
self.fake_room,
fake_redaction_event.sender,
fake_redaction_event.event_id,
fake_redaction_event.redacts,
)
fake_command.return_value.process.assert_called_once()
fake_logger.exception.assert_called_once()
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True) @patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
async def test_ignore_redaction_sent_by_bot_user(self, fake_command: Mock) -> None: async def test_ignore_redaction_sent_by_bot_user(self, fake_command: Mock) -> None:
"""Tests the callback for RoomMessageText with the command prefix""" """Tests the callback for RoomMessageText with the command prefix"""
@ -718,7 +941,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
fake_key_verification_event.transaction_id = fake_transaction_id fake_key_verification_event.transaction_id = fake_transaction_id
fake_sas = Mock() fake_sas = Mock()
fake_sas.get_mac.side_effect = key_verification_get_mac_raise_protocol_error fake_sas.get_mac.side_effect = nio.exceptions.LocalProtocolError
fake_transactions_dict = {fake_transaction_id: fake_sas} fake_transactions_dict = {fake_transaction_id: fake_sas}
self.fake_matrix_client.key_verifications = fake_transactions_dict self.fake_matrix_client.key_verifications = fake_transactions_dict
@ -751,6 +974,81 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
fake_sas.get_mac.assert_called_once_with() fake_sas.get_mac.assert_called_once_with()
self.fake_matrix_client.to_device.assert_called_once_with(fake_sas.get_mac()) self.fake_matrix_client.to_device.assert_called_once_with(fake_sas.get_mac())
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
async def test_decryption_failure(self, fake_logger) -> None:
fake_megolm_event = Mock(spec=nio.MegolmEvent)
fake_megolm_event.sender = "@some_other_fake_user:example.com"
fake_megolm_event.event_id = "some event id"
await self.callbacks.decryption_failure(self.fake_room, fake_megolm_event)
fake_logger.error.assert_called_once()
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
async def test_decryption_failure_in_unauthorized_room(self, fake_logger) -> None:
fake_megolm_event = Mock(spec=nio.MegolmEvent)
fake_megolm_event.sender = "@some_other_fake_user:example.com"
fake_megolm_event.event_id = "some event id"
self.fake_room.room_id = "!unauthorizedroom@example.com"
await self.callbacks.decryption_failure(self.fake_room, fake_megolm_event)
fake_logger.error.assert_not_called()
async def test_unknown_message(self) -> None:
fake_room_unknown_event = Mock(spec=nio.RoomMessageUnknown)
fake_room_unknown_event.source = {
"content": {
"msgtype": "m.key.verification.request",
"methods": ["m.sas.v1"],
}
}
fake_room_unknown_event.event_id = "some event id"
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
self.fake_matrix_client.room_send.assert_called_once_with(
self.fake_room.room_id,
"m.room.message",
{
"msgtype": "m.key.verification.ready",
"methods": ["m.sas.v1"],
"m.relates_to": {
"rel_type": "m.reference",
"event_id": fake_room_unknown_event.event_id,
},
},
)
async def test_unknown_message_with_msgtype_not_verification_request(self) -> None:
fake_room_unknown_event = Mock(spec=nio.RoomMessageUnknown)
fake_room_unknown_event.source = {
"content": {
"msgtype": "unknown",
"methods": ["m.sas.v1"],
}
}
fake_room_unknown_event.event_id = "some event id"
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
self.fake_matrix_client.room_send.assert_not_called()
async def test_unknown_message_with_method_not_sas_v1(self) -> None:
fake_room_unknown_event = Mock(spec=nio.RoomMessageUnknown)
fake_room_unknown_event.source = {
"content": {
"msgtype": "m.key.verification.request",
"methods": [],
}
}
fake_room_unknown_event.event_id = "some event id"
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
self.fake_matrix_client.room_send.assert_not_called()
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

View file

@ -1,5 +1,4 @@
import unittest import unittest
from typing import Any, Dict, Optional
from unittest.mock import Mock from unittest.mock import Mock
import nio import nio
@ -11,16 +10,6 @@ from matrix_alertbot.chat_functions import (
) )
async def send_room_raise_send_retry_error(
room_id: str,
message_type: str,
content: Dict[Any, Any],
tx_id: Optional[str] = None,
ignore_unverified_devices: bool = False,
) -> nio.RoomSendResponse:
raise nio.SendRetryError
class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase): class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
def setUp(self) -> None: def setUp(self) -> None:
pass pass
@ -179,7 +168,7 @@ class ChatFunctionsTestCase(unittest.IsolatedAsyncioTestCase):
async def test_send_text_to_room_raise_send_retry_error(self) -> None: async def test_send_text_to_room_raise_send_retry_error(self) -> None:
fake_matrix_client = Mock(spec=nio.AsyncClient) fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.room_send.side_effect = send_room_raise_send_retry_error fake_matrix_client.room_send.side_effect = nio.exceptions.SendRetryError
fake_room_id = "!abcdefgh:example.com" fake_room_id = "!abcdefgh:example.com"
fake_plaintext_body = "some plaintext message" fake_plaintext_body = "some plaintext message"

View file

@ -1,10 +1,12 @@
import os import os
import sys
import unittest import unittest
from datetime import timedelta from datetime import timedelta
from unittest.mock import Mock, patch from unittest.mock import Mock, patch
import yaml import yaml
import matrix_alertbot.config
from matrix_alertbot.config import DEFAULT_REACTIONS, Config from matrix_alertbot.config import DEFAULT_REACTIONS, Config
from matrix_alertbot.errors import ( from matrix_alertbot.errors import (
InvalidConfigError, InvalidConfigError,
@ -38,8 +40,15 @@ class ConfigTestCase(unittest.TestCase):
@patch("os.path.isdir") @patch("os.path.isdir")
@patch("os.path.exists") @patch("os.path.exists")
@patch("os.mkdir") @patch("os.mkdir")
@patch.object(matrix_alertbot.config, "logger", autospec=True)
@patch.object(matrix_alertbot.config, "logging", autospec=True)
def test_read_minimal_config( def test_read_minimal_config(
self, fake_mkdir: Mock, fake_path_exists: Mock, fake_path_isdir: Mock self,
fake_logging: Mock,
fake_logger: Mock,
fake_mkdir: Mock,
fake_path_exists: Mock,
fake_path_isdir: Mock,
) -> None: ) -> None:
fake_path_isdir.return_value = False fake_path_isdir.return_value = False
fake_path_exists.return_value = False fake_path_exists.return_value = False
@ -51,6 +60,11 @@ class ConfigTestCase(unittest.TestCase):
fake_path_exists.assert_called_once_with("data/store") fake_path_exists.assert_called_once_with("data/store")
fake_mkdir.assert_called_once_with("data/store") fake_mkdir.assert_called_once_with("data/store")
fake_logger.setLevel.assert_called_once_with("INFO")
fake_logger.addHandler.assert_called_once()
fake_logging.StreamHandler.return_value.setLevel("INFO")
fake_logging.StreamHandler.assert_called_once_with(sys.stdout)
self.assertEqual({"@fakes_user:matrix.example.com"}, config.user_ids) self.assertEqual({"@fakes_user:matrix.example.com"}, config.user_ids)
self.assertEqual(1, len(config.accounts)) self.assertEqual(1, len(config.accounts))
self.assertEqual("password", config.accounts[0].password) self.assertEqual("password", config.accounts[0].password)
@ -82,8 +96,15 @@ class ConfigTestCase(unittest.TestCase):
@patch("os.path.isdir") @patch("os.path.isdir")
@patch("os.path.exists") @patch("os.path.exists")
@patch("os.mkdir") @patch("os.mkdir")
@patch.object(matrix_alertbot.config, "logger", autospec=True)
@patch.object(matrix_alertbot.config, "logging", autospec=True)
def test_read_full_config( def test_read_full_config(
self, fake_mkdir: Mock, fake_path_exists: Mock, fake_path_isdir: Mock self,
fake_logging: Mock,
fake_logger: Mock,
fake_mkdir: Mock,
fake_path_exists: Mock,
fake_path_isdir: Mock,
) -> None: ) -> None:
fake_path_isdir.return_value = False fake_path_isdir.return_value = False
fake_path_exists.return_value = False fake_path_exists.return_value = False
@ -95,6 +116,11 @@ class ConfigTestCase(unittest.TestCase):
fake_path_exists.assert_called_once_with("data/store") fake_path_exists.assert_called_once_with("data/store")
fake_mkdir.assert_called_once_with("data/store") fake_mkdir.assert_called_once_with("data/store")
fake_logger.setLevel.assert_called_once_with("DEBUG")
fake_logger.addHandler.assert_called_once()
fake_logging.FileHandler.return_value.setLevel("DEBUG")
fake_logging.FileHandler.assert_called_once_with("fake.log")
self.assertEqual( self.assertEqual(
{"@fakes_user:matrix.example.com", "@other_user:matrix.domain.tld"}, {"@fakes_user:matrix.example.com", "@other_user:matrix.domain.tld"},
config.user_ids, config.user_ids,
@ -333,6 +359,60 @@ class ConfigTestCase(unittest.TestCase):
with self.assertRaises(InvalidConfigError): with self.assertRaises(InvalidConfigError):
config._parse_config_values() config._parse_config_values()
@patch("os.path.isdir")
@patch("os.path.exists")
@patch("os.mkdir")
@patch.object(matrix_alertbot.config, "logger")
def test_parse_config_with_both_logging_disabled(
self,
fake_logger: Mock,
fake_mkdir: Mock,
fake_path_exists: Mock,
fake_path_isdir: Mock,
) -> None:
fake_path_isdir.return_value = False
fake_path_exists.return_value = False
config_path = os.path.join(CONFIG_RESOURCES_DIR, "config.full.yml")
config = DummyConfig(config_path)
config.config_dict["logging"]["file_logging"]["enabled"] = False
config.config_dict["logging"]["console_logging"]["enabled"] = False
config._parse_config_values()
fake_logger.addHandler.assert_not_called()
fake_logger.setLevel.assert_called_once_with("DEBUG")
@patch("os.path.isdir")
@patch("os.path.exists")
@patch("os.mkdir")
@patch.object(matrix_alertbot.config, "logger", autospec=True)
@patch.object(matrix_alertbot.config, "logging", autospec=True)
def test_parse_config_with_level_logging_different(
self,
fake_logging: Mock,
fake_logger: Mock,
fake_mkdir: Mock,
fake_path_exists: Mock,
fake_path_isdir: Mock,
) -> None:
fake_path_isdir.return_value = False
fake_path_exists.return_value = False
config_path = os.path.join(CONFIG_RESOURCES_DIR, "config.full.yml")
config = DummyConfig(config_path)
config.config_dict["logging"]["file_logging"]["enabled"] = True
config.config_dict["logging"]["file_logging"]["level"] = "WARN"
config.config_dict["logging"]["console_logging"]["enabled"] = True
config.config_dict["logging"]["console_logging"]["level"] = "ERROR"
config._parse_config_values()
self.assertEqual(2, fake_logger.addHandler.call_count)
fake_logger.setLevel.assert_called_once_with("DEBUG")
fake_logging.FileHandler.return_value.setLevel.assert_called_with("WARN")
fake_logging.StreamHandler.return_value.setLevel.assert_called_with("ERROR")
if __name__ == "__main__": if __name__ == "__main__":
unittest.main() unittest.main()

302
tests/test_matrix.py Normal file
View file

@ -0,0 +1,302 @@
from __future__ import annotations
import random
import unittest
from unittest.mock import Mock, call, patch
import nio
from diskcache import Cache
import matrix_alertbot
import matrix_alertbot.matrix
from matrix_alertbot.alertmanager import AlertmanagerClient
from matrix_alertbot.config import AccountConfig, Config
from matrix_alertbot.matrix import MatrixClientPool
def mock_create_matrix_client(
matrix_client_pool: MatrixClientPool,
account: AccountConfig,
alertmanager_client: AlertmanagerClient,
cache: Cache,
config: Config,
) -> nio.AsyncClient:
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_matrix_client.logged_in = True
return fake_matrix_client
class FakeAsyncClientConfig:
def __init__(
self,
max_limit_exceeded: int,
max_timeouts: int,
store_sync_tokens: bool,
encryption_enabled: bool,
) -> None:
if encryption_enabled:
raise ImportWarning()
self.max_limit_exceeded = max_limit_exceeded
self.max_timeouts = max_timeouts
self.store_sync_tokens = store_sync_tokens
self.encryption_enabled = encryption_enabled
class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
async def asyncSetUp(self) -> None:
random.seed(42)
self.fake_alertmanager_client = Mock(spec=AlertmanagerClient)
self.fake_cache = Mock(spec=Cache)
self.fake_account_config_1 = Mock(spec=AccountConfig)
self.fake_account_config_1.id = "@fake_user:matrix.example.com"
self.fake_account_config_1.homeserver_url = "https://matrix.example.com"
self.fake_account_config_1.device_id = "ABCDEFGH"
self.fake_account_config_1.token_file = "account1.token.secret"
self.fake_account_config_2 = Mock(spec=AccountConfig)
self.fake_account_config_2.id = "@other_user:chat.example.com"
self.fake_account_config_2.homeserver_url = "https://chat.example.com"
self.fake_account_config_2.device_id = "IJKLMNOP"
self.fake_account_config_2.token_file = "account2.token.secret"
self.fake_config = Mock(spec=Config)
self.fake_config.store_dir = "/dev/null"
self.fake_config.command_prefix = "!alert"
self.fake_config.accounts = [
self.fake_account_config_1,
self.fake_account_config_2,
]
@patch.object(
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
)
async def test_init_matrix_client_pool(self, fake_create_matrix_client) -> None:
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_create_matrix_client.return_value = fake_matrix_client
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
fake_create_matrix_client.assert_has_calls(
[
call(
matrix_client_pool,
self.fake_account_config_1,
self.fake_alertmanager_client,
self.fake_cache,
self.fake_config,
),
call(
matrix_client_pool,
self.fake_account_config_2,
self.fake_alertmanager_client,
self.fake_cache,
self.fake_config,
),
]
)
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
self.assertEqual(fake_matrix_client, matrix_client_pool.matrix_client)
self.assertEqual(2, len(matrix_client_pool._accounts))
self.assertEqual(2, len(matrix_client_pool._matrix_clients))
@patch.object(
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
)
async def test_close_matrix_client_pool(self, fake_create_matrix_client) -> None:
fake_matrix_client = Mock(spec=nio.AsyncClient)
fake_create_matrix_client.return_value = fake_matrix_client
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
await matrix_client_pool.close()
fake_matrix_client.close.assert_has_calls([(call(), call())])
@patch.object(
matrix_alertbot.matrix.MatrixClientPool,
"_create_matrix_client",
autospec=True,
side_effect=mock_create_matrix_client,
)
async def test_switch_active_client(self, fake_create_matrix_client) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
fake_matrix_client_1 = matrix_client_pool.matrix_client
await matrix_client_pool.switch_active_client()
fake_matrix_client_2 = matrix_client_pool.matrix_client
self.assertEqual(self.fake_account_config_2, matrix_client_pool.account)
self.assertNotEqual(fake_matrix_client_2, fake_matrix_client_1)
await matrix_client_pool.switch_active_client()
fake_matrix_client_3 = matrix_client_pool.matrix_client
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
self.assertEqual(fake_matrix_client_3, fake_matrix_client_1)
@patch.object(
matrix_alertbot.matrix.MatrixClientPool,
"_create_matrix_client",
autospec=True,
side_effect=mock_create_matrix_client,
)
async def test_switch_active_client_with_whoami_raise_exception(
self, fake_create_matrix_client
) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
for fake_matrix_client in matrix_client_pool._matrix_clients.values():
fake_matrix_client.whoami.side_effect = Exception
fake_matrix_client_1 = matrix_client_pool.matrix_client
await matrix_client_pool.switch_active_client()
fake_matrix_client_2 = matrix_client_pool.matrix_client
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
self.assertEqual(fake_matrix_client_2, fake_matrix_client_1)
@patch.object(
matrix_alertbot.matrix.MatrixClientPool,
"_create_matrix_client",
autospec=True,
side_effect=mock_create_matrix_client,
)
async def test_switch_active_client_with_whoami_error(
self, fake_create_matrix_client
) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
for fake_matrix_client in matrix_client_pool._matrix_clients.values():
fake_matrix_client.whoami.return_value = Mock(
spec=nio.responses.WhoamiError
)
fake_matrix_client_1 = matrix_client_pool.matrix_client
await matrix_client_pool.switch_active_client()
fake_matrix_client_2 = matrix_client_pool.matrix_client
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
self.assertEqual(fake_matrix_client_2, fake_matrix_client_1)
@patch.object(
matrix_alertbot.matrix.MatrixClientPool,
"_create_matrix_client",
autospec=True,
side_effect=mock_create_matrix_client,
)
async def test_switch_active_client_with_whoami_error_and_not_logged_in(
self, fake_create_matrix_client
) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
for fake_matrix_client in matrix_client_pool._matrix_clients.values():
fake_matrix_client.whoami.return_value = Mock(
spec=nio.responses.WhoamiError
)
fake_matrix_client.logged_in = False
fake_matrix_client_1 = matrix_client_pool.matrix_client
await matrix_client_pool.switch_active_client()
fake_matrix_client_2 = matrix_client_pool.matrix_client
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
self.assertEqual(fake_matrix_client_2, fake_matrix_client_1)
@patch.object(
matrix_alertbot.matrix, "AsyncClientConfig", spec=nio.AsyncClientConfig
)
async def test_create_matrix_client(self, fake_async_client_config: Mock) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
matrix_client_1 = matrix_client_pool._matrix_clients[self.fake_account_config_1]
self.assertEqual(self.fake_account_config_1.id, matrix_client_1.user)
self.assertEqual(
self.fake_account_config_1.device_id, matrix_client_1.device_id
)
self.assertEqual(
self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver
)
self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path)
self.assertEqual(6, len(matrix_client_1.event_callbacks))
self.assertEqual(4, len(matrix_client_1.to_device_callbacks))
fake_async_client_config.assert_has_calls(
[
call(
max_limit_exceeded=5,
max_timeouts=3,
store_sync_tokens=True,
encryption_enabled=True,
),
call(
max_limit_exceeded=5,
max_timeouts=3,
store_sync_tokens=True,
encryption_enabled=True,
),
]
)
@patch.object(
matrix_alertbot.matrix,
"AsyncClientConfig",
spec=nio.AsyncClientConfig,
side_effect=FakeAsyncClientConfig,
)
async def test_create_matrix_client_with_encryption_disabled(
self, fake_async_client_config: Mock
) -> None:
matrix_client_pool = MatrixClientPool(
alertmanager_client=self.fake_alertmanager_client,
cache=self.fake_cache,
config=self.fake_config,
)
matrix_client_1 = matrix_client_pool._matrix_clients[self.fake_account_config_1]
self.assertEqual(self.fake_account_config_1.id, matrix_client_1.user)
self.assertEqual(
self.fake_account_config_1.device_id, matrix_client_1.device_id
)
self.assertEqual(
self.fake_account_config_1.homeserver_url, matrix_client_1.homeserver
)
self.assertEqual(self.fake_config.store_dir, matrix_client_1.store_path)
self.assertEqual(6, len(matrix_client_1.event_callbacks))
self.assertEqual(4, len(matrix_client_1.to_device_callbacks))
self.assertEqual(5, matrix_client_1.config.max_limit_exceeded)
self.assertEqual(3, matrix_client_1.config.max_timeouts)
self.assertTrue(matrix_client_1.config.store_sync_tokens)
self.assertFalse(matrix_client_1.config.encryption_enabled)
if __name__ == "__main__":
unittest.main()

View file

@ -4,27 +4,21 @@ from unittest.mock import Mock, call, patch
import aiohttp.test_utils import aiohttp.test_utils
import nio import nio
from aiohttp import web from aiohttp import web, web_request
from diskcache import Cache from diskcache import Cache
from nio.exceptions import LocalProtocolError
from nio.responses import RoomSendResponse
import matrix_alertbot.webhook import matrix_alertbot.webhook
from matrix_alertbot.alert import Alert, AlertRenderer
from matrix_alertbot.alertmanager import AlertmanagerClient from matrix_alertbot.alertmanager import AlertmanagerClient
from matrix_alertbot.config import Config from matrix_alertbot.config import Config
from matrix_alertbot.errors import ( from matrix_alertbot.errors import (
AlertmanagerError, AlertmanagerError,
MatrixClientError,
SilenceExtendError, SilenceExtendError,
SilenceNotFoundError, SilenceNotFoundError,
) )
from matrix_alertbot.matrix import MatrixClientPool from matrix_alertbot.matrix import MatrixClientPool
from matrix_alertbot.webhook import Webhook from matrix_alertbot.webhook import Webhook, create_alert
def send_text_to_room_raise_error(
client: nio.AsyncClient, room_id: str, plaintext: str, html: str, notice: bool
) -> RoomSendResponse:
raise LocalProtocolError
def update_silence_raise_silence_not_found(fingerprint: str) -> str: def update_silence_raise_silence_not_found(fingerprint: str) -> str:
@ -45,6 +39,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_matrix_client_pool = Mock(spec=MatrixClientPool) self.fake_matrix_client_pool = Mock(spec=MatrixClientPool)
self.fake_matrix_client_pool.matrix_client = self.fake_matrix_client self.fake_matrix_client_pool.matrix_client = self.fake_matrix_client
self.fake_alertmanager_client = Mock(spec=AlertmanagerClient) self.fake_alertmanager_client = Mock(spec=AlertmanagerClient)
self.fake_alert_renderer = Mock(spec=AlertRenderer)
self.fake_cache = Mock(spec=Cache) self.fake_cache = Mock(spec=Cache)
self.fake_room_id = "!abcdefg:example.com" self.fake_room_id = "!abcdefg:example.com"
@ -57,30 +52,41 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_config.cache_expire_time = 0 self.fake_config.cache_expire_time = 0
self.fake_config.template_dir = None self.fake_config.template_dir = None
self.fake_request = Mock(spec=web_request.Request)
self.fake_request.app = {
"alertmanager_client": self.fake_alertmanager_client,
"alert_renderer": self.fake_alert_renderer,
"matrix_client_pool": self.fake_matrix_client_pool,
"cache": self.fake_cache,
"config": self.fake_config,
}
self.fake_alert_1 = {
"fingerprint": "fingerprint1",
"generatorURL": "http://example.com/alert1",
"status": "firing",
"labels": {
"alertname": "alert1",
"severity": "critical",
"job": "job1",
},
"annotations": {"description": "some description1"},
}
self.fake_alert_2 = {
"fingerprint": "fingerprint2",
"generatorURL": "http://example.com/alert2",
"status": "resolved",
"labels": {
"alertname": "alert2",
"severity": "warning",
"job": "job2",
},
"annotations": {"description": "some description2"},
}
self.fake_alerts = { self.fake_alerts = {
"alerts": [ "alerts": [
{ self.fake_alert_1,
"fingerprint": "fingerprint1", self.fake_alert_2,
"generatorURL": "http://example.com/alert1",
"status": "firing",
"labels": {
"alertname": "alert1",
"severity": "critical",
"job": "job1",
},
"annotations": {"description": "some description1"},
},
{
"fingerprint": "fingerprint2",
"generatorURL": "http://example.com/alert2",
"status": "resolved",
"labels": {
"alertname": "alert2",
"severity": "warning",
"job": "job2",
},
"annotations": {"description": "some description2"},
},
] ]
} }
@ -314,13 +320,14 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_cache.set.assert_not_called() self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_not_called() self.fake_cache.delete.assert_not_called()
@patch.object(matrix_alertbot.webhook, "logger", autospec=True)
@patch.object( @patch.object(
matrix_alertbot.webhook, matrix_alertbot.webhook,
"send_text_to_room", "send_text_to_room",
side_effect=send_text_to_room_raise_error, side_effect=nio.exceptions.LocalProtocolError("Local protocol error"),
) )
async def test_post_alerts_raise_send_error( async def test_post_alerts_raise_send_error(
self, fake_send_text_to_room: Mock self, fake_send_text_to_room: Mock, fake_logger: Mock
) -> None: ) -> None:
self.fake_alertmanager_client.update_silence.side_effect = ( self.fake_alertmanager_client.update_silence.side_effect = (
update_silence_raise_silence_not_found update_silence_raise_silence_not_found
@ -341,6 +348,178 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
self.fake_cache.set.assert_not_called() self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_called_once_with("fingerprint1") self.fake_cache.delete.assert_called_once_with("fingerprint1")
fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Local protocol error"
)
@patch.object(matrix_alertbot.webhook, "logger", autospec=True)
@patch.object(
matrix_alertbot.webhook,
"create_alert",
side_effect=MatrixClientError("Matrix client error"),
)
async def test_post_alerts_raise_matrix_client_error(
self, fake_create_alert: Mock, fake_logger: Mock
) -> None:
self.fake_alertmanager_client.update_silence.side_effect = (
update_silence_raise_silence_not_found
)
data = self.fake_alerts
async with self.client.request(
"POST", f"/alerts/{self.fake_room_id}", json=data
) as response:
self.assertEqual(500, response.status)
error_msg = await response.text()
self.assertEqual(
"An error occured when sending alert with fingerprint 'fingerprint1' to Matrix room.",
error_msg,
)
fake_create_alert.assert_called_once()
fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Matrix client error"
)
@patch.object(matrix_alertbot.webhook, "logger", autospec=True)
@patch.object(
matrix_alertbot.webhook,
"send_text_to_room",
side_effect=Exception("Exception"),
)
async def test_post_alerts_raise_exception(
self, fake_send_text_to_room: Mock, fake_logger: Mock
) -> None:
self.fake_alertmanager_client.update_silence.side_effect = (
update_silence_raise_silence_not_found
)
data = self.fake_alerts
async with self.client.request(
"POST", f"/alerts/{self.fake_room_id}", json=data
) as response:
self.assertEqual(500, response.status)
error_msg = await response.text()
self.assertEqual(
"An exception occured when sending alert with fingerprint 'fingerprint1' to Matrix room.",
error_msg,
)
fake_send_text_to_room.assert_called_once()
self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_called_once_with("fingerprint1")
fake_logger.error.assert_called_once_with(
"Unable to send alert fingerprint1 to Matrix room !abcdefg:example.com: Exception"
)
async def test_create_alert_update_silence(self) -> None:
fake_alert = Mock(spec=Alert)
fake_alert.firing = True
fake_alert.fingerprint = "fingerprint"
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
self.fake_alertmanager_client.update_silence.assert_called_once_with(
fake_alert.fingerprint
)
self.fake_alert_renderer.render.assert_not_called()
@patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True)
async def test_create_alert_with_silence_not_found_error(
self, fake_send_text_to_room: Mock
) -> None:
fake_alert = Mock(spec=Alert)
fake_alert.firing = True
fake_alert.fingerprint = "fingerprint"
self.fake_alertmanager_client.update_silence.side_effect = SilenceNotFoundError
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
self.fake_alertmanager_client.update_silence.assert_called_once_with(
fake_alert.fingerprint
)
self.fake_alert_renderer.render.assert_has_calls(
[call(fake_alert, html=False), call(fake_alert, html=True)]
)
fake_send_text_to_room.assert_called_once()
self.fake_cache.set.assert_called_once_with(
fake_send_text_to_room.return_value.event_id,
fake_alert.fingerprint,
expire=self.fake_config.cache_expire_time,
)
self.fake_cache.delete.assert_called_once_with(fake_alert.fingerprint)
@patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True)
async def test_create_alert_with_silence_extend_error(
self, fake_send_text_to_room: Mock
) -> None:
fake_alert = Mock(spec=Alert)
fake_alert.firing = True
fake_alert.fingerprint = "fingerprint"
self.fake_alertmanager_client.update_silence.side_effect = SilenceExtendError
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
self.fake_alertmanager_client.update_silence.assert_called_once_with(
fake_alert.fingerprint
)
self.fake_alert_renderer.render.assert_has_calls(
[call(fake_alert, html=False), call(fake_alert, html=True)]
)
fake_send_text_to_room.assert_called_once()
self.fake_cache.set.assert_called_once_with(
fake_send_text_to_room.return_value.event_id,
fake_alert.fingerprint,
expire=self.fake_config.cache_expire_time,
)
self.fake_cache.delete.assert_not_called()
@patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True)
async def test_create_alert_not_firing(self, fake_send_text_to_room: Mock) -> None:
fake_alert = Mock(spec=Alert)
fake_alert.firing = False
fake_alert.fingerprint = "fingerprint"
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
self.fake_alertmanager_client.update_silence.assert_not_called()
self.fake_alert_renderer.render.assert_has_calls(
[call(fake_alert, html=False), call(fake_alert, html=True)]
)
fake_send_text_to_room.assert_called_once()
self.fake_cache.set.assert_not_called()
self.fake_cache.delete.assert_called_once_with(fake_alert.fingerprint)
@patch.object(matrix_alertbot.webhook, "send_text_to_room", autospec=True)
async def test_create_alert_not_firing_raise_matrix_client_error(
self, fake_send_text_to_room: Mock
) -> None:
fake_alert = Mock(spec=Alert)
fake_alert.firing = False
fake_alert.fingerprint = "fingerprint"
self.fake_matrix_client_pool.matrix_client = None
with self.assertRaises(MatrixClientError):
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
self.fake_alertmanager_client.update_silence.assert_not_called()
self.fake_alert_renderer.render.assert_has_calls(
[call(fake_alert, html=False), call(fake_alert, html=True)]
)
fake_send_text_to_room.assert_not_called()
async def test_health(self) -> None: async def test_health(self) -> None:
async with self.client.request("GET", "/health") as response: async with self.client.request("GET", "/health") as response:
self.assertEqual(200, response.status) self.assertEqual(200, response.status)