Files
vnctalk-prosody/tests/conftest.py
T
Stefan-Sanger 2046867241 docs: add AGENTS.md, patch analysis, and pytest integration suite
- Add AGENTS.md with repo-specific conventions, build steps, and quirks
- Add PATCHES_AND_MODULES.md documenting every upstream deviation
- Add tests/ with pytest/slixmpp integration suite for core, MUC,
  vnctalk extensions, and infrastructure verification
- Include pytest.ini and .gitignore

Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
2026-07-15 17:58:02 +02:00

214 lines
7.4 KiB
Python

"""Test configuration and shared fixtures for vnctalk-prosody verification."""
import asyncio
import os
import ssl
import pytest
import slixmpp
from slixmpp.exceptions import IqError, IqTimeout
class VNCXmppClient(slixmpp.ClientXMPP):
"""Async-friendly XMPP client for vnctalk tests."""
def __init__(self, jid, password, host=None, port=5222, use_ssl=True):
super().__init__(jid, password)
self.connected_event = asyncio.Event()
self.disconnected_event = asyncio.Event()
self.session_started_event = asyncio.Event()
self.add_event_handler("session_start", self._on_session_start)
self.add_event_handler("disconnected", self._on_disconnected)
# Force connection parameters if provided
if host:
self.connect_address = (host, port)
else:
self.connect_address = None
self.use_ssl = use_ssl
def _on_session_start(self, event):
self.session_started_event.set()
def _on_disconnected(self, event):
self.disconnected_event.set()
async def async_connect(self, timeout=30):
if self.connect_address:
self.use_ssl = self.use_ssl
# slixmpp register_plugins is called automatically
if self.use_ssl:
await self.connect(self.connect_address, use_ssl=True)
else:
await self.connect(self.connect_address, use_ssl=False)
else:
await self.connect()
self.process(timeout=1)
await asyncio.wait_for(self.session_started_event.wait(), timeout=timeout)
async def async_disconnect(self):
self.disconnect()
await asyncio.wait_for(self.disconnected_event.wait(), timeout=10)
async def enable_carbons(self):
iq = self.make_iq_set()
iq.append(slixmpp.etree.Element("{urn:xmpp:carbons:2}enable"))
await iq.send()
async def get_mam_prefs(self):
iq = self.make_iq_get()
iq.append(slixmpp.etree.Element("{urn:xmpp:mam:2}prefs"))
res = await iq.send()
return res
async def query_mam(self, with_jid=None, start=None, end=None):
iq = self.make_iq_set()
query = slixmpp.etree.SubElement(iq.xml, "{urn:xmpp:mam:2}query")
if with_jid:
x = slixmpp.etree.SubElement(query, "{jabber:x:data}x")
x.set("type", "submit")
field = slixmpp.etree.SubElement(x, "{jabber:x:data}field")
field.set("var", "with")
value = slixmpp.etree.SubElement(field, "{jabber:x:data}value")
value.text = with_jid
res = await iq.send()
return res
async def get_vcard(self, to=None):
iq = self.make_iq_get(to=to)
iq.append(slixmpp.etree.Element("{vcard-temp}vCard"))
res = await iq.send()
return res
async def set_vcard(self, vcard_xml):
iq = self.make_iq_set()
iq.append(vcard_xml)
res = await iq.send()
return res
async def disco_info(self, to=None):
iq = self.make_iq_get(to=to)
iq.append(slixmpp.etree.Element("{http://jabber.org/protocol/disco#info}query"))
res = await iq.send()
return res
async def disco_items(self, to=None):
iq = self.make_iq_get(to=to)
iq.append(slixmpp.etree.Element("{http://jabber.org/protocol/disco#items}query"))
res = await iq.send()
return res
def pytest_addoption(parser):
parser.addoption(
"--xmpp-host", action="store", default=os.getenv("XMPP_HOST", "localhost"),
help="XMPP server hostname/IP"
)
parser.addoption(
"--xmpp-port", action="store", type=int, default=int(os.getenv("XMPP_PORT", "5222")),
help="XMPP C2S port"
)
parser.addoption(
"--xmpp-jid", action="store", default=os.getenv("XMPP_JID", ""),
help="Test account JID"
)
parser.addoption(
"--xmpp-password", action="store", default=os.getenv("XMPP_PASSWORD", ""),
help="Test account password"
)
parser.addoption(
"--xmpp-domain", action="store", default=os.getenv("XMPP_DOMAIN", "example.com"),
help="XMPP domain"
)
parser.addoption(
"--bosh-url", action="store", default=os.getenv("BOSH_URL", ""),
help="BOSH URL to test (e.g. http://localhost:5280/http-bind)"
)
parser.addoption(
"--ws-url", action="store", default=os.getenv("WS_URL", ""),
help="WebSocket URL to test (e.g. ws://localhost:5280/xmpp-websocket)"
)
parser.addoption(
"--rest-url", action="store", default=os.getenv("REST_URL", ""),
help="mod_http_rest URL (e.g. http://localhost:5280/rest)"
)
parser.addoption(
"--muc-domain", action="store", default=os.getenv("MUC_DOMAIN", ""),
help="MUC component domain (e.g. conference.example.com)"
)
parser.addoption(
"--admin-telnet-host", action="store", default=os.getenv("ADMIN_TELNET_HOST", "127.0.0.1"),
help="Admin telnet host"
)
parser.addoption(
"--admin-telnet-port", action="store", type=int, default=int(os.getenv("ADMIN_TELNET_PORT", "5582")),
help="Admin telnet port"
)
parser.addoption(
"--skip-live", action="store_true", default=False,
help="Skip tests that require a live XMPP connection"
)
@pytest.fixture(scope="session")
def xmpp_config(request):
return {
"host": request.config.getoption("--xmpp-host"),
"port": request.config.getoption("--xmpp-port"),
"jid": request.config.getoption("--xmpp-jid"),
"password": request.config.getoption("--xmpp-password"),
"domain": request.config.getoption("--xmpp-domain"),
"bosh_url": request.config.getoption("--bosh-url"),
"ws_url": request.config.getoption("--ws-url"),
"rest_url": request.config.getoption("--rest-url"),
"muc_domain": request.config.getoption("--muc-domain"),
"admin_telnet_host": request.config.getoption("--admin-telnet-host"),
"admin_telnet_port": request.config.getoption("--admin-telnet-port"),
"skip_live": request.config.getoption("--skip-live"),
}
@pytest.fixture
async def xmpp_client(xmpp_config):
"""Yield a connected XMPP client."""
cfg = xmpp_config
if cfg["skip_live"] or not cfg["jid"] or not cfg["password"]:
pytest.skip("Live XMPP tests disabled or credentials missing")
client = VNCXmppClient(
cfg["jid"], cfg["password"],
host=cfg["host"], port=cfg["port"], use_ssl=True
)
try:
await client.async_connect(timeout=30)
yield client
finally:
if client.session_started_event.is_set():
await client.async_disconnect()
@pytest.fixture
async def second_client(xmpp_config):
"""Yield a second connected client (useful for MUC/carbons tests)."""
cfg = xmpp_config
if cfg["skip_live"] or not cfg["jid"] or not cfg["password"]:
pytest.skip("Live XMPP tests disabled or credentials missing")
# Derive a second resource by appending _test2
jid = cfg["jid"]
if "/" in jid:
bare, _ = jid.split("/", 1)
else:
bare = jid
second_jid = f"{bare}/test2"
client = VNCXmppClient(
second_jid, cfg["password"],
host=cfg["host"], port=cfg["port"], use_ssl=True
)
try:
await client.async_connect(timeout=30)
yield client
finally:
if client.session_started_event.is_set():
await client.async_disconnect()