Files
Stefan-Sanger 9be6241a55 test: cover remaining AUDIT gaps — presence exclusion, hidden override, store_user SQL, module load detection
- 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>
2026-07-15 17:58:02 +02:00

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:

  1. MUC MAM presence exclusiontest_muc_mam_presence_not_archived sends a presence to a room and asserts it does not appear in MAM.
  2. hidden.lib.lua override rejectiontest_hidden_lib_rejects_public_override queries 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).
  3. store_user inversion via PostgreSQLtest_rest_message_archived_under_sender injects via REST, queries the archive table directly, and asserts the "user" column is the sender.
  4. mod_auth_http_async — Documented in MANUAL_TESTS.md §2.1 with a step-by-step mock-HTTP-server procedure.
  5. Zero-coverage module load detectiontest_07_module_load.py probes IQ/presence endpoints for mod_vnc_lastactivity, mod_vnc_delfile, mod_vnc_remotemucstore, mod_vnc_remotemucinvite, and mod_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:

  1. pytest.skip replaced by pytest.fail where a missing feature means module failure.
  2. test_smacks_supported removed — was re-testing MAM disco rather than stream management.
  3. REST → MAM roundtrip addedTestRestToMAMRoundtrip in test_01_core.py.
  4. second_client uses a distinct accountconftest.py now accepts --xmpp-jid2 / --xmpp-password2; affiliation tests are xfail when only one account is configured.
  5. Negative tests addedtest_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).
  6. UUID room names and finally teardown — all MUC tests use uuid.uuid4().hex[:8] suffixes and call destroy_muc in finally blocks.

§ 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.

  1. Federated offline-affiliate broadcastmuc.lib.lua sends to remote-domain affiliates. Requires two Prosody instances with S2S. See MANUAL_TESTS.md §1.3.
  2. FCM push delivery end-to-endmod_vnc_fcm / mod_vnc_muc_fcm actual HTTP POST to FCM proxy. Requires FCM proxy and registered device tokens. See MANUAL_TESTS.md §2.2.
  3. Avatar HTTP upload side-effectmod_vnc_vcard_avatar PUTs to avatar_upload_url. Requires an HTTP server at that URL. See MANUAL_TESTS.md §2.3.
  4. Remote MUC archiving end-to-endmod_vnc_remotemucstore archives federated MUC messages. Requires federation. See MANUAL_TESTS.md §2.4.
  5. Admin telnet on non-loopback interfacemod_admin_telnet.lua binds to "*". Requires connecting from a second network host. See MANUAL_TESTS.md §1.4.