public ElasticLease(
            Guid leasedResourceId, 
            DateTime expires, 
            ElasticLeaseOwner owner, 
            long version = -1)
        {
            if (owner == null)
            {
                throw new ArgumentNullException("owner");
            }

            this._id = leasedResourceId;
            this._expires = expires;
            this._owner = owner;
            this.versioning = new VersioningMixin(version);
        }
        public void TryLoadAndLockById_ContactExistsAndNotLockedWithNoSuccessors_ReturnsContact(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, us, true, ctx)
                    .Should().Be(true);

                var res = contactService.TryLoadAndLock(contact.Id, us, TimeSpan.FromMinutes(1), ctx);

                res.Status.Should().Be(LockAttemptStatus.Success);
                res.LockedObject.ShouldBeEquivalentToContact(contact, l => l.Expires > DateTime.UtcNow && l.IsOwnedBy(us) && l.Version == 3);
            }
        }
        public virtual LockAttemptResult<ElasticContact> TryLoadAndLock(
            Guid id,            
            ElasticLeaseOwner targetLeaseOwner,
            TimeSpan leaseDuration,
            ISystemContext ctx)
        {
            if (targetLeaseOwner == null)
            {
                throw new ArgumentNullException("targetLeaseOwner", "A lease owner must be specified.");
            }

            var contact = this.GetLatest(id, ctx);

            if (contact == null)
            {
                return new LockAttemptResult<ElasticContact>(LockAttemptStatus.NotFound, null, null);
            }

            if (this.TryLockContact(
                    contact,
                    targetLeaseOwner,
                    leaseDuration,
                    ctx) == LockAttemptStatus.Success)
            {
                return new LockAttemptResult<ElasticContact>(
                    LockAttemptStatus.Success,
                    contact,
                    targetLeaseOwner);
            }
            else
            {
                return new LockAttemptResult<ElasticContact>(
                    LockAttemptStatus.AlreadyLocked,
                    contact,
                    contact.Lease.Owner);
            }

            // LockAttemptResult.DatabaseUnavailable - I'm letting any exceptions bubble up for now
        }
        public void DeleteContact_ContactExistsWithExpiredLeaseByDifferentOwner_ContactDeleted(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            IDateTimeController dateTime,
            TestIndexUtils contactIndex) 
        {
            using (contactIndex)
            {
                // arrange contact with expired lease from another owner...
                var timeTraveller = (DateTimeTimeTraveller)dateTime;

                using (timeTraveller.NewJourney(-24))
                {
                    contactService.Save(contact, them, false, ctx).Should().BeTrue();
                }

                // deletes don't have an owner, at this level at least.
                contactService.Delete(contact.Id, ctx);

                contactService
                    .LoadForReadOnly(contact.Id, ctx)
                    .Should()
                    .BeNull();
            }
        }
        public void DeleteContact_ContactExistsWithActiveLeaseBySameOwner_ContactDeleted(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, us, false, ctx).Should().BeTrue();

                // deletes seem to be brutal - they don't care about locks
                contactService.Delete(contact.Id, ctx);

                contactService
                    .LoadForReadOnly(contact.Id, ctx)
                    .Should()
                    .BeNull();
            }
        }
 public void TryLoadContact_ContactExistsAlreadyLockedByDifferentOwner_NotLocked(
     IElasticAnalyticsContactService contactService,
     ElasticLeaseOwner us,
     ElasticContact eContact,
     ISystemContext ctx)
 {
     var res = contactService.TryLoadAndLock(eContact.Id, us, TimeSpan.FromMinutes(1), ctx);
     res.Should().NotBeNull();
     res.LockedObject.Id.Should().Be(eContact.Id);
     res.Status.Should().Be(LockAttemptStatus.AlreadyLocked);
 }
        public void TryLoadAndLockByIdentity_ContactObsolete_NullReturned(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticContact successor,
            ElasticLeaseOwner owner,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, owner, true, ctx)
                    .Should().Be(true);

                contactService.Obsolete(contact.Id, successor.Id, owner, ctx);
                contactService.Save(successor, owner, true, ctx);

                var res = contactService.TryLoadAndLock(contact.Identification.Identity, owner, TimeSpan.FromMinutes(1), ctx);
                res.Status.Should().Be(LockAttemptStatus.NotFound);
                res.LockedObject.Should().BeNull();
            }
        }
         public void ObsoleteContact_ContactExistsAndLockedByDifferentOwner_ReturnsFalse(
            IElasticAnalyticsContactService contactService,
            IRepository<ElasticContact> contactRepo,
            ElasticContact contact,
            Guid successor,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
         {
             using (contactIndex)
             {
                 // contact exists and currently locked.
                 contactService
                     .Save(contact, them, false, ctx)
                     .Should()
                     .Be(true);

                 contactService.Obsolete(contact.Id, successor, us, ctx).Should().BeFalse();
             }
         }
 public void ReleaseContact_ContactDoesntExist_NothingHappens(
     IElasticAnalyticsContactService contactService,
     ElasticContact contact,
     ElasticLeaseOwner us,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         AssertionExtensions.ShouldNotThrow(() => 
             contactService.Release(contact.Id, us, ctx));
     }
 }
 public void DeleteContact_ContactDoesntExist_ReturnsSuccessfully(
     IElasticAnalyticsContactService contactService,
     ILeaseService leaseService,
     ElasticContact contact,
     ElasticLeaseOwner us,
     ElasticLeaseOwner them,
     ISystemContext ctx,
     TestIndexUtils contactIndex) 
 {
     using (contactIndex)
     {
         AssertionExtensions.ShouldNotThrow(() => 
             contactService.Delete(contact.Id, ctx));
     }
 }
        public void LoadForReadOnlyById_ContactObsolete_SuccessorReturned(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticContact successor,
            ElasticLeaseOwner owner,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, owner, true, ctx)
                    .Should().Be(true);

                contactService.Obsolete(contact.Id, successor.Id, owner, ctx);
                contactService.Save(successor, owner, true, ctx);

                contactService.LoadForReadOnly(contact.Id, ctx)
                    .ShouldBeEquivalentToContact(successor, l => l == null);
            }
        }
        public void SaveContact_ContactDoesntExistShouldNotBeReleased_ContactCreatedWithLock(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, false, ctx)
                    .Should().Be(true);

                var res = contactService.TryLoadAndLock(contact.Id, us, TimeSpan.FromMinutes(1), ctx);

                res.Status.Should().Be(LockAttemptStatus.AlreadyLocked);
                res.LockedObject.ShouldBeEquivalentToContact(contact);

                contactService.LoadForReadOnly(contact.Identification.Identity, ctx)
                    .ShouldBeEquivalentToContact(contact, l => l == null);
            }
        }
        public void SaveContact_ContactLockedbyDifferentOwner_ContactNotAdded(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, false, ctx)
                    .Should().Be(true);

                contactService.Save(contact, us, false, ctx)
                    .Should().Be(false);
            }
        }
        public void SaveContact_ContactDoesntExistShouldBeReleased_ContactCreatedLockFree(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner them,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, true, ctx)
                    .Should().Be(true);

                var res = contactService.TryLoadAndLock(contact.Id, us, TimeSpan.FromMinutes(1), ctx);

                res.Status.Should().Be(LockAttemptStatus.Success);
                res.LockedObject.ShouldBeEquivalentToContact(contact, l => l.Expires > DateTime.UtcNow && l.IsOwnedBy(us) && l.Version == 3);

                contactService.LoadForReadOnly(contact.Identification.Identity, ctx)
                    .ShouldBeEquivalentToContact(contact);
            }
        }
        public void LoadForReadOnlyByIdentity_ContactObsolete_NullReturned(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticContact successor,
            ElasticLeaseOwner owner,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, owner, true, ctx)
                    .Should().Be(true);

                contactService.Obsolete(contact.Id, successor.Id, owner, ctx);
                contactService.Save(successor, owner, true, ctx);

                contactService.LoadForReadOnly(contact.Identification.Identity, ctx)
                    .Should().BeNull();
            }
        }
        public void LoadForReadOnlyByIdentity_ContactExistsLockedByDifferentOwner_ContactReturned(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, false, ctx)
                    .Should().Be(true);

                contact.Lease = null;
                contactService.LoadForReadOnly(contact.Identification.Identity, ctx)
                    .ShouldBeEquivalentToContact(contact);
            }
        }
        public void DeleteContact_ContactExistsWithActiveLeaseByDifferentOwner_DeleteStillHonoured(
            IElasticAnalyticsContactService contactService,
            ILeaseService leaseService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, false, ctx).Should().BeTrue();

                // deletes seem to be brutal
                contactService.Delete(contact.Id, ctx);

                contactService
                    .LoadForReadOnly(contact.Id, ctx)
                    .Should()
                    .BeNull();

                // lease is deleted so if we wanted, we can just go and create another contact with the
                // same id straight away...
                contact.Lease = null;
                contactService.Save(contact, us, false, ctx).Should().BeTrue();
            }
        }
 public void ObsoleteContact_ContactDoesntExist_NothingHappens(
     IElasticAnalyticsContactService contactService,
     ElasticContact contact,
     Guid successor,
     ElasticLeaseOwner owner,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     using (contactIndex)
     {
         AssertionExtensions.ShouldNotThrow(() => contactService.Obsolete(contact.Id, successor, owner, ctx));
     }
 }
        public void SaveContact_ContactExistsWithIdentityNotLocked_ContactUpdated(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contact.Identification.IdentityLevel.Should().Be(IdentificationLevel.Known);

                contactService.Save(contact, them, true, ctx)
                    .Should().Be(true);

                //contact.SystemInfo.VisitCount = 999; // make a change

                contactService.Save(contact, us, false, ctx)
                    .Should().Be(true);

                // Assert
                var res = contactService.TryLoadAndLock(contact.Id, us, TimeSpan.FromMinutes(1), ctx);
                res.Status.Should().Be(LockAttemptStatus.Success);
                res.LockedObject.ShouldBeEquivalentToContact(contact, l => l.Expires > DateTime.UtcNow && l.IsOwnedBy(us) && l.Version == 4);

                contactService.LoadForReadOnly(contact.Identification.Identity, ctx)
                    .ShouldBeEquivalentToContact(contact, l => l == null);
            }
        }
        public void LoadForReadOnlyById_ContactExistsNotLocked_ContactReturned(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, them, true, ctx)
                    .Should().Be(true);

                contactService.LoadForReadOnly(contact.Id, ctx)
                    .ShouldBeEquivalentToContact(contact, l => l == null);
            }
        }
        public void ObsoleteContact_ContactExistsWithSuccessorNotLocked_ContactObsoleted(
            IElasticAnalyticsContactService contactService,
            IRepository<ElasticContact> contactRepo,
            ElasticContact contact,
            Guid successor,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            // andes doesn't care if a contact has successors or not apart from when loading a contact.
            using (contactIndex)
            {
                contact.Successor = Guid.NewGuid();
                contactService.Save(contact, us, true, ctx).Should().BeTrue();

                contactService.Obsolete(contact.Id, successor, us, ctx).Should().BeTrue(); 
                var obsoletedContact = contactRepo.Get(contact.Id, ctx);

                obsoletedContact.Id.Should().Be(contact.Id);
                obsoletedContact.Successor.Should().Be(successor);
                obsoletedContact.Should().BeObsolete();
            }
        }
        public void SaveContact_ContactExistsWithIdentityAndIdentityInUse_ThrowsException(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact1,
            ElasticContact contact2,
            ElasticLeaseOwner owner,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact1, owner, true, ctx).Should().Be(true);

                // set the identity to be the same
                contact2.Identification.SetIdentity(
                    contact1.Identification.Identity,
                    contact1.Identification.IdentityLevel);

                AssertionExtensions.ShouldThrow<InvalidOperationException>(
                    () => contactService.Save(contact2, owner, true, ctx));
            }
        }
        public void ObsoleteContact_ContactExistsAndLockedBySameOwner_ContactObsoleted(
            IElasticAnalyticsContactService contactService,
            IRepository<ElasticContact> contactRepo,
            ElasticContact contact,
            Guid successor,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, us, false, ctx).Should().BeTrue();

                contactService.Obsolete(contact.Id, successor, us, ctx).Should().BeTrue();

                var obsoletedContact = contactRepo.Get(contact.Id, ctx);

                obsoletedContact.Id.Should().Be(contact.Id);
                obsoletedContact.Successor.Should().Be(successor);
                obsoletedContact.Should().BeObsolete();
            }
        }
 public void SaveContact_ContactExistsWithIdentityAndContactSaveFailed_ExistingIdentityRestored(
     IElasticAnalyticsContactService contactService,
     ElasticContact contact,
     ElasticLeaseOwner owner,
     ISystemContext ctx,
     TestIndexUtils contactIndex)
 {
     // ? how would a save contact fail, but first an identity save succeeds? (i.e. ES isn't down)
 }
        public void ObsoleteContact_ContactExistsAndExpiredLockedByDifferentOwner_ContactObsoleted(
            IElasticAnalyticsContactService contactService,
            IRepository<ElasticContact> contactRepo,
            ElasticContact contact,
            Guid successor,
            ElasticLeaseOwner us,
            ElasticLeaseOwner them,
            ISystemContext ctx,
            IDateTimeController dateTime,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                // arrange contact with expired lease from another owner...
                var timeTraveller = (DateTimeTimeTraveller)dateTime;

                using (timeTraveller.NewJourney(-24))
                {
                    contactService.Save(contact, them, false, ctx).Should().BeTrue();
                }

                contactService.Obsolete(contact.Id, successor, us, ctx).Should().BeTrue();

                var obsoletedContact = contactRepo.Get(contact.Id, ctx);

                obsoletedContact.Id.Should().Be(contact.Id);
                obsoletedContact.Successor.Should().Be(successor);
                obsoletedContact.Should().BeObsolete();
            }
        }
        public void TryExtendLock_ContactExistsLockedBySameOwner_LeaseExtended(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService.Save(contact, us, false, ctx)
                    .Should().Be(true);

                var res = contactService.TryExtendLock(contact, TimeSpan.FromHours(1), ctx);

                res.Should().BeTrue();
                contact.Lease.IsOwnedBy(us);
            }
        }
        public void ReleaseContact_NoLockExistsForContact_ContactReleased(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                // contact exists, no lock
                contactService.Save(contact, us, true, ctx).Should().BeTrue();

                // release
                contactService.Release(contact.Id, us, ctx);

                // should be able to lock it again
                contactService.TryLoadAndLock(contact.Id, us, TimeSpan.FromHours(1), ctx)
                    .Status.Should()
                    .Be(LockAttemptStatus.Success);
            }
        }
        public void TryExtendLock_ContactExistsLockedByDifferentOwner_NotExtended(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner them,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                // lock by a different owner
                contactService.Save(contact, them, false, ctx)
                    .Should().Be(true);

                // extend it
                contact.Lease = new ElasticLease(contact.Id, DateTime.UtcNow, us); // specify the lease we want
                var res = contactService.TryExtendLock(contact, TimeSpan.FromHours(1), ctx);

                res.Should().BeFalse();
                contact.Lease.IsOwnedBy(us); // currently Andes does not update the lease reference in this scenario
            }
        }
 public void TryLoadContact_ContactDoesntExist_ReturnsNull(
     ElasticLeaseOwner us,
     IElasticAnalyticsContactService contactService,
     ISystemContext ctx)
 {
     var res = contactService.TryLoadAndLock(Guid.NewGuid(), us, TimeSpan.FromMinutes(1), ctx);
     res.LockedObject.Should().BeNull();
     res.Status.Should().Be(LockAttemptStatus.NotFound);
 }
        public void DeleteContact_ContactExistsWithNoLease_ContactDeleted(
            IElasticAnalyticsContactService contactService,
            ElasticContact contact,
            ElasticLeaseOwner us,
            ISystemContext ctx,
            TestIndexUtils contactIndex)
        {
            using (contactIndex)
            {
                contactService
                    .Save(contact, us, true, ctx)
                    .Should().BeTrue();

                contactService.Delete(contact.Id, ctx);

                contactService
                    .LoadForReadOnly(contact.Id, ctx)
                    .Should()
                    .BeNull();
            }
        }