Skip to content

Commit 363aea6

Browse files
author
Sean Quah
committed
Test device list tracking when we incorrectly believe a user is in the room
1 parent e65bbfd commit 363aea6

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed

tests/federation_room_join_partial_state_test.go

Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2476,6 +2476,265 @@ func TestPartialStateJoin(t *testing.T) {
24762476
mustSyncUntilDeviceListsHas(t, alice, syncToken, "left", server.UserID("elsie"))
24772477
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
24782478
})
2479+
2480+
// setupUserIncorrectlyInRoom tricks the homeserver under test into thinking that @elsie is
2481+
// in the room when they have really been kicked. Once the partial state join completes,
2482+
// @elsie will be discovered to be no longer in the room.
2483+
setupUserIncorrectlyInRoom := func(
2484+
t *testing.T, deployment *docker.Deployment, alice *client.CSAPI,
2485+
server *federation.Server, room *federation.ServerRoom,
2486+
) (syncToken string, psjResult partialStateJoinResult) {
2487+
charlie := server.UserID("charlie")
2488+
derek := server.UserID("derek")
2489+
elsie := server.UserID("elsie")
2490+
fred := server.UserID("fred")
2491+
2492+
// The room starts with @charlie and @derek in it.
2493+
// @charlie makes @fred an admin.
2494+
// @charlie makes @derek a moderator.
2495+
var powerLevelsContent map[string]interface{}
2496+
json.Unmarshal(room.CurrentState("m.room.power_levels", "").Content(), &powerLevelsContent)
2497+
powerLevelsContent["users"].(map[string]interface{})[derek] = 50
2498+
powerLevelsContent["users"].(map[string]interface{})[fred] = 100
2499+
room.AddEvent(server.MustCreateEvent(t, room, b.Event{
2500+
Type: "m.room.power_levels",
2501+
StateKey: b.Ptr(""),
2502+
Sender: charlie,
2503+
Content: powerLevelsContent,
2504+
}))
2505+
2506+
// @fred joins and leaves the room.
2507+
fredJoinEvent := createJoinEvent(t, server, room, fred)
2508+
room.AddEvent(fredJoinEvent)
2509+
fredLeaveEvent := createLeaveEvent(t, server, room, fred)
2510+
room.AddEvent(fredLeaveEvent)
2511+
2512+
// @alice:hs1 joins the room.
2513+
psjResult = beginPartialStateJoin(t, server, room, alice)
2514+
2515+
// @elsie joins the room.
2516+
joinEvent := createJoinEvent(t, server, room, elsie)
2517+
room.AddEvent(joinEvent)
2518+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{joinEvent.JSON()}, nil)
2519+
syncToken = awaitEventViaSync(t, alice, room.RoomID, joinEvent.EventID(), "")
2520+
2521+
// @fred "bans" @derek.
2522+
// This is incorrectly accepted, since the homeserver under test does not know whether
2523+
// @fred is really in the room.
2524+
// This event has to be a ban, rather than a kick, otherwise state resolution can bring
2525+
// @derek back into the room and ruin the test setup.
2526+
badKickEvent := server.MustCreateEvent(t, room, b.Event{
2527+
Type: "m.room.member",
2528+
StateKey: b.Ptr(derek),
2529+
Sender: fred,
2530+
Content: map[string]interface{}{"membership": "ban"},
2531+
AuthEvents: room.EventIDsOrReferences([]*gomatrixserverlib.Event{
2532+
room.CurrentState("m.room.create", ""),
2533+
room.CurrentState("m.room.power_levels", ""),
2534+
fredJoinEvent,
2535+
}),
2536+
})
2537+
room.Timeline = append(room.Timeline, badKickEvent)
2538+
room.Depth = badKickEvent.Depth()
2539+
room.ForwardExtremities = []string{badKickEvent.EventID()}
2540+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{badKickEvent.JSON()}, nil)
2541+
syncToken = awaitEventViaSync(t, alice, room.RoomID, badKickEvent.EventID(), syncToken)
2542+
2543+
// @derek kicks @elsie.
2544+
// This is incorrectly rejected since the homeserver under test incorrectly thinks
2545+
// @derek had been kicked from the room.
2546+
kickEvent := server.MustCreateEvent(t, room, b.Event{
2547+
Type: "m.room.member",
2548+
StateKey: b.Ptr(elsie),
2549+
Sender: derek,
2550+
Content: map[string]interface{}{"membership": "leave"},
2551+
})
2552+
room.AddEvent(kickEvent)
2553+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{kickEvent.JSON()}, nil)
2554+
2555+
// Ensure that the kick event has been persisted.
2556+
sentinelEvent := psjResult.CreateMessageEvent(t, "charlie", nil)
2557+
room.AddEvent(sentinelEvent)
2558+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{sentinelEvent.JSON()}, nil)
2559+
syncToken = awaitEventViaSync(t, alice, room.RoomID, sentinelEvent.EventID(), syncToken)
2560+
2561+
// Check that the last kick was incorrectly rejected.
2562+
must.MatchResponse(t,
2563+
alice.DoFunc(t, "GET", []string{"_matrix", "client", "r0", "rooms", room.RoomID, "event", kickEvent.EventID()}),
2564+
match.HTTPResponse{
2565+
StatusCode: 404,
2566+
JSON: []match.JSON{
2567+
match.JSONKeyEqual("errcode", "M_NOT_FOUND"),
2568+
},
2569+
},
2570+
)
2571+
2572+
return syncToken, psjResult
2573+
}
2574+
2575+
// test that device lists stop being tracked when it is discovered that a remote user is not
2576+
// in a room once a partial state join completes.
2577+
t.Run("Device list no longer tracked for user incorrectly believed to be in room", func(t *testing.T) {
2578+
alice, server, userDevicesChannel, room, _, cleanup := setupDeviceListCachingTest(t, deployment, "t36alice")
2579+
defer cleanup()
2580+
2581+
// The room starts with @charlie and @derek in it.
2582+
// @charlie leaves the room.
2583+
// @t36alice:hs1 joins the room.
2584+
// @elsie joins the room.
2585+
// @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2586+
// @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2587+
_, psjResult := setupUserIncorrectlyInRoom(t, deployment, alice, server, room)
2588+
defer psjResult.Destroy()
2589+
// @elsie is now incorrectly believed to be in the room.
2590+
2591+
// The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2592+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2593+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2594+
2595+
// Finish the partial state join.
2596+
// The homeserver under test will discover that @elsie was actually not in the room.
2597+
psjResult.FinishStateRequest()
2598+
awaitPartialStateJoinCompletion(t, room, alice)
2599+
2600+
// @elsie's device list ought to no longer be cached.
2601+
// `device_lists.left` is not working yet: https://github.com/matrix-org/synapse/issues/13886
2602+
// mustSyncUntilDeviceListsHas(t, alice, syncToken, "left", server.UserID("elsie"))
2603+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2604+
})
2605+
2606+
// test that cached device lists are flushed when it is discovered that a remote user was
2607+
// not in a room the whole time once a partial state join completes.
2608+
t.Run("Device list tracking for user incorrectly believed to be in room when they rejoin before partial state join completes", func(t *testing.T) {
2609+
// Tracked in https://github.com/matrix-org/synapse/issues/13887.
2610+
t.Skip("This edge case is being ignored for now.")
2611+
2612+
alice, server, userDevicesChannel, room, _, cleanup := setupDeviceListCachingTest(t, deployment, "t37alice")
2613+
defer cleanup()
2614+
2615+
// The room starts with @charlie and @derek in it.
2616+
// @charlie leaves the room.
2617+
// @t37alice:hs1 joins the room.
2618+
// @elsie joins the room.
2619+
// @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2620+
// @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2621+
syncToken, psjResult := setupUserIncorrectlyInRoom(t, deployment, alice, server, room)
2622+
defer psjResult.Destroy()
2623+
// @elsie is now incorrectly believed to be in the room.
2624+
2625+
// The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2626+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2627+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2628+
2629+
// @elsie rejoins the room.
2630+
joinEvent := createJoinEvent(t, server, room, server.UserID("elsie"))
2631+
room.AddEvent(joinEvent)
2632+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{joinEvent.JSON()}, nil)
2633+
awaitEventViaSync(t, alice, room.RoomID, joinEvent.EventID(), syncToken)
2634+
2635+
// @elsie's device list is still cached.
2636+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2637+
2638+
// Finish the partial state join.
2639+
// The homeserver under test will discover that there was a period where @elsie was
2640+
// actually not in the room.
2641+
psjResult.FinishStateRequest()
2642+
awaitPartialStateJoinCompletion(t, room, alice)
2643+
2644+
// @elsie's device list ought to have been flushed from the cache.
2645+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2646+
})
2647+
2648+
// test that device lists stop being tracked when it is discovered that a remote user is not
2649+
// in a room once a partial state join completes.
2650+
// Similar to a previous test, except @elsie rejoins the room after the partial state join
2651+
// completes, so that their device list is being tracked again at the time we test the
2652+
// device list cache.
2653+
t.Run("Device list tracking for user incorrectly believed to be in room when they rejoin after partial state join completes", func(t *testing.T) {
2654+
alice, server, userDevicesChannel, room, _, cleanup := setupDeviceListCachingTest(t, deployment, "t38alice")
2655+
defer cleanup()
2656+
2657+
// The room starts with @charlie and @derek in it.
2658+
// @charlie leaves the room.
2659+
// @t38alice:hs1 joins the room.
2660+
// @elsie joins the room.
2661+
// @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2662+
// @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2663+
syncToken, psjResult := setupUserIncorrectlyInRoom(t, deployment, alice, server, room)
2664+
defer psjResult.Destroy()
2665+
// @elsie is now incorrectly believed to be in the room.
2666+
2667+
// The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2668+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2669+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2670+
2671+
// Finish the partial state join.
2672+
// The homeserver under test will discover that @elsie was actually not in the room.
2673+
psjResult.FinishStateRequest()
2674+
awaitPartialStateJoinCompletion(t, room, alice)
2675+
// `device_lists.left` is not working yet: https://github.com/matrix-org/synapse/issues/13886
2676+
// mustSyncUntilDeviceListsHas(t, alice, syncToken, "left", server.UserID("elsie"))
2677+
2678+
// @elsie rejoins the room.
2679+
joinEvent := createJoinEvent(t, server, room, server.UserID("elsie"))
2680+
room.AddEvent(joinEvent)
2681+
server.MustSendTransaction(t, deployment, "hs1", []json.RawMessage{joinEvent.JSON()}, nil)
2682+
awaitEventViaSync(t, alice, room.RoomID, joinEvent.EventID(), syncToken)
2683+
2684+
// @elsie's device list ought to have been flushed from the cache.
2685+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2686+
})
2687+
2688+
// test that cached device lists are flushed when it is discovered that a remote user did
2689+
// not share a room the whole time once a partial state join completes.
2690+
t.Run("Device list tracking for user incorrectly believed to be in room when they join another shared room before partial state join completes", func(t *testing.T) {
2691+
// Tracked in https://github.com/matrix-org/synapse/issues/13887.
2692+
t.Skip("This edge case is being ignored for now.")
2693+
2694+
alice, server, userDevicesChannel, room, _, cleanup := setupDeviceListCachingTest(t, deployment, "t39alice")
2695+
defer cleanup()
2696+
2697+
// The room starts with @charlie and @derek in it.
2698+
// @charlie leaves the room.
2699+
// @t39alice:hs1 joins the room.
2700+
// @elsie joins the room.
2701+
// @charlie "kicks" @derek, which the homeserver under test incorrectly accepts.
2702+
// @derek kicks @elsie, which the homeserver under test incorrectly rejects.
2703+
syncToken, psjResult := setupUserIncorrectlyInRoom(t, deployment, alice, server, room)
2704+
defer psjResult.Destroy()
2705+
// @elsie is now incorrectly believed to be in the room.
2706+
2707+
// The homeserver under test incorrectly thinks it is subscribed to @elsie's device list updates.
2708+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2709+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2710+
2711+
// @t39alice:hs1 creates a public room.
2712+
otherRoomID := alice.CreateRoom(t, map[string]interface{}{"preset": "public_chat"})
2713+
2714+
// @elsie joins the room.
2715+
// The homeserver under test is now subscribed to @elsie's device list updates.
2716+
server.MustJoinRoom(t, deployment, "hs1", otherRoomID, server.UserID("elsie"))
2717+
alice.MustSyncUntil(t,
2718+
client.SyncReq{
2719+
Since: syncToken,
2720+
Filter: buildLazyLoadingSyncFilter(nil),
2721+
},
2722+
client.SyncJoinedTo(server.UserID("elsie"), otherRoomID),
2723+
)
2724+
2725+
// The cache device list for @elsie is stale, but the homeserver does not know that yet.
2726+
mustQueryKeysWithoutFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2727+
2728+
// Finish the partial state join.
2729+
// The homeserver under test will discover that @elsie was actually not in the room, and
2730+
// so did not share a room the whole time.
2731+
psjResult.FinishStateRequest()
2732+
awaitPartialStateJoinCompletion(t, room, alice)
2733+
2734+
// @elsie's device list ought to be evicted from the cache.
2735+
mustSyncUntilDeviceListsHas(t, alice, syncToken, "changed", server.UserID("elsie"))
2736+
mustQueryKeysWithFederationRequest(t, alice, userDevicesChannel, server.UserID("elsie"))
2737+
})
24792738
})
24802739
}
24812740

0 commit comments

Comments
 (0)