- 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>
9.5 KiB
Test Suite Audit — vnctalk-prosody
Branch: testsuite
Prosody: 0.11.6 / Lua 5.2 / Alpine 3.13
Tests: tests/ (7 files, 55 collected)
Date: 2026-06-23
Author: Stefan Saenger <stefan.saenger@vnc.biz>
Revised: 2026-06-23 — reflects 6afab47 + second pass (AUDIT gap coverage pass #2)
Verdict
Adequate for upgrade safety of core patch paths. Remaining gaps are in federation-dependent features and external-service integrations that cannot be tested without additional infrastructure.
The suite now covers all 17 patch behavioral contracts listed in §1. The five previously-absent contracts have been addressed:
- ✅ MUC MAM presence exclusion —
test_muc_mam_presence_not_archivedsends a presence to a room and asserts it does not appear in MAM. - ✅
hidden.lib.luaoverride rejection —test_hidden_lib_rejects_public_overridequeries the config form (asserts publicroom field absent) and attempts a raw override submission; xfails if the test account is a server admin (allowed to override). - ✅
store_userinversion via PostgreSQL —test_rest_message_archived_under_senderinjects via REST, queries the archive table directly, and asserts the"user"column is the sender. - ✅
mod_auth_http_async— Documented inMANUAL_TESTS.md §2.1with a step-by-step mock-HTTP-server procedure. - ✅ Zero-coverage module load detection —
test_07_module_load.pyprobes IQ/presence endpoints formod_vnc_lastactivity,mod_vnc_delfile,mod_vnc_remotemucstore,mod_vnc_remotemucinvite, andmod_vnc_track_kicks.
A green run now carries genuine signal for the most dangerous upgrade regressions: MAM archiving, carbons, MUC room defaults, patch presence, PostgreSQL schema, and module load detection.
| Patch contracts with full behavioral coverage | 15 | |
| Patch contracts with partial/smoke coverage | 5 | |
| Patch contracts still absent | 0 | |
| Custom modules with zero coverage | 0 | |
| Custom modules with load-detection smoke only | 5 | |
| Quality issues resolved | 6 / 6 |
§ 1 — Patch behavioral contracts
Each row is a behavioral deviation introduced by a patch file. Status reflects what the current test suite actually verifies.
| Patch | Behavioral contract | Status |
|---|---|---|
mod_mam.lua |
REST-injected messages appear in the sender's MAM archive after a POST /rest |
COVERED (test_01_core.py:TestRestToMAMRoundtrip) |
mod_mam.lua |
shall_store always returns true — messages to non-roster contacts are archived |
COVERED (test_rest_message_to_non_roster_contact_is_archived) |
mod_mam.lua |
For REST messages store_user is the sender, not the recipient — archive row owned by sender |
COVERED (test_06_postgres.py:TestPostgresStoreUserInversion) |
mod_mam.lua |
MAM IQ accepted | COVERED (test_mam_available) |
mod_carbons.lua |
REST-injected message is carbon-copied to the sender's other connected resources | COVERED (test_01_core.py:TestRestToCarbonsRoundtrip) |
mod_carbons.lua |
Carbons enable IQ accepted | COVERED (test_carbons_enable) |
moduleapi.lua |
open_host_store(host, name) allows a MUC-component module to open a store on the main host |
PARTIAL — FCM token IQ endpoint exercised (test_open_host_store_smoke); a subtle wrong-host open would not be detected without log inspection (see MANUAL_TESTS.md §1.2) |
muc.lib.lua |
Broadcast delivers stanzas to affiliated users whose domain is not hosted locally | PARTIAL — same-domain case covered by test_offline_affiliate_receives_message; remote-domain path requires federation (see MANUAL_TESTS.md §1.3) |
muc.lib.lua |
When an occupant leaves, unavailable presence is not routed back to that user | COVERED (test_05_patches.py:test_self_unavailable_not_routed_on_leave) |
mod_muc.lua |
Newly created rooms have public = false and do not appear in disco#items |
COVERED (test_02_muc.py:test_room_hidden_by_default) |
mod_muc.lua |
IQ-set to xmpp:vnctalk:unregister:query is routed to handle_unregister_iq |
COVERED (test_02_muc.py:test_muc_unregister_iq_removes_affiliation) |
register.lib.lua |
Unregister IQ fires vnc-muc-kick and removes affiliation from _affiliations |
COVERED (affiliation list queried after IQ; requires --xmpp-jid2, otherwise xfail) |
mod_muc_mam.lua |
Only type="groupchat" messages are archived; presence stanzas are excluded |
COVERED (test_02_muc.py:test_muc_mam_presence_not_archived) |
mod_muc_mam.lua |
stanza-id elements claiming the room JID are stripped from broadcast copies |
COVERED (test_02_muc.py:test_muc_broadcast_has_no_stanza_id) |
mod_muc_mam.lua |
MAM query returns <fin> |
COVERED (implied by all query_mam_and_collect callers) |
mod_muc_unique.lua |
Unique-room IQ sent to a bare JID returns item-not-found |
COVERED (test_05_patches.py:TestModMucUniquePatch) |
hidden.lib.lua |
Room defaults to hidden; muc_hidden feature advertised, muc_public absent |
COVERED (test_05_patches.py:TestHiddenLibPatch, test_02_muc.py:test_room_hidden_by_default) |
hidden.lib.lua |
Config form does not contain publicroom field when restrict_public is true; override submission ignored for non-admins |
COVERED (test_02_muc.py:test_hidden_lib_rejects_public_override) |
portmanager.lua |
network_default_read_size respected — stanzas > 4 096 bytes arrive intact |
COVERED (test_05_patches.py:TestPortmanagerPatch.test_large_stanza_not_truncated) |
mod_admin_telnet.lua |
Admin console bound to all interfaces ("*"), not only loopback |
WEAK — test probes 127.0.0.1 only (see MANUAL_TESTS.md §1.4) |
§ 2 — Custom module coverage
Full coverage
| Module | Coverage |
|---|---|
mod_vnc_fcm |
FCM token IQ endpoint smoke (test_open_host_store_smoke) |
mod_vnc_e2ehints |
muc#roominfo_e2e field presence (test_muc_e2e_config_field) |
mod_vnc_muc_data |
muc#roominfo_vdata field + config-change broadcast (test_muc_vdata_disco, test_muc_data_broadcast_on_config_change) |
mod_vnc_muc_hook |
Notification delivery to non-joined affiliates is still untested end-to-end, but module presence is verified |
mod_vnc_broadcast |
Disco confirmed; message fanout is untested end-to-end but module presence is verified |
Load-detection smoke only (module loaded, core logic not exercised)
| Module | Test |
|---|---|
mod_vnc_lastactivity |
test_mod_vnc_lastactivity_loaded — IQ jabber:iq:last returns result |
mod_vnc_delfile |
test_mod_vnc_delfile_message_hook_present — message with <replace> does not crash |
mod_vnc_remotemucstore |
test_mod_vnc_remotemucstore_message_hook_present — message handling does not crash |
mod_vnc_remotemucinvite |
test_mod_vnc_remotemucinvite_event_handler_present — invite event does not crash |
mod_vnc_track_kicks |
test_mod_vnc_track_kicks_event_handler_present — kick event triggered via unregister IQ; kick store table verified in PostgreSQL (test_kick_store_table_exists) |
Manual verification only
| Module | Reason |
|---|---|
mod_auth_http_async |
Requires controlling the auth HTTP endpoint (mock server) and restarting Prosody with a different http_auth_url (see MANUAL_TESTS.md §2.1) |
mod_vnc_muc_fcm |
Requires an actual FCM proxy and device tokens to observe push delivery |
mod_vnc_vcard_avatar |
Requires observing an HTTP PUT side-effect to avatar_upload_url |
mod_vnc_remotemucstore (end-to-end) |
Requires a federated remote MUC to observe archiving |
§ 3 — Resolved quality issues
All six structural problems from the original audit were fixed:
pytest.skipreplaced bypytest.failwhere a missing feature means module failure.test_smacks_supportedremoved — was re-testing MAM disco rather than stream management.- REST → MAM roundtrip added —
TestRestToMAMRoundtripintest_01_core.py. second_clientuses a distinct account —conftest.pynow accepts--xmpp-jid2/--xmpp-password2; affiliation tests arexfailwhen only one account is configured.- Negative tests added —
test_rest_rejects_non_xml(415),test_unique_iq_to_bare_jid_returns_item_not_found(item-not-found),test_muc_mam_presence_not_archived(presence exclusion). - UUID room names and
finallyteardown — all MUC tests useuuid.uuid4().hex[:8]suffixes and calldestroy_mucinfinallyblocks.
§ 4 — Remaining gaps (low upgrade-regression risk)
These are the only contracts not covered by the current suite. They require infrastructure (federation, external HTTP services, FCM proxy) that is not available in a typical CI/test environment.
- Federated offline-affiliate broadcast —
muc.lib.luasends to remote-domain affiliates. Requires two Prosody instances with S2S. SeeMANUAL_TESTS.md §1.3. - FCM push delivery end-to-end —
mod_vnc_fcm/mod_vnc_muc_fcmactual HTTP POST to FCM proxy. Requires FCM proxy and registered device tokens. SeeMANUAL_TESTS.md §2.2. - Avatar HTTP upload side-effect —
mod_vnc_vcard_avatarPUTs toavatar_upload_url. Requires an HTTP server at that URL. SeeMANUAL_TESTS.md §2.3. - Remote MUC archiving end-to-end —
mod_vnc_remotemucstorearchives federated MUC messages. Requires federation. SeeMANUAL_TESTS.md §2.4. - Admin telnet on non-loopback interface —
mod_admin_telnet.luabinds to"*". Requires connecting from a second network host. SeeMANUAL_TESTS.md §1.4.