Skip to content

Commit

Permalink
locks: Release all locks when a member leaves
Browse files Browse the repository at this point in the history
Signed-off-by: Lewis Marshall <lewis.marshall@ably.com>
  • Loading branch information
lmars committed Aug 24, 2023
1 parent f33d0d3 commit dfe4fa9
Show file tree
Hide file tree
Showing 2 changed files with 47 additions and 3 deletions.
44 changes: 44 additions & 0 deletions src/Locks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,7 @@ describe('Locks (mockClient)', () => {
const emitSpy = vi.spyOn(space.locks, 'emit');

const msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: member.connectionId,
extras: {
locks: [
Expand Down Expand Up @@ -179,6 +180,7 @@ describe('Locks (mockClient)', () => {
// process a PENDING request for the other member, which should
// transition to LOCKED
let msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: otherConnId,
extras: {
locks: [
Expand All @@ -198,6 +200,7 @@ describe('Locks (mockClient)', () => {
// result matches what is expected
const emitSpy = vi.spyOn(space.locks, 'emit');
msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: client.connection.id,
extras: {
locks: [
Expand Down Expand Up @@ -233,6 +236,7 @@ describe('Locks (mockClient)', () => {
const member = (await space.members.getSelf())!;

let msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: member.connectionId,
extras: {
locks: [
Expand All @@ -249,6 +253,7 @@ describe('Locks (mockClient)', () => {
const emitSpy = vi.spyOn(space.locks, 'emit');

msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: member.connectionId,
extras: undefined,
});
Expand All @@ -258,6 +263,45 @@ describe('Locks (mockClient)', () => {
expect(lock).not.toBeDefined();
expect(emitSpy).toHaveBeenCalledWith('update', lockEvent(member, 'unlocked'));
});

it<SpaceTestContext>('sets all locks to UNLOCKED when a member leaves', async ({ space }) => {
await space.enter();
const member = (await space.members.getSelf())!;

let msg = Realtime.PresenceMessage.fromValues({
action: 'update',
connectionId: member.connectionId,
extras: {
locks: [
{
id: 'lock1',
status: 'pending',
timestamp: Date.now(),
},
{
id: 'lock2',
status: 'pending',
timestamp: Date.now(),
},
],
},
});
await space.locks.processPresenceMessage(msg);
let lock1 = space.locks.get('lock1');
expect(lock1).toBeDefined();
expect(lock1!.member).toEqual(member);
let lock2 = space.locks.get('lock2');
expect(lock2).toBeDefined();
expect(lock2!.member).toEqual(member);

msg.action = 'leave';
await space.locks.processPresenceMessage(msg);

lock1 = space.locks.get('lock1');
expect(lock1).not.toBeDefined();
lock2 = space.locks.get('lock2');
expect(lock2).not.toBeDefined();
});
});

describe('release', () => {
Expand Down
6 changes: 3 additions & 3 deletions src/Locks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -131,9 +131,9 @@ export default class Locks extends EventEmitter<LockEventMap> {
const member = await this.space.members.getByConnectionId(message.connectionId);
if (!member) return;

if (!Array.isArray(message?.extras?.locks)) {
// there are no locks in presence, so release any existing locks for the
// member
if (message.action === 'leave' || !Array.isArray(message?.extras?.locks)) {
// the member has left, or they have no locks in presence, so release any
// existing locks for that member
for (const locks of this.locks.values()) {
const lock = locks.get(member.connectionId);
if (lock) {
Expand Down

0 comments on commit dfe4fa9

Please sign in to comment.