#!/usr/bin/env python3 # Libervia: an XMPP client # Copyright (C) 2009-2026 Jérôme Poisson (goffi@goffi.org) # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU Affero General Public License for more details. # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . from unittest.mock import MagicMock, AsyncMock from pytest import fixture from twisted.internet import defer from twisted.words.protocols.jabber import jid from twisted.words.xish import domish from libervia.backend import G from tx_xmpp.data_form import NS_X_DATA from libervia.backend.core.main import LiberviaBackend from libervia.backend.models.tor import TorMode from libervia.backend.tools import async_trigger as trigger from libervia.backend.core import xmpp @fixture(scope="session") def bridge(): bridge = AsyncMock() bridge.add_signal = MagicMock() bridge.add_method = MagicMock() return bridge @fixture(scope="session") def storage(): return AsyncMock() class MockLiberviaBackend(LiberviaBackend): def __init__(self, bridge, storage): G.set_host(self) self._cb_map = {} self._menus = {} self._menus_paths = {} self._test_config = {} self.profiles = {} self.plugins = {} # map for short name to whole namespace, # extended by plugins with register_namespace self.ns_map = { "x-data": NS_X_DATA, "disco#info": xmpp.NS_DISCO_INFO, } self.memory = MagicMock() self.memory.storage = storage self.memory.config_get.side_effect = self.get_test_config self.trigger = trigger.TriggerManager() self._bridge = bridge defer.ensureDeferred(self._post_init()) self.common_cache = AsyncMock() self._import_plugins() self._add_base_menus() self.initialised = defer.Deferred() self.initialised.callback(None) def get_local_path(self, *args, **kwargs): return MagicMock() def get_test_config(self, section, name, default=None): return self._test_config.get((section or None, name), default) def register_namespace(self, short_name, namespace): # Plugin classes may be instantiated several times in tests, so we do nothing in # this method to avoid ConflictError. pass def _init(self): pass async def _post_init(self): pass @fixture(scope="session") def host(bridge, storage): host = MockLiberviaBackend(bridge=bridge, storage=storage) return host @fixture def client_IQ(): """Factory fixture to create a mock IQ method for testing. Usage: def test_something(client, client_IQ): response_elt = domish.Element((None, "iq")) response_elt["type"] = "result" client.IQ = client_IQ(response_elt=response_elt) # Later in test: assert len(client.IQ.sent_iqs) == 1 @param response: Optional response to return from send(). If None, returns empty IQ. @return: The mock IQ method with attached sent_iqs list. """ def factory(response_elt=None): sent_iqs = [] def client_IQ(type_="set", timeout=60): iq_elt = domish.Element((None, "iq")) iq_elt["type"] = type_ async def mock_send(): sent_iqs.append(iq_elt) if response_elt is not None: return response_elt # Return empty IQ by default empty_response = domish.Element((None, "iq")) empty_response["type"] = "result" return empty_response iq_elt.send = mock_send return iq_elt # Attach list for test assertions client_IQ.sent_iqs = sent_iqs return client_IQ return factory @fixture def client(): client = MagicMock() client.jid = jid.JID("test_user@test.example/123") client.server_jid = jid.JID("test.example") client.pubsub_service = jid.JID("pubsub.test.example") client.pubsub_client = AsyncMock() client.tor = None client.tor_mode = TorMode.DISABLED return client @fixture(autouse=True) def reset_storage(host): """Replace storage with a fresh mock before each test. The host and storage fixtures use session scope, which means any assignments to storage attributes (e.g., storage.method = AsyncMock(...)) persist across tests. This fixture ensures each test starts with a clean slate. """ host.memory.storage = AsyncMock()