Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Synthetic presence leave #649

Merged
merged 7 commits into from
Jul 25, 2024
2 changes: 1 addition & 1 deletion ably/proto_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ type Message struct {
// ClientID is of the publisher of this message (RSL1g1, TM2b).
ClientID string `json:"clientId,omitempty" codec:"clientId,omitempty"`
// ConnectionID of the publisher of this message (TM2c).
ConnectionID string `json:"connectionId,omitempty" codec:"connectionID,omitempty"`
ConnectionID string `json:"connectionId,omitempty" codec:"connectionId,omitempty"`
sacOO7 marked this conversation as resolved.
Show resolved Hide resolved
// Deprecated: This attribute is deprecated and will be removed in future versions
// ConnectionKey is a connectionKey of the active connection.
ConnectionKey string `json:"connectionKey,omitempty" codec:"connectionKey,omitempty"`
Expand Down
4 changes: 2 additions & 2 deletions ably/proto_presence_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,13 @@ type PresenceMessage struct {
}

func (m PresenceMessage) String() string {
return fmt.Sprintf("<PresenceMessage %v clientID=%v data=%v>", [...]string{
return fmt.Sprintf("<PresenceMessage %v id=%v connectionId=%v clientID=%v data=%v>", [...]string{
"absent",
"present",
"enter",
"leave",
"update",
}[m.Action], m.ClientID, m.Data)
}[m.Action], m.ID, m.ConnectionID, m.ClientID, m.Data)
}

func (msg *PresenceMessage) isServerSynthesized() bool {
Expand Down
4 changes: 2 additions & 2 deletions ably/proto_protocol_message.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,8 @@ func (msg *protocolMessage) String() string {
case actionDetached:
return fmt.Sprintf("(action=%q, channel=%q)", msg.Action, msg.Channel)
case actionPresence, actionSync:
return fmt.Sprintf("(action=%q, id=%q, channel=%q, timestamp=%d, presenceMessages=%v)",
msg.Action, msg.ConnectionID, msg.Channel, msg.Timestamp, msg.Presence)
return fmt.Sprintf("(action=%q, id=%v, channel=%q, timestamp=%d, connectionId=%v, presenceMessages=%v)",
msg.Action, msg.ID, msg.Channel, msg.Timestamp, msg.ConnectionID, msg.Presence)
case actionMessage:
return fmt.Sprintf("(action=%q, id=%q, messages=%v)", msg.Action,
msg.ConnectionID, msg.Messages)
Expand Down
113 changes: 113 additions & 0 deletions ably/realtime_presence_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,119 @@ func TestRealtimePresence_EnsureChannelIsAttached(t *testing.T) {
assert.NoError(t, err)
}

func TestRealtimePresence_Presence_Enter_Update_Leave(t *testing.T) {
app, client1 := ablytest.NewRealtime(nil...)
defer safeclose(t, ablytest.FullRealtimeCloser(client1), app)

client2 := app.NewRealtime(ably.WithClientID("client2"))
defer safeclose(t, ablytest.FullRealtimeCloser(client2))

err := ablytest.Wait(ablytest.ConnWaiter(client1, client1.Connect, ably.ConnectionEventConnected), nil)
assert.NoError(t, err)
err = ablytest.Wait(ablytest.ConnWaiter(client2, client2.Connect, ably.ConnectionEventConnected), nil)
assert.NoError(t, err)

channel1 := client1.Channels.Get("channel")
err = channel1.Attach(context.Background())
assert.NoError(t, err)

channel2 := client2.Channels.Get("channel")
err = channel2.Attach(context.Background())
assert.NoError(t, err)

subCh1, unsub1, err := ablytest.ReceivePresenceMessages(channel1, nil)
assert.NoError(t, err)
defer unsub1()

// ENTER
err = channel2.Presence.Enter(context.Background(), "enter client2")
assert.NoError(t, err)

member_received := <-subCh1
assert.Len(t, subCh1, 0) // Ensure no more updates received

assert.Equal(t, ably.PresenceActionEnter, member_received.Action)
assert.Equal(t, "client2", member_received.ClientID)
assert.Equal(t, "enter client2", member_received.Data)

// UPDATE
err = channel2.Presence.Update(context.Background(), "update client2")
assert.NoError(t, err)

member_received = <-subCh1
assert.Len(t, subCh1, 0) // Ensure no more updates received

assert.Equal(t, ably.PresenceActionUpdate, member_received.Action)
assert.Equal(t, "client2", member_received.ClientID)
assert.Equal(t, "update client2", member_received.Data)

// LEAVE
err = channel2.Presence.Leave(context.Background(), "leave client2")
assert.NoError(t, err)

member_received = <-subCh1
assert.Len(t, subCh1, 0) // Ensure no more updates received

assert.Equal(t, ably.PresenceActionLeave, member_received.Action)
assert.Equal(t, "client2", member_received.ClientID)
assert.Equal(t, "leave client2", member_received.Data)
}

func TestRealtimePresence_ServerSynthesized_Leave(t *testing.T) {
app, client1 := ablytest.NewRealtime(nil...)
defer safeclose(t, ablytest.FullRealtimeCloser(client1), app)

client2 := app.NewRealtime(ably.WithClientID("client2"))
defer safeclose(t, ablytest.FullRealtimeCloser(client2))

err := ablytest.Wait(ablytest.ConnWaiter(client1, client1.Connect, ably.ConnectionEventConnected), nil)
assert.NoError(t, err)

err = ablytest.Wait(ablytest.ConnWaiter(client2, client2.Connect, ably.ConnectionEventConnected), nil)
assert.NoError(t, err)

client1Channel := client1.Channels.Get("channel")
err = client1Channel.Attach(context.Background())

assert.NoError(t, err)

client2Channel := client2.Channels.Get("channel")
err = client2Channel.Attach(context.Background())
assert.NoError(t, err)

subCh1, unsub1, err := ablytest.ReceivePresenceMessages(client1Channel, nil)
assert.NoError(t, err)
defer unsub1()

// ENTER
err = client2Channel.Presence.Enter(context.Background(), "enter client2")
assert.NoError(t, err)

member_received := <-subCh1
assert.Len(t, subCh1, 0) // Ensure no more updates received
assert.Equal(t, ably.PresenceActionEnter, member_received.Action)
assert.Equal(t, "client2", member_received.ClientID)
assert.Equal(t, "enter client2", member_received.Data)

members, err := client1Channel.Presence.Get(context.Background())
assert.NoError(t, err)
assert.Len(t, members, 1)

// Server Synthesized Leave when client2 disconnects
client2.Close()

member_received = <-subCh1
assert.Len(t, subCh1, 0) // Ensure no more updates received

assert.Equal(t, ably.PresenceActionLeave, member_received.Action)
assert.Equal(t, "client2", member_received.ClientID)

// Make sure no members are present on the channel
members, err = client1Channel.Presence.Get(context.Background())
assert.NoError(t, err)
assert.Empty(t, members)
}

// When a client is created with a ClientID, Enter is used to announce the client's presence.
// This example shows Client A entering their presence.
func ExampleRealtimePresence_Enter() {
Expand Down
Loading