public void PresenceTests()
        {
            var             onJoinDict  = new Dictionary <string, Item>();
            var             onLeaveDict = new Dictionary <string, Item>();
            var             onSyncCount = 0;
            OnJoinDelegate  onJoin      = (string id, Item currentItem, Item newItem) => onJoinDict[id] = newItem;
            OnLeaveDelegate onLeave     = (string id, Item currentItem, Item newItem) => onLeaveDict[id] = newItem;
            OnSyncDelegate  onSync      = () => onSyncCount++;

            var(presence1, channel1) = NewPresence("P1", onJoin, onLeave, onSync);
            Assert.That(() => GetPresenceIds(presence1), Is.EquivalentTo(new List <string> {
                "P1"
            }).After(networkDelay));

            var(presence2, channel2) = NewPresence("P2");
            Assert.That(onJoinDict.Keys, Is.EquivalentTo(new List <string> {
                "P1", "P2"
            }).After(networkDelay));
            Assert.That(GetPresenceIds(presence1), Is.EquivalentTo(new List <string> {
                "P1", "P2"
            }));
            Assert.That(GetPresenceIds(presence2), Is.EquivalentTo(new List <string> {
                "P1", "P2"
            }));

            channel2.Leave();
            Assert.That(onLeaveDict.Keys, Is.EquivalentTo(new List <string> {
                "P2"
            }).After(networkDelay));
            Assert.That(GetPresenceIds(presence1), Is.EquivalentTo(new List <string> {
                "P1"
            }));
            // P1:join, P2:join, P3:leave
            Assert.AreEqual(3, onSyncCount);
        }
        private (Presence, Channel) NewPresence(string id, OnJoinDelegate onJoin = null, OnLeaveDelegate onLeave = null, OnSyncDelegate onSync = null)
        {
            var socketFactory = new DotNetWebSocketFactory();
            var socket        = new Socket(socketFactory, new Socket.Options
            {
                channelRejoinInterval = TimeSpan.FromMilliseconds(200),
                logger = new BasicLogger()
            });
            var url = string.Format("ws://{0}/phoenix_sharp_test", host);

            socket.Connect(url);
            Assert.IsTrue(socket.state == Socket.State.Open);
            var channel  = socket.MakeChannel("presence");
            var presence = new Presence(channel);

            if (onJoin != null)
            {
                presence.onJoin = onJoin;
            }
            if (onLeave != null)
            {
                presence.onLeave = onLeave;
            }
            if (onSync != null)
            {
                presence.onSync = onSync;
            }

            Reply?joinOkReply    = null;
            Reply?joinErrorReply = null;

            channel.Join(new Dictionary <string, object> {
                { "id", id }
            })
            .Receive(Reply.Status.Ok, r => joinOkReply       = r)
            .Receive(Reply.Status.Error, r => joinErrorReply = r);
            Assert.That(() => joinOkReply.HasValue, Is.True.After(networkDelay, 10));
            return(presence, channel);
        }