Skip to content

Commit c498e0f

Browse files
Add integration tests for server-initiated disconnect paths
Covers the remaining server-disconnect gaps from docs/connection-lifecycle.md: - DuplicateIdentity: a second connection with the same token disconnects the first with DisconnectReason::DuplicateIdentity (no admin needed) - ParticipantRemoved / RoomDeleted: driven via the lk CLI (room/identity parsed from the join token's JWT payload); skipped unless LIVEKIT_API_KEY/LIVEKIT_API_SECRET are set - Eos teardown (S2): after a server-initiated disconnect, onRoomEos fires, state is released, and onDisconnected does not double-fire - Peer disconnect (S6): onParticipantDisconnected fires with the peer's identity and the cached remote handle expires (needs LIVEKIT_TOKEN_B) - Client-vs-server disconnect race (S1xD1): exactly one onDisconnected regardless of which side wins CI: export dev-server LIVEKIT_API_KEY/SECRET in the integration step so the lk-driven tests run there (lk is already installed in that job). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 7b52bce commit c498e0f

3 files changed

Lines changed: 416 additions & 7 deletions

File tree

.github/workflows/tests.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -271,6 +271,10 @@ jobs:
271271
env:
272272
RUST_LOG: "metrics=debug"
273273
LIVEKIT_CREATE_TOKEN_URL: ${{ steps.token_server.outputs.token-url }}
274+
# Dev-server credentials so the admin-driven server-disconnect tests
275+
# (which shell out to `lk`) run instead of skipping.
276+
LIVEKIT_API_KEY: devkey
277+
LIVEKIT_API_SECRET: secret
274278
run: |
275279
set -euo pipefail
276280
source .token_helpers/set_data_track_test_tokens.bash

docs/connection-lifecycle.md

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,14 +99,16 @@ live server (see `docs/testing.md`).
9999
| D5 | SIGINT/SIGTERM during a session || ❌ none (by design: app responsibility, but undocumented) |
100100
| D6 | Process exit without shutdown (warning path) || ❌ none |
101101
| S1 | Server-initiated `kDisconnected` via `ServerLeave` | `integration/test_room_reconnect.cpp` `ServerLeaveDisconnects` | ✅ (SimulateScenario) |
102-
| S1 | Server-initiated `kDisconnected` (kick, room deletion, duplicate identity) || ❌ none — needs server-admin API or fake injector |
103-
| S2 | `kEos` teardown || ❌ none |
104-
| S1×D1 | Race: server disconnect vs. client disconnect || ❌ none |
102+
| S1 | Duplicate identity → `DuplicateIdentity` | `integration/test_room_server_disconnect.cpp` `DuplicateIdentityDisconnectsFirstConnection` | ✅ (second connect, same token) |
103+
| S1 | Kick → `ParticipantRemoved` | `integration/test_room_server_disconnect.cpp` `RemovedParticipantDisconnectsWithReason` | ✅ (`lk` CLI; needs `LIVEKIT_API_KEY`/`SECRET`) |
104+
| S1 | Room deletion → `RoomDeleted` | `integration/test_room_server_disconnect.cpp` `DeletedRoomDisconnectsWithReason` | ✅ (`lk` CLI; needs `LIVEKIT_API_KEY`/`SECRET`) |
105+
| S2 | `kEos` teardown (state released, `onRoomEos`, no double `onDisconnected`) | `integration/test_room_server_disconnect.cpp` `ServerDisconnectTearsDownViaEos` ||
106+
| S1×D1 | Race: server disconnect vs. client disconnect fires `onDisconnected` once | `integration/test_room_server_disconnect.cpp` `ClientDisconnectDuringServerLeaveFiresDisconnectedOnce` | ✅ (best-effort timing) |
105107
| S3 | Reconnecting/Reconnected callbacks | `integration/test_room_reconnect.cpp` `SignalReconnectResumesSession`, `FullReconnectReestablishesSession` | ✅ (SimulateScenario) |
106108
|| `simulateScenario` on a disconnected room throws | `unit/test_room.cpp` `SimulateScenarioOnDisconnectedRoomThrows` ||
107109
| S4 | ConnectionStateChanged callback || ❌ none |
108110
| S5 | FFI panic → SIGTERM | `unit/test_ffi_client.cpp` (SIGTERM handler + flag) ||
109-
| S6 | Remote participant disconnects | | ❌ none (only reachable with a second participant) |
111+
| S6 | Remote participant disconnects | `integration/test_room_server_disconnect.cpp` `PeerDisconnectFiresParticipantDisconnected` | ✅ (needs `LIVEKIT_TOKEN_B`) |
110112
|| Listener use-after-free during destroy | `unit/test_ffi_client.cpp` FakeRoom race/stress tests ||
111113
|| `connectionState()` thread safety | `unit/test_room_callbacks.cpp` ||
112114

@@ -207,9 +209,13 @@ Two complementary mechanisms, at different layers:
207209
as they prove useful: `NodeFailure`, `Migration`, and `DisconnectSignalOnResume`
208210
(resume→full escalation).
209211

210-
Reason-specific disconnects that `SimulateScenario` does **not** cover
211-
(`ParticipantRemoved`, `RoomDeleted`, `DuplicateIdentity`) still need either the
212-
server-admin API or the fake event injector.
212+
Reason-specific disconnects that `SimulateScenario` does **not** cover are now
213+
handled in `integration/test_room_server_disconnect.cpp`: `DuplicateIdentity` by
214+
opening a second connection with the same token, and `ParticipantRemoved` /
215+
`RoomDeleted` by shelling out to the `lk` CLI (skipped unless
216+
`LIVEKIT_API_KEY`/`LIVEKIT_API_SECRET` are set; CI provides the dev-server
217+
credentials). The same file covers Eos teardown (S2), peer disconnects (S6),
218+
and the client-vs-server disconnect race (S1×D1).
213219

214220
3. Add a test for `livekit::shutdown()` with a still-connected room (D4).
215221
4. Document (and add an example for) signal-driven graceful shutdown (D5).

0 commit comments

Comments
 (0)