Files
vnctalk-prosody/tests/test_01_core.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

117 lines
4.8 KiB
Python

"""Core XMPP server tests: connectivity, auth, disco, MAM, carbons."""
import asyncio
import pytest
import slixmpp
from slixmpp.exceptions import IqError, IqTimeout
@pytest.mark.asyncio
class TestCoreXmpp:
async def test_connect_and_auth(self, xmpp_client):
"""Client must authenticate successfully."""
assert xmpp_client.session_started_event.is_set()
assert xmpp_client.boundjid.bare
async def test_disco_info_server(self, xmpp_client, xmpp_config):
"""Server must advertise required features."""
res = await xmpp_client.disco_info(to=xmpp_config["domain"])
features = [f.get("var") for f in res.xml.findall(".//{http://jabber.org/protocol/disco#info}feature")]
required = [
"urn:xmpp:mam:2",
"urn:xmpp:carbons:2",
"urn:xmpp:sid:0",
"http://jabber.org/protocol/disco#info",
"http://jabber.org/protocol/disco#items",
]
for f in required:
assert f in features, f"Missing required disco feature: {f}"
async def test_mam_available(self, xmpp_client):
"""MAM prefs query must succeed."""
res = await xmpp_client.get_mam_prefs()
assert res.xml.find("{urn:xmpp:mam:2}prefs") is not None
async def test_carbons_enable(self, xmpp_client):
"""Carbons enable IQ must succeed."""
await xmpp_client.enable_carbons()
# If no exception was raised, carbons are enabled.
async def test_smacks_supported(self, xmpp_client, xmpp_config):
"""Server should advertise stream management feature if mod_smacks is loaded."""
res = await xmpp_client.disco_info(to=xmpp_config["domain"])
features = [f.get("var") for f in res.xml.findall(".//{http://jabber.org/protocol/disco#info}feature")]
# Stream management is not always in disco#info; it is negotiated at stream level.
# We just verify the connection succeeded with stream features.
assert "urn:xmpp:mam:2" in features
@pytest.mark.asyncio
class TestBoshAndWebsocket:
"""Verify alternative connection paths are available."""
async def test_bosh_url_reachable(self, xmpp_config):
"""BOSH endpoint must return something (not connection refused)."""
import aiohttp
url = xmpp_config.get("bosh_url")
if not url:
pytest.skip("No BOSH URL configured")
async with aiohttp.ClientSession() as session:
# A raw GET/POST to BOSH root should at least not 404 at network level
async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp:
# Prosody BOSH may return 200 with empty body or a policy notice
assert resp.status in (200, 404, 403)
async def test_websocket_url_reachable(self, xmpp_config):
"""WebSocket endpoint must accept upgrade."""
import aiohttp
url = xmpp_config.get("ws_url")
if not url:
pytest.skip("No WebSocket URL configured")
async with aiohttp.ClientSession() as session:
try:
async with session.get(url, timeout=aiohttp.ClientTimeout(total=5)) as resp:
# If we get 400 or 426 upgrade required, the endpoint exists
assert resp.status in (200, 400, 426, 404)
except aiohttp.ClientResponseError as e:
assert e.status in (400, 426)
@pytest.mark.asyncio
class TestRestInjection:
"""mod_http_rest endpoint behaviour."""
async def test_rest_accepts_xml(self, xmpp_config):
"""POST text/xml to /rest must return 201."""
import aiohttp
url = xmpp_config.get("rest_url")
if not url:
pytest.skip("No REST URL configured")
# Minimal valid XMPP stanza
body = '<message to="test@example.com" from="admin@example.com" type="chat"><body>hello</body></message>'
async with aiohttp.ClientSession() as session:
async with session.post(
url,
data=body,
headers={"Content-Type": "text/xml"},
timeout=aiohttp.ClientTimeout(total=10)
) as resp:
# 201 means accepted and injected; 422 means parseable but maybe not routable
assert resp.status in (201, 422)
async def test_rest_rejects_non_xml(self, xmpp_config):
"""POST with wrong Content-Type must return 415."""
import aiohttp
url = xmpp_config.get("rest_url")
if not url:
pytest.skip("No REST URL configured")
async with aiohttp.ClientSession() as session:
async with session.post(
url,
data="not xml",
headers={"Content-Type": "text/plain"},
timeout=aiohttp.ClientTimeout(total=10)
) as resp:
assert resp.status == 415