public void ProcessChangeSet_update_cleanly_completes_without_error() { var context = A.Fake<IDataContext>(); var resolver = A.Fake<IConflictResolver>(); var bfim = A.Dummy<Models.Asset>(); var afim = A.Dummy<Models.Asset>(); var cs = new ChangeSet() { Assets = new List<ChangeItem<Models.Asset>>{ new ChangeItem<Models.Asset>{ Action = ChangeAction.Update, BFIM = bfim, AFIM = afim } } }; var processor = new ChangeSetProcessor(context, resolver); var result = processor.Process(1, false, cs); ExceptionAssert.Throws<ArgumentNullException>(() => processor.Process(1, false, null)); Assert.IsTrue(result.IsEmpty); Assert.AreEqual(ChangeSet.Empty, result); A.CallTo(() => context.Assets.Update(A<Models.Asset>.Ignored)).MustHaveHappened(Repeated.Exactly.Once); A.CallTo(() => context.Assets.Update(afim)).MustHaveHappened(Repeated.Exactly.Once); A.CallTo(() => context.AcceptChanges()).MustHaveHappened(Repeated.Exactly.Once); }
public async Task TestDirectChangeset_Simple() { for (int i = 0; i < 100; i++) { TestDomainServices.TestProvider_Scenarios ds = new TestDomainServices.TestProvider_Scenarios(); DomainServiceContext dsc = new DomainServiceContext(new MockDataService(new MockUser("mathew")), DomainOperationType.Submit); ds.Initialize(dsc); List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); for (int j = 0; j < 500; j++) { TestDomainServices.POCONoValidation e = new TestDomainServices.POCONoValidation() { ID = i, A = "A" + i, B = "B" + i, C = "C" + i, D = "D" + i, E = "E" + i }; entries.Add(new ChangeSetEntry(j, e, null, DomainOperation.Insert)); } await ChangeSetProcessor.ProcessAsync(ds, entries); Assert.IsFalse(entries.Any(p => p.HasError)); } }
public void ProcessChangeSet_update_dirty_invokes_successful_resolver_and_successful_retry() { var repo = A.Fake<IRepository<Asset, int>>(); var context = A.Fake<IDataContext>(); var resolver = A.Fake<IConflictResolver>(); var bfim = A.Dummy<Models.Asset>(); var afim = A.Dummy<Models.Asset>(); var cs = new ChangeSet() { Assets = new List<ChangeItem<Models.Asset>>{ new ChangeItem<Models.Asset>{ Action = ChangeAction.Update, BFIM = bfim, AFIM = afim } } }; A.CallTo(() => repo.KeySelector).Returns((Asset a) => a.Id); A.CallTo(() => context.Assets).Returns(repo); A.CallTo(() => context.AcceptChanges()).Throws(new ConcurrencyException()).Once(); A.CallTo(() => resolver.Resolve(repo, A<ChangeItem<Asset>>.Ignored, A<IDictionary<int,Asset>>.Ignored)).Returns(true); var processor = new ChangeSetProcessor(context, resolver); var result = processor.Process(1, false, cs); Assert.IsTrue(result.IsEmpty); Assert.AreEqual(ChangeSet.Empty, result); A.CallTo(() => context.Assets.Update(afim)).MustHaveHappened(Repeated.Exactly.Once); A.CallTo(() => context.AcceptChanges()).MustHaveHappened(Repeated.Exactly.Twice); }
public async Task TestDirectChangeset_Cities() { for (int i = 0; i < 100; i++) { CityDomainService ds = new CityDomainService(); DomainServiceContext dsc = new DomainServiceContext(new MockDataService(new MockUser("mathew")), DomainOperationType.Submit); ds.Initialize(dsc); List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); for (int j = 0; j < 500; j++) { City newCity = new City() { Name = "Toledo", CountyName = "Lucas", StateName = "OH" }; entries.Add(new ChangeSetEntry(j, newCity, null, DomainOperation.Insert)); } await ChangeSetProcessor.ProcessAsync(ds, entries); Assert.IsFalse(entries.Any(p => p.HasError)); } }
public async Task Authorization_Custom_Authorization_On_Custom_Update() { // Specifically, the City data is marked so that no one can delete a Zip code // from WA unless their user name is WAGuy MockUser notWaGuy = new MockUser("notWAGuy"); notWaGuy.IsAuthenticated = true; DomainServiceDescription serviceDescription = DomainServiceDescription.GetDescription(typeof(CityDomainService)); City city = null; // Execute a query to get a City from WA using (CityDomainService cities = new CityDomainService()) { DomainOperationEntry getCitiesQuery = serviceDescription.GetQueryMethod("GetCities"); // Now prepare for a query to find a Zip in WA DomainServiceContext ctxt = new WcfDomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Query); cities.Initialize(ctxt); IEnumerable result = (await cities.QueryAsync <City>(new QueryDescription(getCitiesQuery), CancellationToken.None)).Result; city = result.OfType <City>().FirstOrDefault(z => z.StateName == "WA"); Assert.IsNotNull(city, "Could not find a city in WA"); } using (CityDomainService cities = new CityDomainService()) { // Now prepare for a submit to invoke AssignCityZoneIfAuthorized as a named update method DomainServiceContext ctxt = new WcfDomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Submit); cities.Initialize(ctxt); // Prepare an attempt to delete this with a user whose name is not WAGuy // This should fail due to a custom auth attribute List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); ChangeSetEntry entry = new ChangeSetEntry(); entry.DomainOperationEntry = serviceDescription.GetCustomMethod(typeof(City), "AssignCityZoneIfAuthorized"); entry.EntityActions = new EntityActionCollection { { "AssignCityZoneIfAuthorized", new object[] { "SomeZone" } } }; entry.Operation = DomainOperation.Update; entry.Entity = city; entries.Add(entry); UnauthorizedAccessException exception = null; try { await ChangeSetProcessor.ProcessAsync(cities, entries); } catch (UnauthorizedAccessException ex) { exception = ex; } Assert.IsNotNull(exception, "Expected failure attempting to perform custom method on WA with inappropriate user name"); Assert.AreEqual("Only one user is authorized to execute operation 'AssignCityZoneIfAuthorized', and it isn't you.", exception.Message); } // Now do that again but with a user who is WAGuy -- it should succeed using (CityDomainService cities = new CityDomainService()) { MockUser waGuy = new MockUser("WAGuy"); waGuy.IsAuthenticated = true; // Now prepare for a submit to invoke AssignCityZoneIfAuthorized as a named update method DomainServiceContext ctxt = new WcfDomainServiceContext(new MockDataService(waGuy), DomainOperationType.Submit); cities.Initialize(ctxt); // Prepare an attempt to delete this with a user whose name is not WAGuy // This should fail due to a custom auth attribute // Prepare a submit to call the AssignCityZoneIfAuthorized with an unauthorized user List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); ChangeSetEntry entry = new ChangeSetEntry(); entry.DomainOperationEntry = serviceDescription.GetCustomMethod(typeof(City), "AssignCityZoneIfAuthorized"); entry.EntityActions = new EntityActionCollection { { "AssignCityZoneIfAuthorized", new object[] { "SomeZone" } } }; entry.Operation = DomainOperation.Update; entry.Entity = city; entries.Add(entry); Exception exception = null; try { await ChangeSetProcessor.ProcessAsync(cities, entries); } catch (UnauthorizedAccessException ex) { exception = ex; } Assert.IsNull(exception, "Expected success attempting to delete a zip from WA with inappropriate user name"); } }
public async Task Authorization_Custom_Authorization_On_CUD() { // Specifically, the City data is marked so that no one can delete a Zip code // from WA unless their user name is WAGuy MockUser notWaGuy = new MockUser("notWAGuy"); notWaGuy.IsAuthenticated = true; DomainServiceDescription serviceDescription = DomainServiceDescription.GetDescription(typeof(CityDomainService)); Zip zip = null; // First execute a query to get some zips DomainOperationEntry getZipsQuery = serviceDescription.GetQueryMethod("GetZips"); DomainServiceContext ctxt; using (CityDomainService cities = new CityDomainService()) { // Now prepare for a query to find a Zip in WA ctxt = new WcfDomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Query); cities.Initialize(ctxt); IEnumerable result = (await cities.QueryAsync <Zip>(new QueryDescription(getZipsQuery), CancellationToken.None)).Result; zip = result.OfType <Zip>().FirstOrDefault(z => z.StateName == "WA"); Assert.IsNotNull(zip, "Could not find a zip code in WA"); } // Prepare a submit to delete this zip from a user who is not authorized using (CityDomainService cities = new CityDomainService()) { // Now prepare for a query to find a Zip in WA ctxt = new WcfDomainServiceContext(new MockDataService(notWaGuy), DomainOperationType.Submit); cities.Initialize(ctxt); // Prepare an attempt to delete this with a user whose name is not WAGuy // This should fail due to a custom auth attribute List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); ChangeSetEntry entry = new ChangeSetEntry(1, zip, zip, DomainOperation.Delete); entries.Add(entry); UnauthorizedAccessException exception = null; try { await ChangeSetProcessor.ProcessAsync(cities, entries); } catch (UnauthorizedAccessException ex) { exception = ex; } Assert.IsNotNull(exception, "Expected failure attempting to delete a zip from WA with inappropriate user name"); Assert.AreEqual("Only one user can delete zip codes from that state, and it isn't you.", exception.Message); } // Now do that again but with a user who is WAGuy -- it should succeed using (CityDomainService cities = new CityDomainService()) { MockUser waGuy = new MockUser("WAGuy"); waGuy.IsAuthenticated = true; // Now try a submit where the user *is* Mathew to validate we succeed ctxt = new WcfDomainServiceContext(new MockDataService(waGuy), DomainOperationType.Submit); cities.Initialize(ctxt); List <ChangeSetEntry> entries = new List <ChangeSetEntry>(); ChangeSetEntry entry = new ChangeSetEntry(1, zip, zip, DomainOperation.Delete); entries.Add(entry); Exception exception = null; try { await ChangeSetProcessor.ProcessAsync(cities, entries); } catch (UnauthorizedAccessException ex) { exception = ex; } Assert.IsNull(exception, "Expected success attempting to delete a zip from WA with inappropriate user name"); } }
public OfflineController(ChangeSetProcessor changeSetProcessor) { _changeSetProcessor = changeSetProcessor; }
public void ProcessChangeSet_update_dirty_invokes_unsuccessful_merge_and_no_retry() { var repo = A.Fake<IRepository<Asset, int>>(); var context = A.Fake<IDataContext>(); var resolver = new RejectConcurrentEditsConflictResolver(); var afim = A.Dummy<Models.Asset>(); var cs = new ChangeSet() { Assets = new List<ChangeItem<Models.Asset>>{ new ChangeItem<Models.Asset>{ Action = ChangeAction.Update, BFIM = A.Dummy<Models.Asset>(), AFIM = afim } } }; A.CallTo(() => repo.KeySelector).Returns((Asset a) => a.Id); A.CallTo(() => context.Assets).Returns(repo); A.CallTo(() => context.AcceptChanges()).Throws(new ConcurrencyException()).Once(); var processor = new ChangeSetProcessor(context, resolver); var result = processor.Process(1, false, cs); Assert.IsFalse(result.IsEmpty); Assert.AreNotEqual(ChangeSet.Empty, result); A.CallTo(() => context.Assets.Update(afim)).MustHaveHappened(Repeated.Exactly.Once); A.CallTo(() => context.AcceptChanges()).MustHaveHappened(Repeated.Exactly.Once); }
public void ProcessChangeSet_which_locks_retains_lock_on_failed_merge() { var job = new Job() { Id = 42 }; var context = A.Fake<IDataContext>(); var resolver = A.Fake<IConflictResolver>(); var bfim = A.Dummy<Models.Asset>(); var afim = A.Dummy<Models.Asset>(); var cs = new ChangeSet() { Assets = new List<ChangeItem<Models.Asset>>{ new ChangeItem<Models.Asset>(ChangeAction.Update,bfim,afim), new ChangeItem<Models.Asset>(ChangeAction.Delete,A.Dummy<Asset>(),null), new ChangeItem<Models.Asset>(ChangeAction.Create,null, A.Dummy<Asset>()) } }; A.CallTo(() => resolver.Resolve(A<IRepository<Asset, int>>.Ignored, A<ChangeItem<Asset>>.Ignored, A<IDictionary<int, Asset>>.Ignored)).Returns(false); A.CallTo(() => context.Jobs.GetById(A<int>.Ignored)).Returns(job); A.CallTo(() => context.Assets.KeySelector).Returns((Asset a) => a.Id); A.CallTo(() => context.AcceptChanges()).Throws(new ConcurrencyException()); A.CallTo(() => context.AcceptChanges()).DoesNothing().Once(); var processor = new ChangeSetProcessor(context, resolver); var result = processor.Process(1, true, cs); Assert.IsFalse(result.IsEmpty); Assert.AreNotEqual(ChangeSet.Empty, result); A.CallTo(() => context.Assets.Update(afim)).MustHaveHappened(Repeated.Exactly.Once); A.CallTo(() => context.AcceptChanges()).MustHaveHappened(Repeated.Exactly.Twice); A.CallTo(() => resolver.Resolve(A<IRepository<Asset, int>>.Ignored, A<ChangeItem<Asset>>.Ignored, A<IDictionary<int, Asset>>.Ignored)).MustHaveHappened(Repeated.Exactly.Once); //Assert.IsNotNull(job.LockedBy); // disabling this check temporarily this functionality was designed around EF and transaction rollbacks }
public void ProcessChangeSet_throws_when_attempt_to_lock_closed_lock() { var context = A.Fake<IDataContext>(); var cs = A.Dummy<ChangeSet>(); A.CallTo(() => context.Jobs.GetById(A<int>.Ignored)) .Returns(new Job() { LockedBy = "FooBar" }); var processor = new ChangeSetProcessor(context, A.Dummy<IConflictResolver>()); ExceptionAssert.Throws<ConcurrencyException>(() => processor.Process(A<int>.Ignored, true, cs)); }
public void ProcessChangeSet_throws_when_invalid_job_id_used_in_lock() { var context = A.Fake<IDataContext>(); var cs = A.Dummy<ChangeSet>(); A.CallTo(() => context.Jobs.GetById(A<int>.Ignored)).Returns(null); var processor = new ChangeSetProcessor(context, A.Dummy<IConflictResolver>()); ExceptionAssert.Throws<EntityNotFoundException>(() => processor.Process(A<int>.Ignored, false, cs)); }
public void Get_initial_changeset_returns_only_partition_with_all_items_in_afim_and_action_is_initialize() { var assets = new List<Asset> { new Asset(){Id=1,JobId=0}, new Asset(){Id=2,JobId=0}, new Asset(){Id=3,JobId=1}, new Asset(){Id=4,JobId=0} }.AsQueryable(); var repo = A.Fake<IRepository<Asset, int>>(); var context = A.Fake<IDataContext>(); var resolver = A.Fake<IConflictResolver>(); A.CallTo(() => repo.GetAll()).Returns(assets); A.CallTo(() => context.Assets).Returns(repo); var processor = new ChangeSetProcessor(context, resolver); var result = processor.BuildInitialChangeSet(0); A.CallTo(() => context.AcceptChanges()).MustNotHaveHappened(); Assert.IsNotNull(result); Assert.IsFalse(result.IsEmpty); Assert.AreNotEqual(ChangeSet.Empty, result); //Assert.IsTrue(result.All(_ => _ is ChangeItem<Asset>)); //Assert.IsTrue(result.All(_ => _.Action == ChangeAction.Initialize)); //Assert.IsTrue(result.All(_ => _.GetBFIM() == null)); //Assert.IsTrue(result.All(_ => _.GetAFIM() != null)); //Assert.AreEqual(3, result.Assets.Count); //Assert.AreEqual(3, result.TotalItemsCount); //Assert.AreEqual(3, result.Count()); Assert.IsTrue(result.Assets.All(_ => _.AFIM.JobId == 0)); }
private void Exec(int jobId, double pcPercent, double conflictGuarantee, IConflictResolver resolver) { bool claimIt = false; if (s_rng.NextDouble() < pcPercent) claimIt = true; var cs = BuildChangeSet(jobId); var dc = new Rel.Data.Ef6.TpContext(); var res = resolver; var csp = new ChangeSetProcessor(dc, res); ChangeSet result; if (s_rng.NextDouble() < conflictGuarantee) { Touch(jobId, cs); } using (new Timed(false, "ChangeSet {0}", res.GetType().Name)) result = csp.Process(jobId, claimIt, cs); }