feature to send alerts in direct message
This commit is contained in:
parent
d277d742e7
commit
13976ea254
19 changed files with 1211 additions and 136 deletions
|
@ -39,6 +39,16 @@ matrix:
|
||||||
# Default is listed here.
|
# Default is listed here.
|
||||||
allowed_reactions: [🤫, 😶, 🤐, 🙊, 🔇, 🔕]
|
allowed_reactions: [🤫, 😶, 🤐, 🙊, 🔇, 🔕]
|
||||||
|
|
||||||
|
dm:
|
||||||
|
filter_labels:
|
||||||
|
matrix: dm
|
||||||
|
select_label: uuid
|
||||||
|
room_title: Alerts for {user}
|
||||||
|
users:
|
||||||
|
- matrix_id: "@user:matrix.example.com"
|
||||||
|
user_id:
|
||||||
|
- ec76b3e6-b49c-46c3-bd35-a329eaeafc4c
|
||||||
|
|
||||||
webhook:
|
webhook:
|
||||||
# Address and port for which the bot should listen to
|
# Address and port for which the bot should listen to
|
||||||
address: 0.0.0.0
|
address: 0.0.0.0
|
||||||
|
|
|
@ -22,15 +22,15 @@
|
||||||
# We use an initial docker container to build all of the runtime dependencies,
|
# We use an initial docker container to build all of the runtime dependencies,
|
||||||
# then transfer those dependencies to the container we're going to ship,
|
# then transfer those dependencies to the container we're going to ship,
|
||||||
# before throwing this one away
|
# before throwing this one away
|
||||||
ARG PYTHON_VERSION=3.10
|
ARG PYTHON_VERSION=3.11
|
||||||
FROM python:${PYTHON_VERSION}-alpine as builder
|
FROM python:${PYTHON_VERSION}-alpine AS builder
|
||||||
|
|
||||||
##
|
##
|
||||||
## Build libolm for matrix-nio e2e support
|
## Build libolm for matrix-nio e2e support
|
||||||
##
|
##
|
||||||
|
|
||||||
# Install libolm build dependencies
|
# Install libolm build dependencies
|
||||||
ARG LIBOLM_VERSION=3.2.10
|
ARG LIBOLM_VERSION=3.2.16
|
||||||
RUN apk add --no-cache \
|
RUN apk add --no-cache \
|
||||||
make \
|
make \
|
||||||
cmake \
|
cmake \
|
||||||
|
@ -49,6 +49,9 @@ ENV PATH="$VIRTUAL_ENV/bin:$PATH"
|
||||||
RUN python -m venv "${VIRTUAL_ENV}"
|
RUN python -m venv "${VIRTUAL_ENV}"
|
||||||
|
|
||||||
WORKDIR "${PROJECT_DIR}"
|
WORKDIR "${PROJECT_DIR}"
|
||||||
|
|
||||||
|
RUN pip install setuptools
|
||||||
|
|
||||||
# Build libolm
|
# Build libolm
|
||||||
#
|
#
|
||||||
# Also build the libolm python bindings and place them at /python-libs
|
# Also build the libolm python bindings and place them at /python-libs
|
||||||
|
|
|
@ -25,3 +25,9 @@ scrape_configs:
|
||||||
|
|
||||||
static_configs:
|
static_configs:
|
||||||
- targets: ["localhost:9090"]
|
- targets: ["localhost:9090"]
|
||||||
|
- targets: ['example.com']
|
||||||
|
labels:
|
||||||
|
uuid: "d8798985-a1d2-431a-9275-106b9cf63922"
|
||||||
|
- targets: ['matrix.org']
|
||||||
|
labels:
|
||||||
|
uuid: "08119079-ba91-4b23-b9a5-519fcb3b5fad"
|
||||||
|
|
|
@ -6,6 +6,7 @@ groups:
|
||||||
expr: up == 1
|
expr: up == 1
|
||||||
labels:
|
labels:
|
||||||
severity: critical
|
severity: critical
|
||||||
|
matrix: dm
|
||||||
annotations:
|
annotations:
|
||||||
description: 'Instance {{ $labels.instance }} is up'
|
description: 'Instance {{ $labels.instance }} is up'
|
||||||
summary: 'Instance is up'
|
summary: 'Instance is up'
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
|
import re
|
||||||
from typing import Dict, Optional
|
from typing import Dict, Optional
|
||||||
|
|
||||||
from jinja2 import (
|
from jinja2 import (
|
||||||
|
@ -25,6 +26,7 @@ class Alert:
|
||||||
labels: Dict[str, str],
|
labels: Dict[str, str],
|
||||||
annotations: Dict[str, str],
|
annotations: Dict[str, str],
|
||||||
firing: bool = True,
|
firing: bool = True,
|
||||||
|
user_id: Optional[str] = None,
|
||||||
):
|
):
|
||||||
self.fingerprint = fingerprint
|
self.fingerprint = fingerprint
|
||||||
self.url = url
|
self.url = url
|
||||||
|
@ -39,6 +41,8 @@ class Alert:
|
||||||
else:
|
else:
|
||||||
self.status = "resolved"
|
self.status = "resolved"
|
||||||
|
|
||||||
|
self.user_id = user_id
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def from_dict(data: Dict) -> Alert:
|
def from_dict(data: Dict) -> Alert:
|
||||||
return Alert(
|
return Alert(
|
||||||
|
@ -57,6 +61,17 @@ class Alert:
|
||||||
def color(self) -> str:
|
def color(self) -> str:
|
||||||
return self.COLORS[self.status]
|
return self.COLORS[self.status]
|
||||||
|
|
||||||
|
def match_label(self, label_name: str, pattern: re.Pattern[str]) -> bool:
|
||||||
|
if label_name not in self.labels:
|
||||||
|
return False
|
||||||
|
return pattern.match(self.labels[label_name]) is not None
|
||||||
|
|
||||||
|
def match_all_labels(self, labels: Dict[str, re.Pattern[str]]) -> bool:
|
||||||
|
for label_name, pattern in labels.items():
|
||||||
|
if not self.match_label(label_name, pattern):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class AlertRenderer:
|
class AlertRenderer:
|
||||||
def __init__(self, template_dir: Optional[str] = None) -> None:
|
def __init__(self, template_dir: Optional[str] = None) -> None:
|
||||||
|
|
|
@ -10,6 +10,7 @@ from aiohttp_prometheus_exporter.trace import PrometheusTraceConfig
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
|
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import (
|
||||||
|
AlertmanagerClientError,
|
||||||
AlertmanagerServerError,
|
AlertmanagerServerError,
|
||||||
AlertNotFoundError,
|
AlertNotFoundError,
|
||||||
SilenceExpiredError,
|
SilenceExpiredError,
|
||||||
|
@ -46,12 +47,27 @@ class AlertmanagerClient:
|
||||||
def __init__(self, url: str, cache: Cache) -> None:
|
def __init__(self, url: str, cache: Cache) -> None:
|
||||||
self.api_url = f"{url}/api/v2"
|
self.api_url = f"{url}/api/v2"
|
||||||
self.cache = cache
|
self.cache = cache
|
||||||
|
self.session = None
|
||||||
|
|
||||||
|
async def start(self) -> None:
|
||||||
self.session = aiohttp.ClientSession(trace_configs=[PrometheusTraceConfig()])
|
self.session = aiohttp.ClientSession(trace_configs=[PrometheusTraceConfig()])
|
||||||
|
|
||||||
async def close(self) -> None:
|
async def close(self) -> None:
|
||||||
|
if self.session is not None:
|
||||||
await self.session.close()
|
await self.session.close()
|
||||||
|
|
||||||
|
async def __aenter__(self) -> AlertmanagerClient:
|
||||||
|
if self.session is None:
|
||||||
|
await self.start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
async def __aexit__(self, *args) -> None:
|
||||||
|
await self.close()
|
||||||
|
|
||||||
async def get_alerts(self) -> List[AlertDict]:
|
async def get_alerts(self) -> List[AlertDict]:
|
||||||
|
if self.session is None:
|
||||||
|
raise AlertmanagerClientError("Alertmanager client is not started")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with self.session.get(f"{self.api_url}/alerts") as response:
|
async with self.session.get(f"{self.api_url}/alerts") as response:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
@ -67,6 +83,9 @@ class AlertmanagerClient:
|
||||||
return self._find_alert(fingerprint, alerts)
|
return self._find_alert(fingerprint, alerts)
|
||||||
|
|
||||||
async def get_silences(self) -> List[SilenceDict]:
|
async def get_silences(self) -> List[SilenceDict]:
|
||||||
|
if self.session is None:
|
||||||
|
raise AlertmanagerClientError("Alertmanager client is not started")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with self.session.get(f"{self.api_url}/silences") as response:
|
async with self.session.get(f"{self.api_url}/silences") as response:
|
||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
|
@ -170,6 +189,9 @@ class AlertmanagerClient:
|
||||||
duration_seconds: Optional[int] = None,
|
duration_seconds: Optional[int] = None,
|
||||||
silence_id: Optional[str] = None,
|
silence_id: Optional[str] = None,
|
||||||
) -> str:
|
) -> str:
|
||||||
|
if self.session is None:
|
||||||
|
raise AlertmanagerClientError("Alertmanager client is not started")
|
||||||
|
|
||||||
if duration_seconds is None:
|
if duration_seconds is None:
|
||||||
duration_delta = DEFAULT_DURATION
|
duration_delta = DEFAULT_DURATION
|
||||||
elif duration_seconds > MAX_DURATION.total_seconds():
|
elif duration_seconds > MAX_DURATION.total_seconds():
|
||||||
|
@ -204,6 +226,9 @@ class AlertmanagerClient:
|
||||||
return data["silenceID"]
|
return data["silenceID"]
|
||||||
|
|
||||||
async def delete_silence(self, silence_id: str) -> None:
|
async def delete_silence(self, silence_id: str) -> None:
|
||||||
|
if self.session is None:
|
||||||
|
raise AlertmanagerClientError("Alertmanager client is not started")
|
||||||
|
|
||||||
silence = await self.get_silence(silence_id)
|
silence = await self.get_silence(silence_id)
|
||||||
|
|
||||||
silence_state = silence["status"]["state"]
|
silence_state = silence["status"]["state"]
|
||||||
|
|
|
@ -77,7 +77,10 @@ class Callbacks:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore messages from unauthorized room
|
# Ignore messages from unauthorized room
|
||||||
if room.room_id not in self.config.allowed_rooms:
|
if (
|
||||||
|
room.room_id not in self.config.allowed_rooms
|
||||||
|
and event.sender not in self.config.dm_users.inverse
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Extract the message text
|
# Extract the message text
|
||||||
|
@ -167,7 +170,11 @@ class Callbacks:
|
||||||
event: The invite event.
|
event: The invite event.
|
||||||
"""
|
"""
|
||||||
# Ignore invites from unauthorized room
|
# Ignore invites from unauthorized room
|
||||||
if room.room_id not in self.config.allowed_rooms:
|
if (
|
||||||
|
room.room_id not in self.config.allowed_rooms
|
||||||
|
and event.sender not in self.config.user_ids
|
||||||
|
and event.sender not in self.config.dm_users.inverse
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.debug(
|
logger.debug(
|
||||||
|
@ -229,7 +236,10 @@ class Callbacks:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore reactions from unauthorized room
|
# Ignore reactions from unauthorized room
|
||||||
if room.room_id not in self.config.allowed_rooms:
|
if (
|
||||||
|
room.room_id not in self.config.allowed_rooms
|
||||||
|
and event.sender not in self.config.dm_users.inverse
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore reactions from ourselves
|
# Ignore reactions from ourselves
|
||||||
|
@ -317,7 +327,10 @@ class Callbacks:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore events from unauthorized room
|
# Ignore events from unauthorized room
|
||||||
if room.room_id not in self.config.allowed_rooms:
|
if (
|
||||||
|
room.room_id not in self.config.allowed_rooms
|
||||||
|
and event.sender not in self.config.dm_users.inverse
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
# Ignore redactions from ourselves
|
# Ignore redactions from ourselves
|
||||||
|
@ -359,7 +372,10 @@ class Callbacks:
|
||||||
event: The encrypted event that we were unable to decrypt.
|
event: The encrypted event that we were unable to decrypt.
|
||||||
"""
|
"""
|
||||||
# Ignore events from unauthorized room
|
# Ignore events from unauthorized room
|
||||||
if room.room_id not in self.config.allowed_rooms:
|
if (
|
||||||
|
room.room_id not in self.config.allowed_rooms
|
||||||
|
and event.sender not in self.config.dm_users.inverse
|
||||||
|
):
|
||||||
return
|
return
|
||||||
|
|
||||||
logger.error(
|
logger.error(
|
||||||
|
|
|
@ -4,7 +4,7 @@ import logging
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any, Dict, List, Optional, TypeVar
|
||||||
|
|
||||||
import pytimeparse2
|
import pytimeparse2
|
||||||
import yaml
|
import yaml
|
||||||
|
@ -53,6 +53,29 @@ INSULT_REACTIONS = {
|
||||||
"🖕",
|
"🖕",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
K = TypeVar("K")
|
||||||
|
V = TypeVar("V")
|
||||||
|
|
||||||
|
|
||||||
|
class BiDict(dict[K, V]):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
super(BiDict, self).__init__(*args, **kwargs)
|
||||||
|
self.inverse = {}
|
||||||
|
for key, value in self.items():
|
||||||
|
self.inverse.setdefault(value, set()).add(key)
|
||||||
|
|
||||||
|
def __setitem__(self, key: K, value: V):
|
||||||
|
if key in self:
|
||||||
|
self.inverse[self[key]].remove(key)
|
||||||
|
super(BiDict, self).__setitem__(key, value)
|
||||||
|
self.inverse.setdefault(value, set()).add(key)
|
||||||
|
|
||||||
|
def __delitem__(self, key: K):
|
||||||
|
self.inverse.setdefault(self[key], set()).remove(key)
|
||||||
|
if self[key] in self.inverse and not self.inverse[self[key]]:
|
||||||
|
del self.inverse[self[key]]
|
||||||
|
super(BiDict, self).__delitem__(key)
|
||||||
|
|
||||||
|
|
||||||
class AccountConfig:
|
class AccountConfig:
|
||||||
def __init__(self, account: Dict[str, str]) -> None:
|
def __init__(self, account: Dict[str, str]) -> None:
|
||||||
|
@ -197,6 +220,23 @@ class Config:
|
||||||
"Supplied both webhook.socket and both webhook.address"
|
"Supplied both webhook.socket and both webhook.address"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
self.dm_users: BiDict[str, str] = BiDict()
|
||||||
|
for user in self._get_cfg(["dm", "users"], default=[]):
|
||||||
|
for user_id in user["user_id"]:
|
||||||
|
self.dm_users[user_id] = user["matrix_id"]
|
||||||
|
|
||||||
|
self.dm_room_title: str = self._get_cfg(["dm", "room_title"], required=False)
|
||||||
|
filter_labels: Dict[str, str] = self._get_cfg(
|
||||||
|
["dm", "filter_labels"], default={}, required=False
|
||||||
|
)
|
||||||
|
self.dm_filter_labels: Dict[str, re.Pattern[str]] = {}
|
||||||
|
for label_name, pattern in filter_labels.items():
|
||||||
|
self.dm_filter_labels[label_name] = re.compile(pattern)
|
||||||
|
|
||||||
|
self.dm_select_label: str = self._get_cfg(
|
||||||
|
["dm", "select_label"], required=False
|
||||||
|
)
|
||||||
|
|
||||||
def _get_cfg(
|
def _get_cfg(
|
||||||
self,
|
self,
|
||||||
path: List[str],
|
path: List[str],
|
||||||
|
|
|
@ -59,6 +59,12 @@ class SilenceExtendError(AlertmanagerError):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class AlertmanagerClientError(AlertmanagerError):
|
||||||
|
"""An error encountered with Alertmanager client."""
|
||||||
|
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
class AlertmanagerServerError(AlertmanagerError):
|
class AlertmanagerServerError(AlertmanagerError):
|
||||||
"""An error encountered with Alertmanager server."""
|
"""An error encountered with Alertmanager server."""
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@ def main() -> None:
|
||||||
webhook_server = Webhook(matrix_client_pool, alertmanager_client, cache, config)
|
webhook_server = Webhook(matrix_client_pool, alertmanager_client, cache, config)
|
||||||
|
|
||||||
loop = asyncio.get_event_loop()
|
loop = asyncio.get_event_loop()
|
||||||
loop.create_task(matrix_client_pool.switch_active_client())
|
loop.create_task(alertmanager_client.start())
|
||||||
loop.create_task(webhook_server.start())
|
loop.create_task(webhook_server.start())
|
||||||
for account in config.accounts:
|
for account in config.accounts:
|
||||||
loop.create_task(matrix_client_pool.start(account, config))
|
loop.create_task(matrix_client_pool.start(account, config))
|
||||||
|
|
|
@ -10,6 +10,7 @@ from typing import Dict, List, Optional, Tuple
|
||||||
|
|
||||||
from aiohttp import ClientConnectionError, ServerDisconnectedError
|
from aiohttp import ClientConnectionError, ServerDisconnectedError
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
|
from nio import RoomPreset, RoomVisibility
|
||||||
from nio.client import AsyncClient, AsyncClientConfig
|
from nio.client import AsyncClient, AsyncClientConfig
|
||||||
from nio.events import (
|
from nio.events import (
|
||||||
InviteMemberEvent,
|
InviteMemberEvent,
|
||||||
|
@ -23,8 +24,14 @@ from nio.events import (
|
||||||
RoomMessageText,
|
RoomMessageText,
|
||||||
RoomMessageUnknown,
|
RoomMessageUnknown,
|
||||||
)
|
)
|
||||||
from nio.exceptions import LocalProtocolError
|
from nio.exceptions import LocalProtocolError, LocalTransportError
|
||||||
from nio.responses import LoginError, WhoamiError
|
from nio.responses import (
|
||||||
|
JoinedMembersError,
|
||||||
|
LoginError,
|
||||||
|
ProfileGetDisplayNameError,
|
||||||
|
RoomCreateError,
|
||||||
|
WhoamiError,
|
||||||
|
)
|
||||||
|
|
||||||
import matrix_alertbot.callback
|
import matrix_alertbot.callback
|
||||||
from matrix_alertbot.alertmanager import AlertmanagerClient
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
||||||
|
@ -51,6 +58,17 @@ class MatrixClientPool:
|
||||||
self.account = next(iter(self._accounts))
|
self.account = next(iter(self._accounts))
|
||||||
self.matrix_client = self._matrix_clients[self.account]
|
self.matrix_client = self._matrix_clients[self.account]
|
||||||
|
|
||||||
|
self.dm_rooms = {}
|
||||||
|
|
||||||
|
def unactive_user_ids(self):
|
||||||
|
active_user_id = self.account.id
|
||||||
|
user_ids = []
|
||||||
|
for account in self._accounts:
|
||||||
|
user_id = account.id
|
||||||
|
if active_user_id is not user_id:
|
||||||
|
user_ids.append(user_id)
|
||||||
|
return user_ids
|
||||||
|
|
||||||
async def switch_active_client(
|
async def switch_active_client(
|
||||||
self,
|
self,
|
||||||
) -> Optional[Tuple[AsyncClient, AccountConfig]]:
|
) -> Optional[Tuple[AsyncClient, AccountConfig]]:
|
||||||
|
@ -59,6 +77,9 @@ class MatrixClientPool:
|
||||||
if account is self.account:
|
if account is self.account:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Bot {account.id} | Checking if matrix client is connected"
|
||||||
|
)
|
||||||
matrix_client = self._matrix_clients[account]
|
matrix_client = self._matrix_clients[account]
|
||||||
try:
|
try:
|
||||||
whoami = await matrix_client.whoami()
|
whoami = await matrix_client.whoami()
|
||||||
|
@ -161,6 +182,113 @@ class MatrixClientPool:
|
||||||
|
|
||||||
return matrix_client
|
return matrix_client
|
||||||
|
|
||||||
|
async def find_existing_dm_rooms(
|
||||||
|
self, account: AccountConfig, matrix_client: AsyncClient, config: Config
|
||||||
|
) -> Dict[str, str]:
|
||||||
|
unactive_user_ids = self.unactive_user_ids()
|
||||||
|
dm_rooms = {}
|
||||||
|
|
||||||
|
for room_id in matrix_client.rooms:
|
||||||
|
if room_id in config.allowed_rooms:
|
||||||
|
continue
|
||||||
|
|
||||||
|
room_members_response = await matrix_client.joined_members(room_id)
|
||||||
|
if isinstance(room_members_response, JoinedMembersError):
|
||||||
|
logger.warning(
|
||||||
|
f"Bot {account.id} | Cannot get joined members for room {room_id}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
room_members = []
|
||||||
|
for room_member in room_members_response.members:
|
||||||
|
room_members.append(room_member.user_id)
|
||||||
|
logger.info(
|
||||||
|
f"Bot {account.id} | Found {len(room_members)} room members in {room_id}"
|
||||||
|
)
|
||||||
|
|
||||||
|
all_accounts_in_room = True
|
||||||
|
for user_id in unactive_user_ids:
|
||||||
|
if user_id not in room_members:
|
||||||
|
all_accounts_in_room = False
|
||||||
|
if not all_accounts_in_room:
|
||||||
|
continue
|
||||||
|
logger.info(f"Bot {account.id} | All matrix clients are in {room_id}")
|
||||||
|
|
||||||
|
for room_member in room_members:
|
||||||
|
if room_member not in config.dm_users.inverse:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if room_member in dm_rooms:
|
||||||
|
logger.warning(
|
||||||
|
f"Bot {account.id} | Found more than one direct room with user {room_member}: {room_id}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
dm_rooms[room_member] = room_id
|
||||||
|
logger.info(
|
||||||
|
f"Bot {account.id} | Found direct room {room_id} with user {room_member}"
|
||||||
|
)
|
||||||
|
|
||||||
|
return dm_rooms
|
||||||
|
|
||||||
|
async def create_dm_rooms(
|
||||||
|
self, account: AccountConfig, matrix_client: AsyncClient, config: Config
|
||||||
|
) -> None:
|
||||||
|
async with self._lock:
|
||||||
|
if matrix_client is self.matrix_client:
|
||||||
|
unactive_accounts = self.unactive_user_ids()
|
||||||
|
|
||||||
|
self.dm_rooms = await self.find_existing_dm_rooms(
|
||||||
|
account=account, matrix_client=matrix_client, config=config
|
||||||
|
)
|
||||||
|
for user_id in config.dm_users.inverse:
|
||||||
|
if user_id in self.dm_rooms:
|
||||||
|
continue
|
||||||
|
|
||||||
|
display_name_response = await matrix_client.get_displayname(user_id)
|
||||||
|
if isinstance(display_name_response, ProfileGetDisplayNameError):
|
||||||
|
error = display_name_response.message
|
||||||
|
logger.warning(
|
||||||
|
f"Bot {account.id} | Cannot fetch user name for {user_id}: {error}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
user_name = display_name_response.displayname
|
||||||
|
|
||||||
|
if config.dm_room_title:
|
||||||
|
room_title = config.dm_room_title.format(user=user_name)
|
||||||
|
else:
|
||||||
|
room_title = None
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Bot {account.id} | Creating direct room with user {user_id}"
|
||||||
|
)
|
||||||
|
invitations = unactive_accounts + [user_id]
|
||||||
|
create_room_response = await matrix_client.room_create(
|
||||||
|
visibility=RoomVisibility.private,
|
||||||
|
name=room_title,
|
||||||
|
invite=invitations,
|
||||||
|
is_direct=True,
|
||||||
|
preset=RoomPreset.private_chat,
|
||||||
|
)
|
||||||
|
if isinstance(create_room_response, RoomCreateError):
|
||||||
|
error = create_room_response.message
|
||||||
|
logger.warning(
|
||||||
|
f"Bot {account.id} | Cannot create direct room with user {user_id}: {error}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
dm_room_id = create_room_response.room_id
|
||||||
|
if dm_room_id is None:
|
||||||
|
logger.warning(
|
||||||
|
f"Bot {account.id} | Cannot find direct room id with user {user_id}"
|
||||||
|
)
|
||||||
|
continue
|
||||||
|
|
||||||
|
logger.info(
|
||||||
|
f"Bot {account.id} | Created direct room {dm_room_id} with user {user_id}"
|
||||||
|
)
|
||||||
|
self.dm_rooms[user_id] = dm_room_id
|
||||||
|
|
||||||
async def start(
|
async def start(
|
||||||
self,
|
self,
|
||||||
account: AccountConfig,
|
account: AccountConfig,
|
||||||
|
@ -196,14 +324,14 @@ class MatrixClientPool:
|
||||||
f"Bot {account.id} | Failed to login: {login_response.message}"
|
f"Bot {account.id} | Failed to login: {login_response.message}"
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
except LocalProtocolError as e:
|
except LocalProtocolError as error:
|
||||||
# There's an edge case here where the user hasn't installed the correct C
|
# There's an edge case here where the user hasn't installed the correct C
|
||||||
# dependencies. In that case, a LocalProtocolError is raised on login.
|
# dependencies. In that case, a LocalProtocolError is raised on login.
|
||||||
logger.fatal(
|
logger.fatal(
|
||||||
f"Bot {account.id} | Failed to login. Have you installed the correct dependencies? "
|
f"Bot {account.id} | Failed to login. Have you installed the correct dependencies? "
|
||||||
"https://github.com/poljar/matrix-nio#installation "
|
"https://github.com/poljar/matrix-nio#installation "
|
||||||
"Error: %s",
|
"Error: %s",
|
||||||
e,
|
error,
|
||||||
)
|
)
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
@ -233,8 +361,19 @@ class MatrixClientPool:
|
||||||
|
|
||||||
logger.info(f"Bot {account.id} | Logged in.")
|
logger.info(f"Bot {account.id} | Logged in.")
|
||||||
|
|
||||||
|
await matrix_client.sync(timeout=30000, full_state=True)
|
||||||
|
|
||||||
|
await self.create_dm_rooms(
|
||||||
|
account=account, matrix_client=matrix_client, config=config
|
||||||
|
)
|
||||||
|
|
||||||
await matrix_client.sync_forever(timeout=30000, full_state=True)
|
await matrix_client.sync_forever(timeout=30000, full_state=True)
|
||||||
except (ClientConnectionError, ServerDisconnectedError, TimeoutError):
|
except (
|
||||||
|
ClientConnectionError,
|
||||||
|
LocalTransportError,
|
||||||
|
ServerDisconnectedError,
|
||||||
|
TimeoutError,
|
||||||
|
):
|
||||||
await matrix_client.close()
|
await matrix_client.close()
|
||||||
|
|
||||||
logger.warning(
|
logger.warning(
|
||||||
|
|
|
@ -103,6 +103,7 @@ async def create_alerts(request: web_request.Request) -> web.Response:
|
||||||
except KeyError as e:
|
except KeyError as e:
|
||||||
logger.error(f"Cannot parse alert dict: {e}")
|
logger.error(f"Cannot parse alert dict: {e}")
|
||||||
return web.Response(status=400, body=f"Invalid alert: {alert_dict}.")
|
return web.Response(status=400, body=f"Invalid alert: {alert_dict}.")
|
||||||
|
|
||||||
alerts.append(alert)
|
alerts.append(alert)
|
||||||
|
|
||||||
for alert in alerts:
|
for alert in alerts:
|
||||||
|
@ -153,6 +154,21 @@ async def create_alert(
|
||||||
cache: Cache = request.app["cache"]
|
cache: Cache = request.app["cache"]
|
||||||
config: Config = request.app["config"]
|
config: Config = request.app["config"]
|
||||||
|
|
||||||
|
if config.dm_select_label and config.dm_select_label in alert.labels:
|
||||||
|
if alert.match_all_labels(config.dm_filter_labels):
|
||||||
|
dm_select_value = alert.labels[config.dm_select_label]
|
||||||
|
if dm_select_value not in config.dm_users:
|
||||||
|
logger.warning(
|
||||||
|
f"Cannot find user with label {config.dm_select_label}={dm_select_value}"
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
user_id = config.dm_users[dm_select_value]
|
||||||
|
if user_id not in matrix_client_pool.dm_rooms:
|
||||||
|
logger.warning(f"Cannot find a matrix room for user {user_id}")
|
||||||
|
return
|
||||||
|
room_id = matrix_client_pool.dm_rooms[user_id]
|
||||||
|
|
||||||
if alert.firing:
|
if alert.firing:
|
||||||
try:
|
try:
|
||||||
silence_id = await alertmanager_client.update_silence(alert.fingerprint)
|
silence_id = await alertmanager_client.update_silence(alert.fingerprint)
|
||||||
|
|
|
@ -71,6 +71,19 @@ alertmanager:
|
||||||
# Url to Alertmanager server
|
# Url to Alertmanager server
|
||||||
url: http://localhost:9093
|
url: http://localhost:9093
|
||||||
|
|
||||||
|
dm:
|
||||||
|
filter_labels:
|
||||||
|
matrix: dm
|
||||||
|
select_label: uuid
|
||||||
|
room_title: Alerts for {user}
|
||||||
|
users:
|
||||||
|
- matrix_id: "@some_other_user1:example.com"
|
||||||
|
user_id:
|
||||||
|
- a7b37c33-574c-45ac-bb07-a3b314c2da54
|
||||||
|
- matrix_id: "@some_other_user2:example.com"
|
||||||
|
user_id:
|
||||||
|
- cfb32a1d-737a-4618-8ee9-09b254d98fee
|
||||||
|
- 27e73f9b-b40a-4d84-b5b5-225931f6c289
|
||||||
cache:
|
cache:
|
||||||
# The path to a directory for caching alerts and silences
|
# The path to a directory for caching alerts and silences
|
||||||
path: "data/cache"
|
path: "data/cache"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
|
|
||||||
|
@ -43,6 +44,39 @@ class AlertTestCase(unittest.TestCase):
|
||||||
self.assertEqual("resolved", alert.status)
|
self.assertEqual("resolved", alert.status)
|
||||||
self.assertFalse(alert.firing)
|
self.assertFalse(alert.firing)
|
||||||
|
|
||||||
|
def test_match_label(self) -> None:
|
||||||
|
alert = Alert.from_dict(self.alert_dict)
|
||||||
|
|
||||||
|
pattern = re.compile(r"^alert\d+$", re.I)
|
||||||
|
self.assertTrue(alert.match_label("alertname", pattern))
|
||||||
|
|
||||||
|
pattern = re.compile("alert2")
|
||||||
|
self.assertFalse(alert.match_label("alertname", pattern))
|
||||||
|
|
||||||
|
pattern = re.compile(r"^.*$", re.I)
|
||||||
|
self.assertFalse(alert.match_label("inexistent_label", pattern))
|
||||||
|
|
||||||
|
def test_match_all_labels(self) -> None:
|
||||||
|
alert = Alert.from_dict(self.alert_dict)
|
||||||
|
|
||||||
|
patterns = {
|
||||||
|
"alertname": re.compile(r"^alert\d+$", re.I),
|
||||||
|
"job": re.compile(r"^job\d+$", re.I),
|
||||||
|
}
|
||||||
|
self.assertTrue(alert.match_all_labels(patterns))
|
||||||
|
|
||||||
|
patterns = {
|
||||||
|
"alertname": re.compile(r"^alert\d+$", re.I),
|
||||||
|
"job": re.compile("job2"),
|
||||||
|
}
|
||||||
|
self.assertFalse(alert.match_all_labels(patterns))
|
||||||
|
|
||||||
|
patterns = {
|
||||||
|
"alertname": re.compile(r"^alert\d+$", re.I),
|
||||||
|
"inexistent_label": re.compile(r"^.*$", re.I),
|
||||||
|
}
|
||||||
|
self.assertFalse(alert.match_all_labels(patterns))
|
||||||
|
|
||||||
|
|
||||||
class AlertRendererTestCase(unittest.TestCase):
|
class AlertRendererTestCase(unittest.TestCase):
|
||||||
def setUp(self) -> None:
|
def setUp(self) -> None:
|
||||||
|
|
|
@ -194,7 +194,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
alerts = await alertmanager_client.get_alerts()
|
alerts = await alertmanager_client.get_alerts()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -224,7 +224,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
alerts = await alertmanager_client.get_alerts()
|
alerts = await alertmanager_client.get_alerts()
|
||||||
|
|
||||||
self.assertEqual([], alerts)
|
self.assertEqual([], alerts)
|
||||||
|
@ -237,7 +237,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager_client.get_alerts()
|
await alertmanager_client.get_alerts()
|
||||||
|
|
||||||
|
@ -249,7 +249,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silences = await alertmanager_client.get_silences()
|
silences = await alertmanager_client.get_silences()
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -278,7 +278,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silences = await alertmanager_client.get_silences()
|
silences = await alertmanager_client.get_silences()
|
||||||
|
|
||||||
self.assertEqual([], silences)
|
self.assertEqual([], silences)
|
||||||
|
@ -291,7 +291,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager_client.get_silences()
|
await alertmanager_client.get_silences()
|
||||||
|
|
||||||
|
@ -303,7 +303,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
alert = await alertmanager_client.get_alert("fingerprint1")
|
alert = await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
self.assertEqual(
|
self.assertEqual(
|
||||||
|
@ -323,7 +323,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertNotFoundError):
|
with self.assertRaises(AlertNotFoundError):
|
||||||
await alertmanager_client.get_alert("fingerprint1")
|
await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
|
@ -335,7 +335,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager_client.get_alert("fingerprint1")
|
await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
|
@ -347,7 +347,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence1 = await alertmanager_client.get_silence("silence1")
|
silence1 = await alertmanager_client.get_silence("silence1")
|
||||||
silence2 = await alertmanager_client.get_silence("silence2")
|
silence2 = await alertmanager_client.get_silence("silence2")
|
||||||
|
|
||||||
|
@ -378,7 +378,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(SilenceNotFoundError):
|
with self.assertRaises(SilenceNotFoundError):
|
||||||
await alertmanager_client.get_silence("silence1")
|
await alertmanager_client.get_silence("silence1")
|
||||||
|
|
||||||
|
@ -390,7 +390,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
await alertmanager_client.get_silence("silence1")
|
await alertmanager_client.get_silence("silence1")
|
||||||
|
|
||||||
|
@ -403,7 +403,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id = await alertmanager_client.create_silence(
|
silence_id = await alertmanager_client.create_silence(
|
||||||
"fingerprint1", "user", 86400
|
"fingerprint1", "user", 86400
|
||||||
)
|
)
|
||||||
|
@ -440,7 +440,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.create_silence("fingerprint1", "user", 86400)
|
await alertmanager_client.create_silence("fingerprint1", "user", 86400)
|
||||||
with self.assertRaises(SilenceExtendError):
|
with self.assertRaises(SilenceExtendError):
|
||||||
await alertmanager_client.update_silence("fingerprint1")
|
await alertmanager_client.update_silence("fingerprint1")
|
||||||
|
@ -457,7 +457,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id1 = await alertmanager_client.create_silence(
|
silence_id1 = await alertmanager_client.create_silence(
|
||||||
"fingerprint1", "user", 86400
|
"fingerprint1", "user", 86400
|
||||||
)
|
)
|
||||||
|
@ -498,7 +498,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.create_silence("fingerprint1", "user1", 86400)
|
await alertmanager_client.create_silence("fingerprint1", "user1", 86400)
|
||||||
silence_id2 = await alertmanager_client.update_silence(
|
silence_id2 = await alertmanager_client.update_silence(
|
||||||
"fingerprint1", "user2", 864000
|
"fingerprint1", "user2", 864000
|
||||||
|
@ -543,7 +543,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_cache = Mock(spec=Cache)
|
fake_cache = Mock(spec=Cache)
|
||||||
|
|
||||||
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
|
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id1 = await alertmanager_client.create_or_update_silence(
|
silence_id1 = await alertmanager_client.create_or_update_silence(
|
||||||
"fingerprint1", "user", 86400
|
"fingerprint1", "user", 86400
|
||||||
)
|
)
|
||||||
|
@ -563,7 +563,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_update_silence.return_value = "silence1"
|
fake_update_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 alertmanager_client:
|
||||||
silence_id1 = await alertmanager_client.create_or_update_silence(
|
silence_id1 = await alertmanager_client.create_or_update_silence(
|
||||||
"fingerprint1", "user", 86400
|
"fingerprint1", "user", 86400
|
||||||
)
|
)
|
||||||
|
@ -584,7 +584,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id = await alertmanager_client.create_silence(
|
silence_id = await alertmanager_client.create_silence(
|
||||||
"fingerprint1", "user"
|
"fingerprint1", "user"
|
||||||
)
|
)
|
||||||
|
@ -621,7 +621,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence1_id = await alertmanager_client.create_silence(
|
silence1_id = await alertmanager_client.create_silence(
|
||||||
"fingerprint1", "user"
|
"fingerprint1", "user"
|
||||||
)
|
)
|
||||||
|
@ -667,7 +667,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_cache = Mock(spec=Cache)
|
fake_cache = Mock(spec=Cache)
|
||||||
|
|
||||||
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
|
alertmanager_client = AlertmanagerClient("http://localhost", fake_cache)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id1 = await alertmanager_client.create_or_update_silence(
|
silence_id1 = await alertmanager_client.create_or_update_silence(
|
||||||
"fingerprint1", "user"
|
"fingerprint1", "user"
|
||||||
)
|
)
|
||||||
|
@ -687,7 +687,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_update_silence.return_value = "silence1"
|
fake_update_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 alertmanager_client:
|
||||||
silence_id1 = await alertmanager_client.create_or_update_silence(
|
silence_id1 = await alertmanager_client.create_or_update_silence(
|
||||||
"fingerprint1", "user"
|
"fingerprint1", "user"
|
||||||
)
|
)
|
||||||
|
@ -707,7 +707,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
silence_id = await alertmanager_client.create_silence(
|
silence_id = await alertmanager_client.create_silence(
|
||||||
"fingerprint1", "user", int(timedelta.max.total_seconds()) + 1
|
"fingerprint1", "user", int(timedelta.max.total_seconds()) + 1
|
||||||
)
|
)
|
||||||
|
@ -743,7 +743,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(AlertNotFoundError):
|
with self.assertRaises(AlertNotFoundError):
|
||||||
await alertmanager_client.create_silence("fingerprint1", "user")
|
await alertmanager_client.create_silence("fingerprint1", "user")
|
||||||
|
|
||||||
|
@ -756,7 +756,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.get_alert("fingerprint1")
|
await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
|
@ -770,7 +770,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(SilenceNotFoundError):
|
with self.assertRaises(SilenceNotFoundError):
|
||||||
await alertmanager_client.update_silence("fingerprint1")
|
await alertmanager_client.update_silence("fingerprint1")
|
||||||
with self.assertRaises(SilenceNotFoundError):
|
with self.assertRaises(SilenceNotFoundError):
|
||||||
|
@ -784,7 +784,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(SilenceExtendError):
|
with self.assertRaises(SilenceExtendError):
|
||||||
await alertmanager_client.update_silence("fingerprint1")
|
await alertmanager_client.update_silence("fingerprint1")
|
||||||
|
|
||||||
|
@ -796,7 +796,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.get_alert("fingerprint1")
|
await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
|
@ -810,7 +810,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.delete_silence("silence1")
|
await alertmanager_client.delete_silence("silence1")
|
||||||
silences = await alertmanager_client.get_silences()
|
silences = await alertmanager_client.get_silences()
|
||||||
|
|
||||||
|
@ -834,7 +834,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
with self.assertRaises(SilenceExpiredError):
|
with self.assertRaises(SilenceExpiredError):
|
||||||
await alertmanager_client.delete_silence("silence2")
|
await alertmanager_client.delete_silence("silence2")
|
||||||
silences = await alertmanager_client.get_silences()
|
silences = await alertmanager_client.get_silences()
|
||||||
|
@ -849,7 +849,7 @@ class AlertmanagerClientTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
alertmanager_client = AlertmanagerClient(
|
alertmanager_client = AlertmanagerClient(
|
||||||
f"http://localhost:{port}", fake_cache
|
f"http://localhost:{port}", fake_cache
|
||||||
)
|
)
|
||||||
async with aiotools.closing_async(alertmanager_client):
|
async with alertmanager_client:
|
||||||
await alertmanager_client.get_alert("fingerprint1")
|
await alertmanager_client.get_alert("fingerprint1")
|
||||||
|
|
||||||
with self.assertRaises(AlertmanagerServerError):
|
with self.assertRaises(AlertmanagerServerError):
|
||||||
|
|
|
@ -12,14 +12,16 @@ import matrix_alertbot.alertmanager
|
||||||
import matrix_alertbot.callback
|
import matrix_alertbot.callback
|
||||||
import matrix_alertbot.command
|
import matrix_alertbot.command
|
||||||
import matrix_alertbot.matrix
|
import matrix_alertbot.matrix
|
||||||
|
from matrix_alertbot.config import BiDict
|
||||||
|
|
||||||
|
|
||||||
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
|
||||||
self.fake_matrix_client = Mock(spec=nio.AsyncClient)
|
self.fake_matrix_client1 = Mock(spec=nio.AsyncClient)
|
||||||
self.fake_matrix_client.user_id = "@fake_user:example.com"
|
self.fake_matrix_client1.user_id = "@fake_user1:example.com"
|
||||||
# self.fake_matrix_client.user = "@fake_user"
|
self.fake_matrix_client2 = Mock(spec=nio.AsyncClient)
|
||||||
|
self.fake_matrix_client2.user_id = "@fake_user2:example.com"
|
||||||
|
|
||||||
self.fake_cache = MagicMock(spec=Cache)
|
self.fake_cache = MagicMock(spec=Cache)
|
||||||
self.fake_alertmanager_client = Mock(
|
self.fake_alertmanager_client = Mock(
|
||||||
|
@ -36,15 +38,21 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
self.fake_config.allowed_rooms = [self.fake_room.room_id]
|
self.fake_config.allowed_rooms = [self.fake_room.room_id]
|
||||||
self.fake_config.allowed_reactions = ["🤫", "🤗"]
|
self.fake_config.allowed_reactions = ["🤫", "🤗"]
|
||||||
self.fake_config.insult_reactions = ["🤗"]
|
self.fake_config.insult_reactions = ["🤗"]
|
||||||
self.fake_config.user_ids = [self.fake_matrix_client.user_id]
|
self.fake_config.user_ids = [
|
||||||
|
self.fake_matrix_client1.user_id,
|
||||||
|
self.fake_matrix_client2.user_id,
|
||||||
|
]
|
||||||
|
self.fake_config.dm_users = BiDict(
|
||||||
|
{"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@fake_dm_user:example.com"}
|
||||||
|
)
|
||||||
|
|
||||||
self.fake_matrix_client_pool = Mock(
|
self.fake_matrix_client_pool = Mock(
|
||||||
spec=matrix_alertbot.matrix.MatrixClientPool
|
spec=matrix_alertbot.matrix.MatrixClientPool
|
||||||
)
|
)
|
||||||
self.fake_matrix_client_pool.matrix_client = self.fake_matrix_client
|
self.fake_matrix_client_pool.matrix_client = self.fake_matrix_client1
|
||||||
|
|
||||||
self.callbacks = matrix_alertbot.callback.Callbacks(
|
self.callbacks = matrix_alertbot.callback.Callbacks(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -61,7 +69,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
||||||
|
|
||||||
# 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_client1.join.assert_called_once_with(self.fake_room.room_id)
|
||||||
|
|
||||||
async def test_invite_in_unauthorized_room(self) -> None:
|
async def test_invite_in_unauthorized_room(self) -> None:
|
||||||
"""Tests the callback for InviteMemberEvents"""
|
"""Tests the callback for InviteMemberEvents"""
|
||||||
|
@ -75,7 +83,39 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
||||||
|
|
||||||
# Check that we attempted to join the room
|
# Check that we attempted to join the room
|
||||||
self.fake_matrix_client.join.assert_not_called()
|
self.fake_matrix_client1.join.assert_not_called()
|
||||||
|
|
||||||
|
async def test_invite_from_dm_user(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 = "@fake_dm_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_client1.join.assert_called_once_with(
|
||||||
|
"!unauthorizedroom@example.com"
|
||||||
|
)
|
||||||
|
|
||||||
|
async def test_invite_from_other_matrix_client(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 = self.fake_matrix_client2.user_id
|
||||||
|
|
||||||
|
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_client1.join.assert_called_once_with(
|
||||||
|
"!unauthorizedroom@example.com"
|
||||||
|
)
|
||||||
|
|
||||||
async def test_invite_raise_join_error(self) -> None:
|
async def test_invite_raise_join_error(self) -> None:
|
||||||
"""Tests the callback for InviteMemberEvents"""
|
"""Tests the callback for InviteMemberEvents"""
|
||||||
|
@ -85,13 +125,13 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_join_error = Mock(spec=nio.JoinError)
|
fake_join_error = Mock(spec=nio.JoinError)
|
||||||
fake_join_error.message = "error message"
|
fake_join_error.message = "error message"
|
||||||
self.fake_matrix_client.join.return_value = fake_join_error
|
self.fake_matrix_client1.join.return_value = fake_join_error
|
||||||
|
|
||||||
# Pretend that we received an invite event
|
# Pretend that we received an invite event
|
||||||
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
await self.callbacks.invite(self.fake_room, fake_invite_event)
|
||||||
|
|
||||||
# Check that we attempted to join the room
|
# Check that we attempted to join the room
|
||||||
self.fake_matrix_client.join.assert_has_calls(
|
self.fake_matrix_client1.join.assert_has_calls(
|
||||||
[
|
[
|
||||||
call("!abcdefg:example.com"),
|
call("!abcdefg:example.com"),
|
||||||
call("!abcdefg:example.com"),
|
call("!abcdefg:example.com"),
|
||||||
|
@ -121,7 +161,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user help"
|
fake_message_event.body = "@fake_user1 help"
|
||||||
fake_message_event.source = {"content": {}}
|
fake_message_event.source = {"content": {}}
|
||||||
|
|
||||||
self.fake_matrix_client_pool.matrix_client = None
|
self.fake_matrix_client_pool.matrix_client = None
|
||||||
|
@ -141,7 +181,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user help"
|
fake_message_event.body = "@fake_user1 help"
|
||||||
fake_message_event.source = {"content": {}}
|
fake_message_event.source = {"content": {}}
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
|
@ -149,7 +189,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_with(
|
fake_command.assert_called_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -168,7 +208,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user help"
|
fake_message_event.body = "@fake_user1 help"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -180,7 +220,42 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
|
self.fake_cache,
|
||||||
|
self.fake_alertmanager_client,
|
||||||
|
self.fake_config,
|
||||||
|
self.fake_room,
|
||||||
|
fake_message_event.sender,
|
||||||
|
fake_message_event.event_id,
|
||||||
|
(),
|
||||||
|
)
|
||||||
|
fake_command.return_value.process.assert_called_once()
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.command, "HelpCommand", autospec=True)
|
||||||
|
async def test_message_help_in_reply_with_mention_sent_by_dm_user(
|
||||||
|
self, fake_command: Mock
|
||||||
|
) -> None:
|
||||||
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
# 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 = "@fake_dm_user:example.com"
|
||||||
|
fake_message_event.body = "@fake_user1 help"
|
||||||
|
fake_message_event.source = {
|
||||||
|
"content": {
|
||||||
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.fake_room.room_id = "!unauthorizedroom@example.com"
|
||||||
|
|
||||||
|
# Pretend that we received a text message event
|
||||||
|
await self.callbacks.message(self.fake_room, fake_message_event)
|
||||||
|
|
||||||
|
# Check that we attempted to execute the command
|
||||||
|
fake_command.assert_called_once_with(
|
||||||
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -197,7 +272,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
|
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.sender = self.fake_matrix_client.user_id
|
fake_message_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.message(self.fake_room, fake_message_event)
|
await self.callbacks.message(self.fake_room, fake_message_event)
|
||||||
|
@ -232,7 +307,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user ack"
|
fake_message_event.body = "@fake_user1 ack"
|
||||||
fake_message_event.source = {"content": {}}
|
fake_message_event.source = {"content": {}}
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
|
@ -250,7 +325,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user:example.com ack"
|
fake_message_event.body = "@fake_user1:example.com ack"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -262,7 +337,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -283,7 +358,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "fake_user ack"
|
fake_message_event.body = "fake_user1 ack"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -295,7 +370,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -316,7 +391,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user:example.com @fake_user:example.com: ack"
|
fake_message_event.body = "@fake_user1:example.com @fake_user2:example.com: ack"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -328,7 +403,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -349,7 +424,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user unack"
|
fake_message_event.body = "@fake_user1 unack"
|
||||||
fake_message_event.source = {"content": {}}
|
fake_message_event.source = {"content": {}}
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
|
@ -367,7 +442,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user unack"
|
fake_message_event.body = "@fake_user1 unack"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -379,7 +454,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -401,7 +476,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_message_event = Mock(spec=nio.RoomMessageText)
|
fake_message_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_message_event.event_id = "some event id"
|
fake_message_event.event_id = "some event id"
|
||||||
fake_message_event.sender = "@some_other_fake_user:example.com"
|
fake_message_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_message_event.body = "@fake_user ack"
|
fake_message_event.body = "@fake_user1 ack"
|
||||||
fake_message_event.source = {
|
fake_message_event.source = {
|
||||||
"content": {
|
"content": {
|
||||||
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
"m.relates_to": {"m.in_reply_to": {"event_id": "some alert event id"}}
|
||||||
|
@ -417,7 +492,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that the command was not executed
|
# Check that the command was not executed
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -437,7 +512,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_alert_event.event_id = "some alert event id"
|
fake_alert_event.event_id = "some alert event id"
|
||||||
fake_alert_event.sender = self.fake_matrix_client.user_id
|
fake_alert_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
fake_reaction_event.event_id = "some event id"
|
fake_reaction_event.event_id = "some event id"
|
||||||
|
@ -447,7 +522,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
||||||
fake_event_response.event = fake_alert_event
|
fake_event_response.event = fake_alert_event
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
self.fake_matrix_client_pool.matrix_client = None
|
self.fake_matrix_client_pool.matrix_client = None
|
||||||
|
|
||||||
|
@ -466,7 +541,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_alert_event.event_id = "some alert event id"
|
fake_alert_event.event_id = "some alert event id"
|
||||||
fake_alert_event.sender = self.fake_matrix_client.user_id
|
fake_alert_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
fake_reaction_event.event_id = "some event id"
|
fake_reaction_event.event_id = "some event id"
|
||||||
|
@ -476,14 +551,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
||||||
fake_event_response.event = fake_alert_event
|
fake_event_response.event = fake_alert_event
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -493,7 +568,51 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
"some alert event id",
|
"some alert event id",
|
||||||
)
|
)
|
||||||
fake_command.return_value.process.assert_called_once()
|
fake_command.return_value.process.assert_called_once()
|
||||||
self.fake_matrix_client.room_get_event.assert_called_once_with(
|
self.fake_matrix_client1.room_get_event.assert_called_once_with(
|
||||||
|
self.fake_room.room_id, fake_alert_event.event_id
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_angry_user_command.assert_not_called()
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.callback, "AngryUserCommand", autospec=True)
|
||||||
|
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
|
||||||
|
async def test_reaction_from_dm_user(
|
||||||
|
self, fake_command: Mock, fake_angry_user_command
|
||||||
|
) -> None:
|
||||||
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
# 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_client1.user_id
|
||||||
|
|
||||||
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
|
fake_reaction_event.event_id = "some event id"
|
||||||
|
fake_reaction_event.sender = "@fake_dm_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_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
|
self.fake_room.room_id = "!unauthorizedroom@example.com"
|
||||||
|
|
||||||
|
# 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_client1,
|
||||||
|
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_client1.room_get_event.assert_called_once_with(
|
||||||
self.fake_room.room_id, fake_alert_event.event_id
|
self.fake_room.room_id, fake_alert_event.event_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -508,7 +627,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_alert_event.event_id = "some alert event id"
|
fake_alert_event.event_id = "some alert event id"
|
||||||
fake_alert_event.sender = self.fake_matrix_client.user_id
|
fake_alert_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
fake_reaction_event.event_id = "some event id"
|
fake_reaction_event.event_id = "some event id"
|
||||||
|
@ -518,14 +637,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
||||||
fake_event_response.event = fake_alert_event
|
fake_event_response.event = fake_alert_event
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -535,12 +654,12 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
"some alert event id",
|
"some alert event id",
|
||||||
)
|
)
|
||||||
fake_command.return_value.process.assert_called_once()
|
fake_command.return_value.process.assert_called_once()
|
||||||
self.fake_matrix_client.room_get_event.assert_called_once_with(
|
self.fake_matrix_client1.room_get_event.assert_called_once_with(
|
||||||
self.fake_room.room_id, fake_alert_event.event_id
|
self.fake_room.room_id, fake_alert_event.event_id
|
||||||
)
|
)
|
||||||
|
|
||||||
fake_angry_user_command.assert_called_once_with(
|
fake_angry_user_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -564,7 +683,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_reaction_event.key = "🤫"
|
fake_reaction_event.key = "🤫"
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventError)
|
fake_event_response = Mock(spec=nio.RoomGetEventError)
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
||||||
|
@ -572,7 +691,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_not_called()
|
fake_command.assert_not_called()
|
||||||
self.fake_cache.set.assert_not_called()
|
self.fake_cache.set.assert_not_called()
|
||||||
self.fake_matrix_client.room_get_event.assert_called_once_with(
|
self.fake_matrix_client1.room_get_event.assert_called_once_with(
|
||||||
self.fake_room.room_id, fake_alert_event_id
|
self.fake_room.room_id, fake_alert_event_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -595,7 +714,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
||||||
fake_event_response.event = fake_alert_event
|
fake_event_response.event = fake_alert_event
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
await self.callbacks.reaction(self.fake_room, fake_reaction_event)
|
||||||
|
@ -603,7 +722,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_not_called()
|
fake_command.assert_not_called()
|
||||||
self.fake_cache.set.assert_not_called()
|
self.fake_cache.set.assert_not_called()
|
||||||
self.fake_matrix_client.room_get_event.assert_called_once_with(
|
self.fake_matrix_client1.room_get_event.assert_called_once_with(
|
||||||
self.fake_room.room_id, fake_alert_event.event_id
|
self.fake_room.room_id, fake_alert_event.event_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -616,7 +735,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
fake_alert_event = Mock(spec=nio.RoomMessageText)
|
||||||
fake_alert_event.event_id = "some alert event id"
|
fake_alert_event.event_id = "some alert event id"
|
||||||
fake_alert_event.sender = self.fake_matrix_client.user_id
|
fake_alert_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
fake_reaction_event.event_id = "some event id"
|
fake_reaction_event.event_id = "some event id"
|
||||||
|
@ -626,7 +745,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
fake_event_response = Mock(spec=nio.RoomGetEventResponse)
|
||||||
fake_event_response.event = fake_alert_event
|
fake_event_response.event = fake_alert_event
|
||||||
self.fake_matrix_client.room_get_event.return_value = fake_event_response
|
self.fake_matrix_client1.room_get_event.return_value = fake_event_response
|
||||||
|
|
||||||
fake_command.return_value.process.side_effect = (
|
fake_command.return_value.process.side_effect = (
|
||||||
nio.exceptions.LocalProtocolError
|
nio.exceptions.LocalProtocolError
|
||||||
|
@ -637,7 +756,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -647,7 +766,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
"some alert event id",
|
"some alert event id",
|
||||||
)
|
)
|
||||||
fake_command.return_value.process.assert_called_once()
|
fake_command.return_value.process.assert_called_once()
|
||||||
self.fake_matrix_client.room_get_event.assert_called_once_with(
|
self.fake_matrix_client1.room_get_event.assert_called_once_with(
|
||||||
self.fake_room.room_id, fake_alert_event.event_id
|
self.fake_room.room_id, fake_alert_event.event_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -671,7 +790,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_not_called()
|
fake_command.assert_not_called()
|
||||||
self.fake_matrix_client.room_get_event.assert_not_called()
|
self.fake_matrix_client1.room_get_event.assert_not_called()
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
|
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
|
||||||
async def test_ignore_reaction_sent_by_bot_user(self, fake_command: Mock) -> None:
|
async def test_ignore_reaction_sent_by_bot_user(self, fake_command: Mock) -> None:
|
||||||
|
@ -682,7 +801,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
fake_reaction_event = Mock(spec=nio.ReactionEvent)
|
||||||
fake_reaction_event.type = "m.reaction"
|
fake_reaction_event.type = "m.reaction"
|
||||||
fake_reaction_event.event_id = "some event id"
|
fake_reaction_event.event_id = "some event id"
|
||||||
fake_reaction_event.sender = self.fake_matrix_client.user_id
|
fake_reaction_event.sender = self.fake_matrix_client1.user_id
|
||||||
fake_reaction_event.reacts_to = fake_alert_event_id
|
fake_reaction_event.reacts_to = fake_alert_event_id
|
||||||
fake_reaction_event.key = "unknown"
|
fake_reaction_event.key = "unknown"
|
||||||
|
|
||||||
|
@ -691,7 +810,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_not_called()
|
fake_command.assert_not_called()
|
||||||
self.fake_matrix_client.room_get_event.assert_not_called()
|
self.fake_matrix_client1.room_get_event.assert_not_called()
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
|
@patch.object(matrix_alertbot.callback, "AckAlertCommand", autospec=True)
|
||||||
async def test_ignore_reaction_in_unauthorized_room(
|
async def test_ignore_reaction_in_unauthorized_room(
|
||||||
|
@ -715,7 +834,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_not_called()
|
fake_command.assert_not_called()
|
||||||
self.fake_matrix_client.room_get_event.assert_not_called()
|
self.fake_matrix_client1.room_get_event.assert_not_called()
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
|
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
|
||||||
async def test_redaction_client_not_in_pool(self, fake_command: Mock) -> None:
|
async def test_redaction_client_not_in_pool(self, fake_command: Mock) -> None:
|
||||||
|
@ -758,7 +877,39 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
|
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()
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.callback, "UnackAlertCommand", autospec=True)
|
||||||
|
async def test_redaction_by_dm_user(self, fake_command: Mock) -> None:
|
||||||
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
# 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 = "@fake_dm_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_room.room_id = "!unauthorizedroom@example.com"
|
||||||
|
|
||||||
|
# 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_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -795,7 +946,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_command.assert_called_once_with(
|
fake_command.assert_called_once_with(
|
||||||
self.fake_matrix_client,
|
self.fake_matrix_client1,
|
||||||
self.fake_cache,
|
self.fake_cache,
|
||||||
self.fake_alertmanager_client,
|
self.fake_alertmanager_client,
|
||||||
self.fake_config,
|
self.fake_config,
|
||||||
|
@ -813,7 +964,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
# Tests that the bot process messages in the room that contain a command
|
# Tests that the bot process messages in the room that contain a command
|
||||||
fake_redaction_event = Mock(spec=nio.RedactionEvent)
|
fake_redaction_event = Mock(spec=nio.RedactionEvent)
|
||||||
fake_redaction_event.sender = self.fake_matrix_client.user_id
|
fake_redaction_event.sender = self.fake_matrix_client1.user_id
|
||||||
|
|
||||||
fake_cache_dict: Dict = {}
|
fake_cache_dict: Dict = {}
|
||||||
self.fake_cache.__getitem__.side_effect = fake_cache_dict.__getitem__
|
self.fake_cache.__getitem__.side_effect = fake_cache_dict.__getitem__
|
||||||
|
@ -859,16 +1010,16 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_start(fake_key_verification_event)
|
await self.callbacks.key_verification_start(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.accept_key_verification.assert_called_once_with(
|
self.fake_matrix_client1.accept_key_verification.assert_called_once_with(
|
||||||
fake_transaction_id
|
fake_transaction_id
|
||||||
)
|
)
|
||||||
self.fake_matrix_client.to_device.assert_called_once_with(fake_sas.share_key())
|
self.fake_matrix_client1.to_device.assert_called_once_with(fake_sas.share_key())
|
||||||
|
|
||||||
async def test_key_verification_start_with_emoji_not_supported(self) -> None:
|
async def test_key_verification_start_with_emoji_not_supported(self) -> None:
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
@ -883,14 +1034,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_start(fake_key_verification_event)
|
await self.callbacks.key_verification_start(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.accept_key_verification.assert_not_called()
|
self.fake_matrix_client1.accept_key_verification.assert_not_called()
|
||||||
self.fake_matrix_client.to_device.assert_not_called()
|
self.fake_matrix_client1.to_device.assert_not_called()
|
||||||
|
|
||||||
async def test_key_verification_start_with_accept_key_verification_error(
|
async def test_key_verification_start_with_accept_key_verification_error(
|
||||||
self,
|
self,
|
||||||
|
@ -905,22 +1056,22 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_key_verification_event.short_authentication_string = ["emoji"]
|
fake_key_verification_event.short_authentication_string = ["emoji"]
|
||||||
fake_key_verification_event.transaction_id = fake_transaction_id
|
fake_key_verification_event.transaction_id = fake_transaction_id
|
||||||
|
|
||||||
self.fake_matrix_client.accept_key_verification.return_value = Mock(
|
self.fake_matrix_client1.accept_key_verification.return_value = Mock(
|
||||||
spec=nio.ToDeviceError
|
spec=nio.ToDeviceError
|
||||||
)
|
)
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_start(fake_key_verification_event)
|
await self.callbacks.key_verification_start(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.accept_key_verification.assert_called_once_with(
|
self.fake_matrix_client1.accept_key_verification.assert_called_once_with(
|
||||||
fake_transaction_id
|
fake_transaction_id
|
||||||
)
|
)
|
||||||
self.fake_matrix_client.to_device.assert_not_called()
|
self.fake_matrix_client1.to_device.assert_not_called()
|
||||||
|
|
||||||
async def test_key_verification_start_with_to_device_error(
|
async def test_key_verification_start_with_to_device_error(
|
||||||
self,
|
self,
|
||||||
|
@ -935,20 +1086,20 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_key_verification_event.short_authentication_string = ["emoji"]
|
fake_key_verification_event.short_authentication_string = ["emoji"]
|
||||||
fake_key_verification_event.transaction_id = fake_transaction_id
|
fake_key_verification_event.transaction_id = fake_transaction_id
|
||||||
|
|
||||||
self.fake_matrix_client.to_device.return_value = Mock(spec=nio.ToDeviceError)
|
self.fake_matrix_client1.to_device.return_value = Mock(spec=nio.ToDeviceError)
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_start(fake_key_verification_event)
|
await self.callbacks.key_verification_start(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.accept_key_verification.assert_called_once_with(
|
self.fake_matrix_client1.accept_key_verification.assert_called_once_with(
|
||||||
fake_transaction_id
|
fake_transaction_id
|
||||||
)
|
)
|
||||||
self.fake_matrix_client.to_device.assert_called_once_with(fake_sas.share_key())
|
self.fake_matrix_client1.to_device.assert_called_once_with(fake_sas.share_key())
|
||||||
|
|
||||||
async def test_key_verification_cancel(self) -> None:
|
async def test_key_verification_cancel(self) -> None:
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
@ -977,13 +1128,13 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
("emoji2", "alt text2"),
|
("emoji2", "alt text2"),
|
||||||
]
|
]
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_confirm(fake_key_verification_event)
|
await self.callbacks.key_verification_confirm(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.confirm_short_auth_string.assert_called_once_with(
|
self.fake_matrix_client1.confirm_short_auth_string.assert_called_once_with(
|
||||||
fake_transaction_id
|
fake_transaction_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -996,7 +1147,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_key_verification_event.sender = "@some_other_fake_user:example.com"
|
fake_key_verification_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_key_verification_event.transaction_id = fake_transaction_id
|
fake_key_verification_event.transaction_id = fake_transaction_id
|
||||||
|
|
||||||
self.fake_matrix_client.confirm_short_auth_string.return_value = Mock(
|
self.fake_matrix_client1.confirm_short_auth_string.return_value = Mock(
|
||||||
spec=nio.ToDeviceError
|
spec=nio.ToDeviceError
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1006,13 +1157,13 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
("emoji2", "alt text2"),
|
("emoji2", "alt text2"),
|
||||||
]
|
]
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_confirm(fake_key_verification_event)
|
await self.callbacks.key_verification_confirm(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
self.fake_matrix_client.confirm_short_auth_string.assert_called_once_with(
|
self.fake_matrix_client1.confirm_short_auth_string.assert_called_once_with(
|
||||||
fake_transaction_id
|
fake_transaction_id
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1028,14 +1179,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
fake_sas.verified_devices = ["HGFEDCBA"]
|
fake_sas.verified_devices = ["HGFEDCBA"]
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_end(fake_key_verification_event)
|
await self.callbacks.key_verification_end(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
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_client1.to_device.assert_called_once_with(fake_sas.get_mac())
|
||||||
|
|
||||||
async def test_key_verification_end_with_missing_transaction_id(self) -> None:
|
async def test_key_verification_end_with_missing_transaction_id(self) -> None:
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
@ -1048,14 +1199,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
fake_transactions_dict = {}
|
fake_transactions_dict = {}
|
||||||
self.fake_matrix_client.key_verifications = fake_transactions_dict
|
self.fake_matrix_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_end(fake_key_verification_event)
|
await self.callbacks.key_verification_end(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_sas.get_mac.assert_not_called()
|
fake_sas.get_mac.assert_not_called()
|
||||||
self.fake_matrix_client.to_device.assert_not_called()
|
self.fake_matrix_client1.to_device.assert_not_called()
|
||||||
|
|
||||||
async def test_key_verification_end_with_mac_error(self) -> None:
|
async def test_key_verification_end_with_mac_error(self) -> None:
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
@ -1069,14 +1220,14 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
fake_sas.get_mac.side_effect = nio.exceptions.LocalProtocolError
|
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_end(fake_key_verification_event)
|
await self.callbacks.key_verification_end(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
fake_sas.get_mac.assert_called_once_with()
|
fake_sas.get_mac.assert_called_once_with()
|
||||||
self.fake_matrix_client.to_device.assert_not_called()
|
self.fake_matrix_client1.to_device.assert_not_called()
|
||||||
|
|
||||||
async def test_key_verification_end_with_to_device_error(self) -> None:
|
async def test_key_verification_end_with_to_device_error(self) -> None:
|
||||||
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
"""Tests the callback for RoomMessageText with a mention of the bot"""
|
||||||
|
@ -1087,18 +1238,18 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
fake_key_verification_event.sender = "@some_other_fake_user:example.com"
|
fake_key_verification_event.sender = "@some_other_fake_user:example.com"
|
||||||
fake_key_verification_event.transaction_id = fake_transaction_id
|
fake_key_verification_event.transaction_id = fake_transaction_id
|
||||||
|
|
||||||
self.fake_matrix_client.to_device.return_value = Mock(spec=nio.ToDeviceError)
|
self.fake_matrix_client1.to_device.return_value = Mock(spec=nio.ToDeviceError)
|
||||||
|
|
||||||
fake_sas = Mock()
|
fake_sas = Mock()
|
||||||
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_client1.key_verifications = fake_transactions_dict
|
||||||
|
|
||||||
# Pretend that we received a text message event
|
# Pretend that we received a text message event
|
||||||
await self.callbacks.key_verification_end(fake_key_verification_event)
|
await self.callbacks.key_verification_end(fake_key_verification_event)
|
||||||
|
|
||||||
# Check that we attempted to execute the command
|
# Check that we attempted to execute the command
|
||||||
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_client1.to_device.assert_called_once_with(fake_sas.get_mac())
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
|
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
|
||||||
async def test_decryption_failure(self, fake_logger) -> None:
|
async def test_decryption_failure(self, fake_logger) -> None:
|
||||||
|
@ -1110,6 +1261,18 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
fake_logger.error.assert_called_once()
|
fake_logger.error.assert_called_once()
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
|
||||||
|
async def test_decryption_failure_from_dm_user(self, fake_logger) -> None:
|
||||||
|
fake_megolm_event = Mock(spec=nio.MegolmEvent)
|
||||||
|
fake_megolm_event.sender = "@fake_dm_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_called_once()
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
|
@patch.object(matrix_alertbot.callback, "logger", autospec=True)
|
||||||
async def test_decryption_failure_in_unauthorized_room(self, fake_logger) -> None:
|
async def test_decryption_failure_in_unauthorized_room(self, fake_logger) -> None:
|
||||||
fake_megolm_event = Mock(spec=nio.MegolmEvent)
|
fake_megolm_event = Mock(spec=nio.MegolmEvent)
|
||||||
|
@ -1134,7 +1297,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
||||||
|
|
||||||
self.fake_matrix_client.room_send.assert_called_once_with(
|
self.fake_matrix_client1.room_send.assert_called_once_with(
|
||||||
self.fake_room.room_id,
|
self.fake_room.room_id,
|
||||||
"m.room.message",
|
"m.room.message",
|
||||||
{
|
{
|
||||||
|
@ -1159,7 +1322,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
||||||
|
|
||||||
self.fake_matrix_client.room_send.assert_not_called()
|
self.fake_matrix_client1.room_send.assert_not_called()
|
||||||
|
|
||||||
async def test_unknown_message_with_method_not_sas_v1(self) -> None:
|
async def test_unknown_message_with_method_not_sas_v1(self) -> None:
|
||||||
fake_room_unknown_event = Mock(spec=nio.RoomMessageUnknown)
|
fake_room_unknown_event = Mock(spec=nio.RoomMessageUnknown)
|
||||||
|
@ -1173,7 +1336,7 @@ class CallbacksTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
|
|
||||||
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
await self.callbacks.unknown_message(self.fake_room, fake_room_unknown_event)
|
||||||
|
|
||||||
self.fake_matrix_client.room_send.assert_not_called()
|
self.fake_matrix_client1.room_send.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
import unittest
|
import unittest
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
|
@ -7,7 +8,7 @@ from unittest.mock import Mock, patch
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
import matrix_alertbot.config
|
import matrix_alertbot.config
|
||||||
from matrix_alertbot.config import DEFAULT_REACTIONS, Config
|
from matrix_alertbot.config import DEFAULT_REACTIONS, BiDict, Config
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import (
|
||||||
InvalidConfigError,
|
InvalidConfigError,
|
||||||
ParseConfigError,
|
ParseConfigError,
|
||||||
|
@ -36,6 +37,72 @@ def mock_path_exists(path: str) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
class BiDictTestCase(unittest.TestCase):
|
||||||
|
def test_init_bidict(self) -> None:
|
||||||
|
data = {"room1": "user1", "room2": "user1", "room3": "user2"}
|
||||||
|
bidict = BiDict(data)
|
||||||
|
self.assertDictEqual(data, bidict)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"user1": {"room1", "room2"}, "user2": {"room3"}}, bidict.inverse
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_del_item_bidict(self) -> None:
|
||||||
|
data = {"room1": "user1", "room2": "user1", "room3": "user2"}
|
||||||
|
bidict = BiDict(data)
|
||||||
|
|
||||||
|
del bidict["room1"]
|
||||||
|
self.assertDictEqual({"room2": "user1", "room3": "user2"}, bidict)
|
||||||
|
self.assertDictEqual({"user1": {"room2"}, "user2": {"room3"}}, bidict.inverse)
|
||||||
|
|
||||||
|
del bidict["room3"]
|
||||||
|
self.assertDictEqual({"room2": "user1"}, bidict)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"user1": {"room2"},
|
||||||
|
},
|
||||||
|
bidict.inverse,
|
||||||
|
)
|
||||||
|
|
||||||
|
del bidict["room2"]
|
||||||
|
self.assertDictEqual({}, bidict)
|
||||||
|
self.assertDictEqual({}, bidict.inverse)
|
||||||
|
|
||||||
|
with self.assertRaises(KeyError):
|
||||||
|
del bidict["room4"]
|
||||||
|
|
||||||
|
def test_add_item_bidict(self) -> None:
|
||||||
|
data = {"room1": "user1", "room2": "user1", "room3": "user2"}
|
||||||
|
bidict = BiDict(data)
|
||||||
|
|
||||||
|
bidict["room4"] = "user2"
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"room1": "user1", "room2": "user1", "room3": "user2", "room4": "user2"},
|
||||||
|
bidict,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"user1": {"room1", "room2"}, "user2": {"room3", "room4"}}, bidict.inverse
|
||||||
|
)
|
||||||
|
|
||||||
|
bidict["room4"] = "user1"
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"room1": "user1", "room2": "user1", "room3": "user2", "room4": "user1"},
|
||||||
|
bidict,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"user1": {"room1", "room2", "room4"}, "user2": {"room3"}}, bidict.inverse
|
||||||
|
)
|
||||||
|
|
||||||
|
bidict["room3"] = "user1"
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"room1": "user1", "room2": "user1", "room3": "user1", "room4": "user1"},
|
||||||
|
bidict,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"user1": {"room1", "room2", "room3", "room4"}, "user2": set()},
|
||||||
|
bidict.inverse,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class ConfigTestCase(unittest.TestCase):
|
class ConfigTestCase(unittest.TestCase):
|
||||||
@patch("os.path.isdir")
|
@patch("os.path.isdir")
|
||||||
@patch("os.path.exists")
|
@patch("os.path.exists")
|
||||||
|
@ -141,6 +208,32 @@ class ConfigTestCase(unittest.TestCase):
|
||||||
self.assertEqual({"🤫", "😶", "🤐", "🤗"}, config.allowed_reactions)
|
self.assertEqual({"🤫", "😶", "🤐", "🤗"}, config.allowed_reactions)
|
||||||
self.assertEqual({"🤗"}, config.insult_reactions)
|
self.assertEqual({"🤗"}, config.insult_reactions)
|
||||||
|
|
||||||
|
self.assertDictEqual({"matrix": re.compile("dm")}, config.dm_filter_labels)
|
||||||
|
self.assertEqual("uuid", config.dm_select_label)
|
||||||
|
self.assertEqual(
|
||||||
|
"Alerts for FakeUser", config.dm_room_title.format(user="FakeUser")
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@some_other_user1:example.com",
|
||||||
|
"cfb32a1d-737a-4618-8ee9-09b254d98fee": "@some_other_user2:example.com",
|
||||||
|
"27e73f9b-b40a-4d84-b5b5-225931f6c289": "@some_other_user2:example.com",
|
||||||
|
},
|
||||||
|
config.dm_users,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@some_other_user1:example.com": {
|
||||||
|
"a7b37c33-574c-45ac-bb07-a3b314c2da54"
|
||||||
|
},
|
||||||
|
"@some_other_user2:example.com": {
|
||||||
|
"cfb32a1d-737a-4618-8ee9-09b254d98fee",
|
||||||
|
"27e73f9b-b40a-4d84-b5b5-225931f6c289",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
config.dm_users.inverse,
|
||||||
|
)
|
||||||
|
|
||||||
self.assertIsNone(config.address)
|
self.assertIsNone(config.address)
|
||||||
self.assertIsNone(config.port)
|
self.assertIsNone(config.port)
|
||||||
self.assertEqual("matrix-alertbot.socket", config.socket)
|
self.assertEqual("matrix-alertbot.socket", config.socket)
|
||||||
|
|
|
@ -2,15 +2,24 @@ from __future__ import annotations
|
||||||
|
|
||||||
import random
|
import random
|
||||||
import unittest
|
import unittest
|
||||||
from unittest.mock import Mock, call, patch
|
from unittest.mock import AsyncMock, Mock, call, patch
|
||||||
|
|
||||||
import nio
|
import nio
|
||||||
from diskcache import Cache
|
from diskcache import Cache
|
||||||
|
from nio.api import RoomPreset, RoomVisibility
|
||||||
|
from nio.responses import (
|
||||||
|
JoinedMembersError,
|
||||||
|
JoinedMembersResponse,
|
||||||
|
ProfileGetDisplayNameError,
|
||||||
|
ProfileGetDisplayNameResponse,
|
||||||
|
RoomCreateError,
|
||||||
|
RoomCreateResponse,
|
||||||
|
)
|
||||||
|
|
||||||
import matrix_alertbot
|
import matrix_alertbot
|
||||||
import matrix_alertbot.matrix
|
import matrix_alertbot.matrix
|
||||||
from matrix_alertbot.alertmanager import AlertmanagerClient
|
from matrix_alertbot.alertmanager import AlertmanagerClient
|
||||||
from matrix_alertbot.config import AccountConfig, Config
|
from matrix_alertbot.config import AccountConfig, BiDict, Config
|
||||||
from matrix_alertbot.matrix import MatrixClientPool
|
from matrix_alertbot.matrix import MatrixClientPool
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +35,30 @@ def mock_create_matrix_client(
|
||||||
return fake_matrix_client
|
return fake_matrix_client
|
||||||
|
|
||||||
|
|
||||||
|
def mock_joined_members(room_id: str) -> JoinedMembersResponse | JoinedMembersError:
|
||||||
|
fake_joined_members_response = Mock(spec=JoinedMembersResponse)
|
||||||
|
if "dmroom" in room_id:
|
||||||
|
fake_joined_members_response.members = [
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@fake_dm_user:example.com"),
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@fake_user:matrix.example.com"),
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@other_user:chat.example.com"),
|
||||||
|
]
|
||||||
|
elif "!missing_other_user:example.com" == room_id:
|
||||||
|
fake_joined_members_response.members = [
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@fake_dm_user:example.com"),
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@fake_user:matrix.example.com"),
|
||||||
|
]
|
||||||
|
elif "!missing_dm_user:example.com" == room_id:
|
||||||
|
fake_joined_members_response.members = [
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@fake_user:matrix.example.com"),
|
||||||
|
Mock(spec=nio.RoomMember, user_id="@other_user:chat.example.com"),
|
||||||
|
]
|
||||||
|
else:
|
||||||
|
fake_joined_members_response = Mock(spec=JoinedMembersError)
|
||||||
|
|
||||||
|
return fake_joined_members_response
|
||||||
|
|
||||||
|
|
||||||
class FakeAsyncClientConfig:
|
class FakeAsyncClientConfig:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
|
@ -66,6 +99,14 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
self.fake_account_config_1,
|
self.fake_account_config_1,
|
||||||
self.fake_account_config_2,
|
self.fake_account_config_2,
|
||||||
]
|
]
|
||||||
|
self.fake_config.allowed_rooms = "!abcdefg:example.com"
|
||||||
|
self.fake_config.dm_users = BiDict(
|
||||||
|
{
|
||||||
|
"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@fake_dm_user:example.com",
|
||||||
|
"cfb32a1d-737a-4618-8ee9-09b254d98fee": "@other_dm_user:example.com",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
self.fake_config.dm_room_title = "Alerts for {user}"
|
||||||
|
|
||||||
@patch.object(
|
@patch.object(
|
||||||
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
|
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
|
||||||
|
@ -104,6 +145,24 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
self.assertEqual(2, len(matrix_client_pool._accounts))
|
self.assertEqual(2, len(matrix_client_pool._accounts))
|
||||||
self.assertEqual(2, len(matrix_client_pool._matrix_clients))
|
self.assertEqual(2, len(matrix_client_pool._matrix_clients))
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
|
||||||
|
)
|
||||||
|
async def test_unactive_user_ids(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,
|
||||||
|
)
|
||||||
|
|
||||||
|
unactive_user_ids = matrix_client_pool.unactive_user_ids()
|
||||||
|
|
||||||
|
self.assertEqual(self.fake_account_config_1, matrix_client_pool.account)
|
||||||
|
self.assertListEqual([self.fake_account_config_2.id], unactive_user_ids)
|
||||||
|
|
||||||
@patch.object(
|
@patch.object(
|
||||||
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
|
matrix_alertbot.matrix.MatrixClientPool, "_create_matrix_client", autospec=True
|
||||||
)
|
)
|
||||||
|
@ -296,6 +355,335 @@ class MatrixClientPoolTestCase(unittest.IsolatedAsyncioTestCase):
|
||||||
self.assertTrue(matrix_client_1.config.store_sync_tokens)
|
self.assertTrue(matrix_client_1.config.store_sync_tokens)
|
||||||
self.assertFalse(matrix_client_1.config.encryption_enabled)
|
self.assertFalse(matrix_client_1.config.encryption_enabled)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_find_existing_dm_rooms(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 = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.rooms = [
|
||||||
|
"!abcdefg:example.com",
|
||||||
|
"!fake_dmroom:example.com",
|
||||||
|
"!missing_other_user:example.com",
|
||||||
|
"!missing_dm_user:example.com",
|
||||||
|
"!error:example.com",
|
||||||
|
]
|
||||||
|
fake_matrix_client.joined_members.side_effect = mock_joined_members
|
||||||
|
|
||||||
|
dm_rooms = await matrix_client_pool.find_existing_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.joined_members.assert_has_calls(
|
||||||
|
[
|
||||||
|
call("!fake_dmroom:example.com"),
|
||||||
|
call("!missing_other_user:example.com"),
|
||||||
|
call("!missing_dm_user:example.com"),
|
||||||
|
call("!error:example.com"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"@fake_dm_user:example.com": "!fake_dmroom:example.com"}, dm_rooms
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_find_existing_dm_rooms_with_duplicates(
|
||||||
|
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 = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.rooms = [
|
||||||
|
"!abcdefg:example.com",
|
||||||
|
"!fake_dmroom:example.com",
|
||||||
|
"!other_dmroom:example.com",
|
||||||
|
]
|
||||||
|
fake_matrix_client.joined_members.side_effect = mock_joined_members
|
||||||
|
|
||||||
|
dm_rooms = await matrix_client_pool.find_existing_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.joined_members.assert_has_calls(
|
||||||
|
[
|
||||||
|
call("!fake_dmroom:example.com"),
|
||||||
|
call("!other_dmroom:example.com"),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{"@fake_dm_user:example.com": "!fake_dmroom:example.com"}, dm_rooms
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_create_dm_rooms(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_find_existing_dm_rooms = AsyncMock(autospec=True)
|
||||||
|
fake_find_existing_dm_rooms.return_value = {
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com"
|
||||||
|
}
|
||||||
|
matrix_client_pool.find_existing_dm_rooms = fake_find_existing_dm_rooms
|
||||||
|
|
||||||
|
fake_matrix_client = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.get_displayname.return_value = Mock(
|
||||||
|
spec=ProfileGetDisplayNameResponse, displayname="FakeUser"
|
||||||
|
)
|
||||||
|
fake_room_create_response = Mock(spec=RoomCreateResponse)
|
||||||
|
fake_room_create_response.room_id = "!fake_dmroom:example.com"
|
||||||
|
fake_matrix_client.room_create.return_value = fake_room_create_response
|
||||||
|
|
||||||
|
await matrix_client_pool.create_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.get_displayname.assert_called_once_with(
|
||||||
|
"@fake_dm_user:example.com"
|
||||||
|
)
|
||||||
|
fake_matrix_client.room_create.assert_called_once_with(
|
||||||
|
visibility=RoomVisibility.private,
|
||||||
|
name="Alerts for FakeUser",
|
||||||
|
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
|
||||||
|
is_direct=True,
|
||||||
|
preset=RoomPreset.private_chat,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@fake_dm_user:example.com": "!fake_dmroom:example.com",
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com",
|
||||||
|
},
|
||||||
|
matrix_client_pool.dm_rooms,
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_create_dm_rooms_with_empty_room_id(
|
||||||
|
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_find_existing_dm_rooms = AsyncMock(autospec=True)
|
||||||
|
fake_find_existing_dm_rooms.return_value = {
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com"
|
||||||
|
}
|
||||||
|
matrix_client_pool.find_existing_dm_rooms = fake_find_existing_dm_rooms
|
||||||
|
|
||||||
|
fake_matrix_client = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.get_displayname.return_value = Mock(
|
||||||
|
spec=ProfileGetDisplayNameResponse, displayname="FakeUser"
|
||||||
|
)
|
||||||
|
fake_room_create_response = Mock(spec=RoomCreateResponse)
|
||||||
|
fake_room_create_response.room_id = None
|
||||||
|
fake_matrix_client.room_create.return_value = fake_room_create_response
|
||||||
|
|
||||||
|
await matrix_client_pool.create_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.get_displayname.assert_called_once_with(
|
||||||
|
"@fake_dm_user:example.com"
|
||||||
|
)
|
||||||
|
fake_matrix_client.room_create.assert_called_once_with(
|
||||||
|
visibility=RoomVisibility.private,
|
||||||
|
name="Alerts for FakeUser",
|
||||||
|
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
|
||||||
|
is_direct=True,
|
||||||
|
preset=RoomPreset.private_chat,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com",
|
||||||
|
},
|
||||||
|
matrix_client_pool.dm_rooms,
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_create_dm_rooms_with_empty_room_title(
|
||||||
|
self, fake_create_matrix_client
|
||||||
|
) -> None:
|
||||||
|
self.fake_config.dm_room_title = None
|
||||||
|
|
||||||
|
matrix_client_pool = MatrixClientPool(
|
||||||
|
alertmanager_client=self.fake_alertmanager_client,
|
||||||
|
cache=self.fake_cache,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
fake_find_existing_dm_rooms = AsyncMock(autospec=True)
|
||||||
|
fake_find_existing_dm_rooms.return_value = {
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com"
|
||||||
|
}
|
||||||
|
matrix_client_pool.find_existing_dm_rooms = fake_find_existing_dm_rooms
|
||||||
|
|
||||||
|
fake_matrix_client = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.get_displayname.return_value = Mock(
|
||||||
|
spec=ProfileGetDisplayNameResponse, displayname="FakeUser"
|
||||||
|
)
|
||||||
|
fake_room_create_response = Mock(spec=RoomCreateResponse)
|
||||||
|
fake_room_create_response.room_id = "!fake_dmroom:example.com"
|
||||||
|
fake_matrix_client.room_create.return_value = fake_room_create_response
|
||||||
|
|
||||||
|
await matrix_client_pool.create_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.get_displayname.assert_called_once_with(
|
||||||
|
"@fake_dm_user:example.com"
|
||||||
|
)
|
||||||
|
fake_matrix_client.room_create.assert_called_once_with(
|
||||||
|
visibility=RoomVisibility.private,
|
||||||
|
name=None,
|
||||||
|
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
|
||||||
|
is_direct=True,
|
||||||
|
preset=RoomPreset.private_chat,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@fake_dm_user:example.com": "!fake_dmroom:example.com",
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com",
|
||||||
|
},
|
||||||
|
matrix_client_pool.dm_rooms,
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_create_dm_rooms_with_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,
|
||||||
|
)
|
||||||
|
fake_find_existing_dm_rooms = AsyncMock(autospec=True)
|
||||||
|
fake_find_existing_dm_rooms.return_value = {
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com"
|
||||||
|
}
|
||||||
|
matrix_client_pool.find_existing_dm_rooms = fake_find_existing_dm_rooms
|
||||||
|
|
||||||
|
fake_matrix_client = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.get_displayname.return_value = Mock(
|
||||||
|
spec=ProfileGetDisplayNameResponse, displayname="FakeUser"
|
||||||
|
)
|
||||||
|
fake_room_create_response = Mock(spec=RoomCreateError)
|
||||||
|
fake_room_create_response.message = "error"
|
||||||
|
fake_matrix_client.room_create.return_value = fake_room_create_response
|
||||||
|
|
||||||
|
await matrix_client_pool.create_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.get_displayname.assert_called_once_with(
|
||||||
|
"@fake_dm_user:example.com"
|
||||||
|
)
|
||||||
|
fake_matrix_client.room_create.assert_called_once_with(
|
||||||
|
visibility=RoomVisibility.private,
|
||||||
|
name="Alerts for FakeUser",
|
||||||
|
invite=["@other_user:chat.example.com", "@fake_dm_user:example.com"],
|
||||||
|
is_direct=True,
|
||||||
|
preset=RoomPreset.private_chat,
|
||||||
|
)
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com",
|
||||||
|
},
|
||||||
|
matrix_client_pool.dm_rooms,
|
||||||
|
)
|
||||||
|
|
||||||
|
@patch.object(
|
||||||
|
matrix_alertbot.matrix.MatrixClientPool,
|
||||||
|
"_create_matrix_client",
|
||||||
|
autospec=True,
|
||||||
|
side_effect=mock_create_matrix_client,
|
||||||
|
)
|
||||||
|
async def test_create_dm_rooms_with_display_name_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,
|
||||||
|
)
|
||||||
|
fake_find_existing_dm_rooms = AsyncMock(autospec=True)
|
||||||
|
fake_find_existing_dm_rooms.return_value = {
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com"
|
||||||
|
}
|
||||||
|
matrix_client_pool.find_existing_dm_rooms = fake_find_existing_dm_rooms
|
||||||
|
|
||||||
|
fake_matrix_client = matrix_client_pool.matrix_client
|
||||||
|
fake_matrix_client.get_displayname.return_value = Mock(
|
||||||
|
spec=ProfileGetDisplayNameError, message="error"
|
||||||
|
)
|
||||||
|
fake_room_create_response = Mock(spec=RoomCreateResponse)
|
||||||
|
fake_room_create_response.room_id = None
|
||||||
|
fake_matrix_client.room_create.return_value = fake_room_create_response
|
||||||
|
|
||||||
|
await matrix_client_pool.create_dm_rooms(
|
||||||
|
account=matrix_client_pool.account,
|
||||||
|
matrix_client=matrix_client_pool.matrix_client,
|
||||||
|
config=self.fake_config,
|
||||||
|
)
|
||||||
|
|
||||||
|
fake_matrix_client.get_displayname.assert_called_once_with(
|
||||||
|
"@fake_dm_user:example.com"
|
||||||
|
)
|
||||||
|
fake_matrix_client.room_create.assert_not_called()
|
||||||
|
self.assertDictEqual(
|
||||||
|
{
|
||||||
|
"@other_dm_user:example.com": "!other_dmroom:example.com",
|
||||||
|
},
|
||||||
|
matrix_client_pool.dm_rooms,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import re
|
||||||
import unittest
|
import unittest
|
||||||
from typing import Dict
|
from typing import Dict
|
||||||
from unittest.mock import Mock, call, patch
|
from unittest.mock import Mock, call, patch
|
||||||
|
@ -10,7 +11,7 @@ from diskcache import Cache
|
||||||
import matrix_alertbot.webhook
|
import matrix_alertbot.webhook
|
||||||
from matrix_alertbot.alert import Alert, AlertRenderer
|
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 BiDict, Config
|
||||||
from matrix_alertbot.errors import (
|
from matrix_alertbot.errors import (
|
||||||
AlertmanagerError,
|
AlertmanagerError,
|
||||||
MatrixClientError,
|
MatrixClientError,
|
||||||
|
@ -38,6 +39,9 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
self.fake_matrix_client = Mock(spec=nio.AsyncClient)
|
self.fake_matrix_client = Mock(spec=nio.AsyncClient)
|
||||||
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_matrix_client_pool.dm_rooms = {
|
||||||
|
"@fake_dm_user:example.com": "!dmroom:example.com"
|
||||||
|
}
|
||||||
self.fake_alertmanager_client = Mock(spec=AlertmanagerClient)
|
self.fake_alertmanager_client = Mock(spec=AlertmanagerClient)
|
||||||
self.fake_alert_renderer = Mock(spec=AlertRenderer)
|
self.fake_alert_renderer = Mock(spec=AlertRenderer)
|
||||||
self.fake_cache = Mock(spec=Cache)
|
self.fake_cache = Mock(spec=Cache)
|
||||||
|
@ -51,6 +55,11 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
self.fake_config.allowed_rooms = [self.fake_room_id]
|
self.fake_config.allowed_rooms = [self.fake_room_id]
|
||||||
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_config.dm_select_label = "uuid"
|
||||||
|
self.fake_config.dm_filter_labels = {"matrix-alertbot": re.compile("dm")}
|
||||||
|
self.fake_config.dm_users = BiDict(
|
||||||
|
{"a7b37c33-574c-45ac-bb07-a3b314c2da54": "@fake_dm_user:example.com"}
|
||||||
|
)
|
||||||
|
|
||||||
self.fake_request = Mock(spec=web_request.Request)
|
self.fake_request = Mock(spec=web_request.Request)
|
||||||
self.fake_request.app = {
|
self.fake_request.app = {
|
||||||
|
@ -89,6 +98,20 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
self.fake_alert_2,
|
self.fake_alert_2,
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
self.fake_dm_alert = {
|
||||||
|
"fingerprint": "fingerprint",
|
||||||
|
"generatorURL": "http://example.com/alert",
|
||||||
|
"status": "firing",
|
||||||
|
"labels": {
|
||||||
|
"alertname": "alert",
|
||||||
|
"severity": "warning",
|
||||||
|
"job": "job",
|
||||||
|
"uuid": "a7b37c33-574c-45ac-bb07-a3b314c2da54",
|
||||||
|
"matrix-alertbot": "dm",
|
||||||
|
},
|
||||||
|
"annotations": {"description": "some description"},
|
||||||
|
}
|
||||||
|
self.fake_dm_alerts = {"alerts": [self.fake_dm_alert]}
|
||||||
|
|
||||||
webhook = Webhook(
|
webhook = Webhook(
|
||||||
self.fake_matrix_client_pool,
|
self.fake_matrix_client_pool,
|
||||||
|
@ -261,6 +284,85 @@ 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, "send_text_to_room")
|
||||||
|
async def test_post_alerts_for_dm_user(self, fake_send_text_to_room: Mock) -> None:
|
||||||
|
self.fake_alertmanager_client.update_silence.side_effect = (
|
||||||
|
update_silence_raise_silence_not_found
|
||||||
|
)
|
||||||
|
|
||||||
|
data = self.fake_dm_alerts
|
||||||
|
async with self.client.request(
|
||||||
|
"POST", f"/alerts/{self.fake_room_id}", json=data
|
||||||
|
) as response:
|
||||||
|
self.assertEqual(200, response.status)
|
||||||
|
|
||||||
|
self.fake_alertmanager_client.update_silence.assert_called_once_with(
|
||||||
|
"fingerprint"
|
||||||
|
)
|
||||||
|
self.assertEqual(1, fake_send_text_to_room.call_count)
|
||||||
|
fake_send_text_to_room.assert_has_calls(
|
||||||
|
[
|
||||||
|
call(
|
||||||
|
self.fake_matrix_client,
|
||||||
|
self.fake_matrix_client_pool.dm_rooms["@fake_dm_user:example.com"],
|
||||||
|
"[⚠️ WARNING] alert: some description",
|
||||||
|
'<font color="#ffc107">\n <b>[⚠️ WARNING]</b>\n</font> '
|
||||||
|
'<a href="http://example.com/alert">alert</a>\n (job)<br/>\n'
|
||||||
|
"some description",
|
||||||
|
notice=False,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
)
|
||||||
|
self.fake_cache.set.assert_called_once_with(
|
||||||
|
fake_send_text_to_room.return_value.event_id,
|
||||||
|
"fingerprint",
|
||||||
|
expire=self.fake_config.cache_expire_time,
|
||||||
|
)
|
||||||
|
self.assertEqual(1, self.fake_cache.delete.call_count)
|
||||||
|
self.fake_cache.delete.assert_has_calls([call("fingerprint")])
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.webhook, "send_text_to_room")
|
||||||
|
async def test_post_alerts_for_unknown_dm_user(
|
||||||
|
self, fake_send_text_to_room: Mock
|
||||||
|
) -> None:
|
||||||
|
self.fake_alertmanager_client.update_silence.side_effect = (
|
||||||
|
update_silence_raise_silence_not_found
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fake_config.dm_users = BiDict()
|
||||||
|
|
||||||
|
data = self.fake_dm_alerts
|
||||||
|
async with self.client.request(
|
||||||
|
"POST", f"/alerts/{self.fake_room_id}", json=data
|
||||||
|
) as response:
|
||||||
|
self.assertEqual(200, response.status)
|
||||||
|
|
||||||
|
self.fake_alertmanager_client.update_silence.assert_not_called()
|
||||||
|
fake_send_text_to_room.assert_not_called()
|
||||||
|
self.fake_cache.set.assert_not_called()
|
||||||
|
self.fake_cache.delete.assert_not_called()
|
||||||
|
|
||||||
|
@patch.object(matrix_alertbot.webhook, "send_text_to_room")
|
||||||
|
async def test_post_alerts_for_dm_user_with_unknown_room(
|
||||||
|
self, fake_send_text_to_room: Mock
|
||||||
|
) -> None:
|
||||||
|
self.fake_alertmanager_client.update_silence.side_effect = (
|
||||||
|
update_silence_raise_silence_not_found
|
||||||
|
)
|
||||||
|
|
||||||
|
self.fake_matrix_client_pool.dm_rooms = {}
|
||||||
|
|
||||||
|
data = self.fake_dm_alerts
|
||||||
|
async with self.client.request(
|
||||||
|
"POST", f"/alerts/{self.fake_room_id}", json=data
|
||||||
|
) as response:
|
||||||
|
self.assertEqual(200, response.status)
|
||||||
|
|
||||||
|
self.fake_alertmanager_client.update_silence.assert_not_called()
|
||||||
|
fake_send_text_to_room.assert_not_called()
|
||||||
|
self.fake_cache.set.assert_not_called()
|
||||||
|
self.fake_cache.delete.assert_not_called()
|
||||||
|
|
||||||
@patch.object(matrix_alertbot.webhook, "send_text_to_room")
|
@patch.object(matrix_alertbot.webhook, "send_text_to_room")
|
||||||
async def test_post_alerts_with_empty_data(
|
async def test_post_alerts_with_empty_data(
|
||||||
self, fake_send_text_to_room: Mock
|
self, fake_send_text_to_room: Mock
|
||||||
|
@ -418,6 +520,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
fake_alert = Mock(spec=Alert)
|
fake_alert = Mock(spec=Alert)
|
||||||
fake_alert.firing = True
|
fake_alert.firing = True
|
||||||
fake_alert.fingerprint = "fingerprint"
|
fake_alert.fingerprint = "fingerprint"
|
||||||
|
fake_alert.labels = []
|
||||||
|
|
||||||
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
|
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
|
||||||
|
|
||||||
|
@ -433,6 +536,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
fake_alert = Mock(spec=Alert)
|
fake_alert = Mock(spec=Alert)
|
||||||
fake_alert.firing = True
|
fake_alert.firing = True
|
||||||
fake_alert.fingerprint = "fingerprint"
|
fake_alert.fingerprint = "fingerprint"
|
||||||
|
fake_alert.labels = []
|
||||||
|
|
||||||
self.fake_alertmanager_client.update_silence.side_effect = SilenceNotFoundError
|
self.fake_alertmanager_client.update_silence.side_effect = SilenceNotFoundError
|
||||||
|
|
||||||
|
@ -461,6 +565,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
fake_alert = Mock(spec=Alert)
|
fake_alert = Mock(spec=Alert)
|
||||||
fake_alert.firing = True
|
fake_alert.firing = True
|
||||||
fake_alert.fingerprint = "fingerprint"
|
fake_alert.fingerprint = "fingerprint"
|
||||||
|
fake_alert.labels = []
|
||||||
|
|
||||||
self.fake_alertmanager_client.update_silence.side_effect = SilenceExtendError
|
self.fake_alertmanager_client.update_silence.side_effect = SilenceExtendError
|
||||||
|
|
||||||
|
@ -487,6 +592,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
fake_alert = Mock(spec=Alert)
|
fake_alert = Mock(spec=Alert)
|
||||||
fake_alert.firing = False
|
fake_alert.firing = False
|
||||||
fake_alert.fingerprint = "fingerprint"
|
fake_alert.fingerprint = "fingerprint"
|
||||||
|
fake_alert.labels = []
|
||||||
|
|
||||||
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
|
await create_alert(fake_alert, self.fake_room_id, self.fake_request)
|
||||||
|
|
||||||
|
@ -507,6 +613,7 @@ class WebhookApplicationTestCase(aiohttp.test_utils.AioHTTPTestCase):
|
||||||
fake_alert = Mock(spec=Alert)
|
fake_alert = Mock(spec=Alert)
|
||||||
fake_alert.firing = False
|
fake_alert.firing = False
|
||||||
fake_alert.fingerprint = "fingerprint"
|
fake_alert.fingerprint = "fingerprint"
|
||||||
|
fake_alert.labels = []
|
||||||
|
|
||||||
self.fake_matrix_client_pool.matrix_client = None
|
self.fake_matrix_client_pool.matrix_client = None
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue