1. REST 404 "Unknown host: prosody": the tester reaches prosody by
service name (http://prosody:5280), so aiohttp sends Host: prosody,
which Prosody rejects as an unknown vhost. The auto host-header
heuristic only overrides for IP/localhost. Set REST_HOST_HEADER=
example.com explicitly in the tester env so HTTP routing lands on the
example.com VirtualHost that serves mod_http_rest /rest.
2. healthcheck.sh not found: the static test_04_infra checks resolve
../config/healthcheck.sh (= /config/healthcheck.sh) but the tester
image only ships /tests. Mount ./config:/config:ro so the path
resolves inside the container.
3. telnet non-loopback banner: read only 256 bytes, capturing just the
ASCII-art top and never the literal "Prosody" text. Bump to 1024 to
match test_04_infra.test_telnet_banner, which passes with the larger
read.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The tester container uses WORKDIR /tests, so 'tests/pytest.ini' resolves
to /tests/tests/pytest.ini which does not exist, causing FileNotFoundError
in CI. Use paths relative to /tests (pytest . -c pytest.ini) for all
in-container invocations: CI job, Makefile target, tester image CMD, and
docs. Host-run scripts are unchanged (they run from repo root).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Always sending Host: <xmpp domain> to /rest broke when REST was fronted
by an ingress (TLS hostname mismatch -> 431). Add a --rest-host-header
option (REST_HOST_HEADER env, default 'auto') that sends the XMPP domain
Host only for IP/localhost URLs where prosody needs it for vhost routing;
'none' disables, any other value is sent verbatim. Documents the option
in tests/README.md.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
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>
Bump Dockerfile to prosody-13.0.6; all 8 patches re-ported against 13.0.6.
Re-ported patches (3 changed, 5 applied with offset):
- hidden.lib.patch: 13.0 uses module:may() instead of um_is_admin; changed
to 'if restrict_public then' (same intent: hide option for everyone)
- mod_muc.patch: 13.0 added restrict_pm between register and
presence_broadcast; updated hunk 1 context
- mod_muc_unique.patch: 13.0 uses 'require "prosody.util.stanza"'
(namespaced); updated context
- muc.lib, mod_carbons, mod_mam, mod_muc_mam, register.lib: applied
with line offsets, no re-port needed
Config changes:
- Remove mod_posix from modules_enabled (13.0 absorbed signal handling,
pidfile, and run_as_root check into core util/startup.lua)
- Move pubsub from modules_enabled to Component (13.0 requires pubsub
to be loaded as a component, not a module)
Test fix:
- test_muc_fcm_push_to_offline_member: wait for count=2 captures instead
of 1 — mod_vnc_muc_fcm pushes to ALL affiliated members (including
sender, because it can't see main-host sessions from the MUC
component); the test was racing on which push arrived first
Verified: prosodyctl check config passes; compose-harness testsuite
green (80 passed, 6 skipped, 0 failed).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The module exists in vnctalk/ but is not listed in modules_enabled in
the config template. The test previously passed on external deployments
by coincidence — the test users already had vCards with FN from real
usage. On the compose harness with a fresh DB, the test fails because
nothing generates a vCard.
Add @pytest.mark.skip with a reason pointing to the missing module.
Simplify run-tests.sh to a single pytest invocation (was 12 separate
calls). Update m1-manual-tasks.md §3.5 to reflect the skip.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Three changes to complete the repo-level work for Milestone 1 (0.11.6 →
0.12.6) plus a howto for the remaining manual/external tasks:
1. config/prosody.cfg.lua.template: re-add run_as_root = true. Production
and the compose harness run as root (startup.sh writes into root-owned
/etc/prosody/). Without it, mod_posix calls prosody.shutdown() during
startup, which deactivates c2s (port 5222) before the shutdown itself
errors out (prosody.main_thread is nil during module init), leaving
Prosody running without c2s.
2. tests/test_08_image_patches.py: replace 3 stale patch-marker entries
that checked for patches M1 intentionally dropped (moduleapi,
mod_admin_telnet, muc.lib dumpTable) with markers that verify their
config-based replacements (console_interfaces, http_interfaces) and
the storagemanager.open() rewrite in mod_vnc_muc_fcm.lua.
3. upgrade-plan.md: update M1 status to reflect the post-M1 fixes
(run_as_root, default_storage, stale tests), mark manual steps (DB
migration, telnet console regression, external testsuite) with
cross-references to m1-manual-tasks.md, and correct 0.12.5 → 0.12.6
throughout.
4. m1-manual-tasks.md: new file documenting the three manual tasks that
require external infrastructure — DB schema migration (one-way, with
rehearse-on-copy procedure), telnet console regression test (0.12
reimplemented the console on mod_admin_shell), and external testsuite
run against a dev deployment.
Verified: compose-harness testsuite — 80 passed, 5 skipped, 1 pre-existing
failure (test_vcard_fallback: mod_vnc_vcard_fallback not enabled in config,
unrelated to M1).
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Extend tests/README.md with:
- Running with docker-compose: make test one-shot, host-run flow, mock
capture/log inspection, teardown.
- Importing an existing database into the compose postgres (psql/pg_restore,
fresh-volume flow, auth-backend caveat).
- Running against a standalone local prosody container (build + docker run,
port mapping, optional separate mock for side-effect tests).
- Updated env table (MOCK_URL, SMACKS_HIBERNATION_TIME), test coverage table
(test_08-12), prerequisites (tests/requirements.txt), and notes reflecting
the new healthcheck execution, REST Host header, and XMPP_HOST/PORT fix.
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- mod_vnc_lastactivity: the jabber:iq:last handler returns <forbidden> when
the query has no 'to'; send it to another existing user's bare JID and
assert the result carries a 'seconds' attribute
- set body via msg["body"] and do not await message send() (returns None)
- use unique uuid room names instead of a fixed duplicated localpart
- unlock the freshly-created (locked) room with configure_muc before a
second user joins / is affiliated
- the unregister IQ is fire-and-forget (no reply); tolerate IqTimeout rather
than failing the kick-tracking smoke test
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody keeps every store in just two tables — prosody (keyval/map) and
prosodyarchive (archive) — distinguished by a `store` column. The previous
tests looked for separate tables named like %muc_log%, %kick%, %activity%,
which never exist, so they failed.
- check stores via the `store` column in prosody/prosodyarchive, not by
table name: muc_log + kick are archive stores, activity + vcard are
keyval stores (matching the modules' open_store calls)
- assert the two real tables (prosody, prosodyarchive) exist
- verify MUC archives are stored under the conference component host
- store_user inversion: 'key' is the generated archive UID, not the message
id, so match the REST-injected message on value LIKE and then assert the
owning 'user' is the sender and 'with' is the recipient
Verified against the live database (schema and sample rows inspected).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
A half-open PostgreSQL tunnel accepts TCP but never completes the PG
handshake; asyncpg.connect() with no timeout then hangs the entire
test_06 suite indefinitely. Wrap connect in wait_for(timeout=10) and skip
with a clear message when the DB is unreachable, and only close the
connection in the finally if it was actually established (the previous
unconditional close raised NameError when connect failed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- set body via msg["body"] / read via msg["body"]; do not await message
send() (returns None in slixmpp)
- str(msg["from"]) / str(pres["from"]) before .startswith (JID not str)
- mod_muc_unique: query a bare JID on the MUC component (a room JID), not a
user on the main host. The item-not-found handler is registered on the
component; a query to the user host is merely service-unavailable.
- offline-affiliate test: unlock the freshly-created room with configure_muc
before setting affiliations
Verified the portmanager network_default_read_size patch end-to-end: chat
bodies up to 12000 bytes are delivered intact.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
conftest:
- send initial presence after connect so the server routes directed
messages to the resource (chats to a bare JID otherwise go to offline
storage, breaking the timestamp/receipt tests)
- configure_muc: include FORM_TYPE on non-empty config submits, else
Prosody rejects them ("Form is not of type room configuration")
test_03_vnctalk:
- set body via msg["body"] and do not await presence/message send()
- read body via msg["body"], not msg.body
- replace nonexistent slixmpp.jid.nodeprep / slixmpp.util.id.short with uuid
- disco_info form lookup uses .// (form nested under <query>)
- test_broadcast_component rewritten to exercise real fanout: the
component is message-only (no disco), so post a <vncTalkBroadcast> with a
<to> recipient and assert that recipient receives the cast copy
- config-change broadcast test: unlock the room first, trigger via a real
config submit (not a subject change), and catch the bodyless
<x xmlns='xmpp:vnctalk:update'> with a low-level handler (slixmpp's
message event needs a <body>)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
conftest:
- presence.send()/message.send() return None in slixmpp; do not await them
- JID objects are not str; use str(pres["from"]) before .startswith
- disco_info/disco_items take a timeout so a non-responding server fails
fast instead of hanging the suite
- query_mam_and_collect takes a `to` arg to query a MUC archive (MUC MAM
lives at the room, not the user)
- add configure_muc() to submit the owner config form; a freshly-created
Prosody room is locked until configured and other users cannot join
test_02_muc:
- set body via msg["body"], not msg.body (the latter sets a Python attr
and produces a bodyless stanza that is neither reflected nor archived)
- MUC MAM test queries the room and matches the inner forwarded message by
local name (Prosody may omit xmlns='jabber:client')
- unlock rooms with configure_muc before a second user joins / is affiliated
- broadcast test rewritten as test_muc_broadcast_strips_spoofed_stanza_id:
the real contract is anti-spoofing (a client-injected by-room stanza-id is
stripped) while the room still adds its own legitimate XEP-0359 stanza-id
- unregister IQ handler is fire-and-forget (sends no reply); tolerate the
IqTimeout and assert the side effect (affiliation removed)
- hidden-override test skips when the server does not restrict public rooms
- disco_info form lookups use .// (form is nested under <query>)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
slixmpp's high-level "message" event only fires for stanzas with a
top-level <body> (matcher '{jabber:client}message/{jabber:client}body').
MAM result wrappers (<message><result xmlns='urn:xmpp:mam:2'>...) have no
<body> of their own, so the event never fired and the MAM tests saw zero
results even though the server correctly returned them (<fin complete>).
Capture results with a low-level Callback + MatchXPath on the result
element instead, the same mechanism slixmpp's own xep_0313 plugin uses.
Also filter MAM queries by a start timestamp so a large archive does not
push fresh messages off the first page, and match the inner forwarded
<message> by local name to tolerate Prosody omitting xmlns='jabber:client'.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Archiving triggered by REST injection is asynchronous: the HTTP endpoint
returns 201 before archive:append necessarily completes. A single 1s sleep
was often too short, causing flaky failures even though the message was
present in the database moments later.
- Add _poll_mam_for_id helper that queries MAM up to 10 times with 1s
intervals until the target message id appears
- Replace fixed sleeps in both REST MAM roundtrip tests with polling
- Improves reliability on slower or more loaded Prosody instances
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
For REST-injected messages, mod_mam.lua's patched message_handler sets
both store_user and the 'with' field to the sender's JID (because c2s is
false for REST and the vnc_rest branch only overrides store_user, not
'with'). Filtering by recipient to_jid therefore returns no results.
- Remove with_jid=to_jid from both REST MAM roundtrip tests
- Query the full sender archive and scan for the message id instead
- This is more robust regardless of the 'with' field semantics
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
XEP-0313 requires the MAM query data form to include a hidden FORM_TYPE
field with value 'urn:xmpp:mam:2'. Without it, Prosody's dataform
validation may reject or ignore the 'with' filter, causing the query to
return empty results even when messages exist in the archive.
- Introduce _add_mam_query_form helper that always injects FORM_TYPE
- Update query_mam and query_mam_and_collect to use the helper
- Also support start/end filters for completeness
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody's mod_mam sends <result> elements as individual messages but the
<fin> element inside the IQ response stanza. The old code waited for a
<message> containing <fin>, which never arrives, causing TimeoutError.
- Remove on_mam_fin message handler
- Use iq.send() timeout instead of waiting for a message <fin>
- Add 0.5s grace period after IQ response for trailing <result> messages
- Clean up only the message handler in finally
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
mod_mam.lua advertises urn:xmpp:mam:2 and urn:xmpp:sid:0 via the
account-disco-info hook (user bare JID), not on the server domain.
The previous test incorrectly expected them from the server domain.
- test_disco_info_server: only assert basic disco#info / disco#items
- test_disco_info_account: new test querying bare JID for MAM + sid
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- slixmpp 1.16 (asyncio) has no process() method; connect() already
schedules background processing. Remove the call that caused
AttributeError: 'VNCXmppClient' object has no attribute 'process'
- REST endpoint returns 422 (not 415) for invalid XML; accept both
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
slixmpp 1.16.0 connect() is not a coroutine; it returns an asyncio.Future
and takes (host, port) kwargs, not (address, use_ssl). The previous code
awaited connect() with wrong args causing TypeError inside the async fixture,
which pytest-asyncio surfaced as ERROR at setup.
- Pass host/port/ssl_context to super().__init__() so XMLStream stores them
- Remove manual connect_address and use_ssl tracking
- async_connect: simply await self.connect() with no args (host/port already
configured in __init__)
- Keep ssl_context creation with verify_mode=CERT_NONE for self-signed certs
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
pytest-asyncio 1.4 with asyncio_mode=auto requires async fixtures to be
decorated with @pytest_asyncio.fixture instead of @pytest.fixture.
Without this, async generator fixtures raise AssertionError during setup
at plugin.py:558, causing ERROR before any test code runs.
- Add import pytest_asyncio
- Replace @pytest.fixture with @pytest_asyncio.fixture for xmpp_client,
second_client, and pg_connection async fixtures
- Leave sync fixtures (xmpp_config, rest_injector) unchanged
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
Prosody containers use self-generated self-signed certs. slixmpp with
use_ssl=True verifies certificates by default, causing
SSLCertVerificationError and pytest ERROR during fixture setup.
- VNCXmppClient: add verify_ssl parameter (default False) that creates
an ssl.SSLContext with verify_mode=CERT_NONE when disabled
- conftest.py: add --verify-ssl CLI option / VERIFY_SSL env var;
pass verify_ssl through xmpp_config to both client fixtures
- docs: document --verify-ssl in README.md and AGENTS.md
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
The /rest endpoint is typically protected by a reverse proxy. The
RESTInjector helper now accepts optional auth_user/auth_password and
sends an Authorization: Basic header when configured.
- conftest.py: add --rest-user/--rest-password CLI options and env vars;
update RESTInjector to include Basic auth headers
- test_01_core.py: refactor TestRestInjection to use the authenticated
rest_injector fixture instead of raw aiohttp calls
- tests/README.md + AGENTS.md: document REST_USER/REST_PASSWORD
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- test_02_muc.py: add test_muc_mam_presence_not_archived (negative assertion
that presence stanzas are excluded from MUC MAM) and
test_hidden_lib_rejects_public_override (config form absence + raw override
submission with xfail for admin accounts)
- test_06_postgres.py: add TestPostgresStoreUserInversion which injects via
REST and queries the archive table directly to assert the 'user' column is
the sender; add TestPostgresKickStore and TestPostgresActivityStore for
mod_vnc_track_kicks and mod_vnc_lastactivity store tables
- test_07_module_load.py: new file with smoke tests for the five previously
zero-coverage modules (lastactivity IQ, delfile message hook, remotemucstore
message hook, remotemucinvite event, track_kicks event)
- MANUAL_TESTS.md: expand mod_auth_http_async section with mock-HTTP-server
procedure; add container build verification steps
- AUDIT.md: revise verdict to 'adequate for upgrade safety'; update all
coverage tables to reflect 55 tests across 7 files
Part-of: <http://gitlab.vnc.biz/uxf/vnctalk-prosody/-/merge_requests/3>
- 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>