Skip to content

Commit a4e0351

Browse files
Check sender of ENTER presence message in space.enter()
It shouldn’t return as a result of somebody else entering the space. I’m not sure whether this is the best way of predicating whether or not to return — perhaps checking for some unique ID on the received ENTER presence message would be better. But it seems like an OK low-thought solution at least for the time being.
1 parent 1431c3c commit a4e0351

File tree

2 files changed

+71
-2
lines changed

2 files changed

+71
-2
lines changed

src/Space.test.ts

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,9 @@ describe('Space', () => {
6363
beforeEach<SpaceTestContext>(({ presence }) => {
6464
vi.spyOn(presence, 'subscribe').mockImplementation(
6565
async (_, listener?: (presenceMessage: Types.PresenceMessage) => void) => {
66-
listener!(createPresenceMessage('enter' /* arbitrarily chosen */));
66+
listener!(
67+
createPresenceMessage('enter' /* arbitrarily chosen */, { clientId: 'MOCK_CLIENT_ID', connectionId: '1' }),
68+
);
6769
},
6870
);
6971
});
@@ -74,6 +76,64 @@ describe('Space', () => {
7476
expect(spy).toHaveBeenNthCalledWith(1, createProfileUpdate({ current: { name: 'Betty' } }));
7577
});
7678

79+
describe.each([
80+
{
81+
scenario: 'when it receives a presence message from a client ID and connection ID that matches ours',
82+
presenceMessageData: { clientId: 'MOCK_CLIENT_ID', connectionId: '1' },
83+
shouldComplete: true,
84+
},
85+
{
86+
scenario: 'when it receives a presence message from a client ID that isn’t ours',
87+
presenceMessageData: { clientId: 'OTHER_MOCK_CLIENT_ID', connectionId: '1' },
88+
shouldComplete: false,
89+
},
90+
{
91+
scenario: 'when it receives a presence message from a connection ID that isn’t ours',
92+
presenceMessageData: { clientId: 'MOCK_CLIENT_ID', connectionId: '2' },
93+
shouldComplete: false,
94+
},
95+
])('$scenario', ({ presenceMessageData, shouldComplete }) => {
96+
it<SpaceTestContext>(shouldComplete ? 'completes' : 'does not complete', async ({ space, presence }) => {
97+
const unsubscribeSpy = vi.spyOn(presence, 'unsubscribe');
98+
99+
vi.spyOn(presence, 'subscribe').mockImplementation(
100+
async (_, listener?: (presenceMessage: Types.PresenceMessage) => void) => {
101+
listener!(createPresenceMessage('enter' /* arbitrarily chosen */, presenceMessageData));
102+
},
103+
);
104+
105+
space.enter();
106+
107+
// Note: There’s no nice way (i.e. without timeouts) to actually check that space.enter() didn’t complete, so we use "did it remove its presence listener?" as a proxy for "did it complete?"
108+
if (shouldComplete) {
109+
expect(unsubscribeSpy).toHaveBeenCalled();
110+
} else {
111+
expect(unsubscribeSpy).not.toHaveBeenCalled();
112+
}
113+
});
114+
});
115+
116+
it<SpaceTestContext>('doesn’t complete as a result of a presence message from a client ID that is not ours', async ({
117+
space,
118+
presence,
119+
}) => {
120+
const unsubscribeSpy = vi.spyOn(presence, 'unsubscribe');
121+
122+
vi.spyOn(presence, 'subscribe').mockImplementation(
123+
async (_, listener?: (presenceMessage: Types.PresenceMessage) => void) => {
124+
listener!(
125+
createPresenceMessage('enter' /* arbitrarily chosen */, {
126+
clientId: 'OTHER_MOCK_CLIENT_ID',
127+
connectionId: '1',
128+
}),
129+
);
130+
},
131+
);
132+
133+
space.enter();
134+
expect(unsubscribeSpy).not.toHaveBeenCalled();
135+
});
136+
77137
it<SpaceTestContext>('returns current space members', async ({ presenceMap, space }) => {
78138
presenceMap.set('1', createPresenceMessage('enter'));
79139
presenceMap.set('2', createPresenceMessage('update', { clientId: '2', connectionId: '2' }));

src/Space.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -244,7 +244,16 @@ class Space extends EventEmitter<SpaceEventMap> {
244244
return new Promise((resolve) => {
245245
const presence = this.channel.presence;
246246

247-
const presenceListener = async () => {
247+
const presenceListener = async (presenceMessage: Types.PresenceMessage) => {
248+
if (
249+
!(
250+
presenceMessage.clientId == this.client.auth.clientId &&
251+
presenceMessage.connectionId == this.client.connection.id
252+
)
253+
) {
254+
return;
255+
}
256+
248257
presence.unsubscribe(presenceListener);
249258

250259
const presenceMessages = await presence.get();

0 commit comments

Comments
 (0)