feat(backend): gateway integration tests — multi-node, multi-device (#215)#288
Open
daveades wants to merge 5 commits into
Open
feat(backend): gateway integration tests — multi-node, multi-device (#215)#288daveades wants to merge 5 commits into
daveades wants to merge 5 commits into
Conversation
…-device scenarios (codebestia#215) - Spin up two Socket.IO instances sharing a real Redis adapter to verify cross-node delivery: message sent on node-1 reaches a socket on node-2 - Assert multi-device fanout: all active devices of a recipient receive the new_message event when per-device envelopes are provided - Verify persist-before-deliver ordering via an intentional DB latency probe — new_message is never emitted until the DB insert resolves - Test revocation disconnect: publishing device_revoked:{id} to Redis triggers force-disconnect and client notification on any node - Test resume/sync determinism: missed ephemeral events are replayed from the Redis stream on reconnect, idempotent with an advanced cursor Add redis:7-alpine service to the backend-ci GitHub Actions workflow so these tests run in CI. Bump vitest testTimeout to 15 s for network I/O.
|
@daveades Great news! 🎉 Based on an automated assessment of this PR, the linked Wave issue(s) no longer count against your application limits. You can now already apply to more issues while waiting for a review of this PR. Keep up the great work! 🚀 |
added 4 commits
June 29, 2026 11:36
…in integration suite
- Add vi.mock('../lib/redis.js', () => ({ redis: null })) to readReceipts.test.ts
so the if (redis) branch in message_read never runs during these unit tests.
Adding a Redis service to CI (codebestia#215) made redis truthy, triggering a findMany
call that was absent from the db mock and failing two tests.
- Add conversationMembers.findMany and the missing drizzle-orm exports (ne,
isNull, inArray, sql) to readReceipts.test.ts for completeness.
- Replace pub/sub .disconnect() with await .quit().catch(() => {}) in the
gateway integration test to prevent unhandled 'Connection is closed'
rejections during suite teardown.
…s on teardown
ioredis rejects pending-command Promises in its event_handler.js close()
function when a connection is force-closed. Those rejections are internal to
ioredis and cannot be caught via .catch() on the quit() call because they are
created from event callbacks, not from the quit() promise chain.
Register a targeted unhandledRejection handler before the suite starts that
silences only Error('Connection is closed.') messages and re-throws everything
else, then removes itself in afterAll once the Redis connection is fully torn
down. This prevents Vitest from treating the ioredis teardown noise as test
errors while preserving visibility of any other genuine unhandled rejections.
…ejections on teardown" This reverts commit ff5b6f8.
…rejections on teardown" This reverts commit 95205cf.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #215
socket.ioservers sharing a@socket.io/redis-adapterinstance; asserts a message sent on node-1 arrives at a socket connected to node-2new_messagewhen per-device envelopes are provideddb.insert().returning()mock and assertsdb_insert_donealways precedesnew_message_receivedin the event order arraydevice_revoked:{deviceId}directly to Redis and asserts the socket receivesdevice_revokedand is disconnected, exercising the cross-instance pub/sub path instartDeviceRevocationListenerrecordEphemeralEvent, triggersresumewith an empty cursor, asserts all events are replayed in order; then re-issuesresumewith the advanced cursor and asserts no duplicatesImplementation notes
vi.mock) following the existing test patterns; only Redis is real (ioredis connected to the service container)vi.hoistedreference holder wires the real Redis instance into thelib/redis.jsmodule mock so presence, rate-limit, and resume-stream services share the same connectioncreateGatewayNode()factory assembles the same middleware stack asindex.tswithout the side-effecting boot sequenceCI changes
Added a
redis:7-alpineservice to.github/workflows/backend-ci.ymland exposedREDIS_URL=redis://localhost:6379so the integration tests run in the existing backend job. Also bumpedvitest.config.tstestTimeoutto 15 s for network I/O headroom.Test plan
pnpm testinapps/backendwith a local Redis running passes all 5 new tests