public async Task Semaphore_DeleteKey()
        {
            const string keyName = "test/semaphore/deletekey";

            var semaphore = _client.Semaphore(keyName, 2);

            try
            {
                await semaphore.Acquire(CancellationToken.None);

                Assert.True(semaphore.IsHeld);

                var req = await _client.KV.DeleteTree(keyName);

                Assert.True(req.Response);

                await TimeoutUtils.WaitFor(() => !semaphore.IsHeld, "Expected deleting tree to release semaphore");
            }
            finally
            {
                try
                {
                    await semaphore.Release();
                }
                catch (SemaphoreNotHeldException)
                {
                    // Exception expected if checks pass
                }
            }
        }
        public async Task Lock_DeleteKey()
        {
            const string keyName = "test/lock/deletekey";

            var lockKey = (Lock)_client.CreateLock(keyName);

            try
            {
                await lockKey.Acquire(CancellationToken.None);

                Assert.True(lockKey.IsHeld);

                var checker = TimeoutUtils.WaitFor(() => !lockKey.IsHeld, "Expected key delete to release lock");

                await _client.KV.Delete(lockKey.Opts.Key);

                await checker;
            }
            finally
            {
                try
                {
                    await lockKey.Release();

                    await lockKey.Destroy();
                }
                catch (LockNotHeldException)
                {
                    // Exception expected if above checks all pass
                }
            }
        }
        public async Task Lock_ForceInvalidate()
        {
            const string keyName = "test/lock/forceinvalidate";

            var lockKey = (Lock)_client.CreateLock(keyName);

            try
            {
                await lockKey.Acquire(CancellationToken.None);

                Assert.True(lockKey.IsHeld);

                var checker = TimeoutUtils.WaitFor(
                    () => !lockKey.IsHeld,
                    "Expected session destroy to release lock");

                await _client.Session.Destroy(lockKey.LockSession);

                await checker;
            }
            finally
            {
                try
                {
                    await lockKey.Release();

                    await lockKey.Destroy();
                }
                catch (LockNotHeldException)
                {
                    // Exception expected if above checks all pass
                }
            }
        }
        public async Task Lock_EphemeralAcquireRelease()
        {
            const string keyName   = "test/lock/ephemerallock";
            var          sessionId = await _client.Session.Create(new SessionEntry { Behavior = SessionBehavior.Delete });

            var l = await _client.AcquireLock(new LockOptions(keyName) { Session = sessionId.Response }, CancellationToken.None);

            Assert.True(l.IsHeld);
            await _client.Session.Destroy(sessionId.Response);

            await TimeoutUtils.WaitFor(() => !l.IsHeld, "Expected lock to be lost when session destroyed");

            Assert.Null((await _client.KV.Get(keyName)).Response);
        }
        public async Task Lock_ReclaimLock()
        {
            const string keyName = "test/lock/reclaim";

            var sessionRequest = await _client.Session.Create();

            var sessionId = sessionRequest.Response;

            try
            {
                var lock1 = _client.CreateLock(new LockOptions(keyName)
                {
                    Session = sessionId
                });

                var lock2 = _client.CreateLock(new LockOptions(keyName)
                {
                    Session = sessionId
                });

                try
                {
                    await lock1.Acquire(CancellationToken.None);

                    Assert.True(lock1.IsHeld);

                    await TimeoutUtils.WithTimeout(lock2.Acquire(CancellationToken.None));

                    Assert.True(lock2.IsHeld);
                }
                finally
                {
                    await lock1.Release();
                }

                await TimeoutUtils.WaitFor(() => !lock1.IsHeld, "Lock 1 is still held");

                // By releasing lock1, lock2 should also eventually be released as it is for the same session
                await TimeoutUtils.WaitFor(() => !lock2.IsHeld, "Lock 2 is still held");
            }
            finally
            {
                var destroyResponse = await _client.Session.Destroy(sessionId);

                Assert.True(destroyResponse.Response, "Failed to destroy session");
            }
        }