test: carbon-copied REST test uses same-bare-JID second resource

The test was skipping because it depended on the second_client fixture,
which uses XMPP_JID2 (a distinct account). Carbons require both
resources to share the same bare JID.

Rewrite to create a second VNCXmppClient inline with the same bare JID
as xmpp_client but a different resource (/carbon-<random>), so the test
runs regardless of whether XMPP_JID2 is configured or distinct.

Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
This commit is contained in:
2026-07-15 17:58:02 +02:00
parent 015c554d30
commit aa822329dd
+64 -35
View File
@@ -6,6 +6,8 @@ import pytest
import slixmpp
import xml.etree.ElementTree as ET
from conftest import VNCXmppClient
@pytest.mark.asyncio
class TestCoreXmpp:
@@ -235,47 +237,74 @@ class TestRestToCarbonsRoundtrip:
"""Priority 2 from AUDIT: REST-injected messages are carbon-copied."""
async def test_rest_message_carbon_copied_to_second_resource(
self, xmpp_client, second_client, xmpp_config, rest_injector
self, xmpp_client, xmpp_config, rest_injector
):
"""Enable carbons on both resources. Inject via REST. Second resource must receive a carbon."""
# Both clients share the same bare JID (second_client is same account, different resource)
# or they may be different accounts. For carbons, they MUST be same bare JID.
if xmpp_client.boundjid.bare != second_client.boundjid.bare:
pytest.skip("Carbons test requires both clients to share the same bare JID")
"""Enable carbons on two resources of the same account. Inject via REST.
Second resource must receive a carbon copy.
await xmpp_client.enable_carbons()
await second_client.enable_carbons()
await asyncio.sleep(0.5)
Creates a second client with the same bare JID as xmpp_client but a
different resource, so this test works even when XMPP_JID2 is a
distinct account (or not configured at all).
"""
bare_jid = xmpp_client.boundjid.bare
password = xmpp_config["password"]
second_jid = f"{bare_jid}/carbon-{uuid.uuid4().hex[:4]}"
carbon_received = asyncio.Event()
carbon_bodies = []
def on_message(msg):
if msg["type"] == "chat":
carb = msg.xml.find("{urn:xmpp:carbons:2}received")
if carb is not None:
fwd = carb.find("{urn:xmpp:forward:0}forwarded")
if fwd is not None:
body_el = fwd.find("{jabber:client}message/{jabber:client}body")
if body_el is not None and body_el.text:
carbon_bodies.append(body_el.text)
carbon_received.set()
second_client.add_event_handler("message", on_message)
second_client = VNCXmppClient(
second_jid, password,
host=xmpp_config["host"], port=xmpp_config["port"],
use_ssl=True, verify_ssl=xmpp_config["verify_ssl"],
)
try:
msg_id = f"rest-carbons-{uuid.uuid4().hex[:12]}"
from_jid = xmpp_client.boundjid.bare
to_jid = xmpp_client.boundjid.bare
body = f"carbon test {msg_id}"
await second_client.async_connect(timeout=30)
status, _ = await rest_injector.inject_message(msg_id, from_jid, to_jid, body)
assert status in (201, 422)
await xmpp_client.enable_carbons()
await second_client.enable_carbons()
await asyncio.sleep(0.5)
carbon_received = asyncio.Event()
carbon_bodies = []
# A REST-injected message enters through the sender-side (c2s)
# carbons path (the mod_carbons patch hooks vnc-rest-message as a
# c2s handler), so other resources of the *sender* receive <sent>
# carbons; <received> would only appear on recipient resources.
# slixmpp's "message" event only fires for messages with a <body>;
# carbon wrappers have none, so a raw stream handler is needed.
from slixmpp.xmlstream.handler import Callback
from slixmpp.xmlstream.matcher import MatchXPath
def on_carbon_wrapper(msg):
for tag in ("sent", "received"):
carb = msg.xml.find("{urn:xmpp:carbons:2}" + tag)
if carb is not None:
fwd = carb.find("{urn:xmpp:forward:0}forwarded")
if fwd is not None:
body_el = fwd.find("{jabber:client}message/{jabber:client}body")
if body_el is not None and body_el.text:
carbon_bodies.append(body_el.text)
carbon_received.set()
second_client.register_handler(Callback(
"carbon-probe", MatchXPath("{jabber:client}message"), on_carbon_wrapper
))
try:
await asyncio.wait_for(carbon_received.wait(), timeout=8)
except asyncio.TimeoutError:
pytest.fail("Carbon copy not received on second resource after REST injection")
msg_id = f"rest-carbons-{uuid.uuid4().hex[:12]}"
from_jid = bare_jid
to_jid = bare_jid
body = f"carbon test {msg_id}"
assert any(body in b for b in carbon_bodies), "Carbon body does not match injected message"
status, _ = await rest_injector.inject_message(msg_id, from_jid, to_jid, body)
assert status in (201, 422)
try:
await asyncio.wait_for(carbon_received.wait(), timeout=8)
except asyncio.TimeoutError:
pytest.fail("Carbon copy not received on second resource after REST injection")
assert any(body in b for b in carbon_bodies), "Carbon body does not match injected message"
finally:
second_client.remove_handler("carbon-probe")
finally:
second_client.del_event_handler("message", on_message)
if second_client.session_started_event.is_set():
await second_client.async_disconnect()