public async Task When_simultaneous_reservations_are_placed_for_one_of_a_fixed_quantity_of_a_resource_then_different_values_are_reserved() { //arrange var barrier = new Barrier(2); var reservationService = new SqlReservationService(() => new ReservationServiceDbContextThatForcesConcurrencyDuringSave(barrier)); // given a fixed quantity of some resource, e.g. promo codes: var promoCode = "promo-code-" + Any.Word(); var reservedValue1 = "firstValue:" + Any.CamelCaseName(); var reservedValue2 = "SecondValue:" + Any.CamelCaseName(); await reservationService.Reserve(reservedValue1, promoCode, Any.CamelCaseName(), TimeSpan.FromDays(-1)); await reservationService.Reserve(reservedValue2, promoCode, Any.CamelCaseName(), TimeSpan.FromDays(-1)); //act var result = await reservationService.ReserveAny( scope : promoCode, ownerToken : Any.FullName(), lease : TimeSpan.FromMinutes(2), confirmationToken : Any.CamelCaseName()); var result2 = await reservationService.ReserveAny( scope : promoCode, ownerToken : Any.FullName(), lease : TimeSpan.FromMinutes(2), confirmationToken : Any.CamelCaseName()); //assert result.Should().NotBe(result2); }
public override async Task When_a_clock_is_set_on_a_command_then_it_takes_precedence_over_default_clock() { // arrange var clockName = Any.CamelCaseName(); var create = new CreateOrder(Any.Guid(), Any.FullName()); var clock = new CommandScheduler.Clock { Name = clockName, UtcNow = DateTimeOffset.Parse("2016-03-01 02:00:00 AM") }; using (var commandScheduler = CommandSchedulerDbContext()) { commandScheduler.Clocks.Add(clock); commandScheduler.SaveChanges(); } // act await Schedule( create, dueTime : DateTimeOffset.Parse("2016-03-20 09:00:00 AM"), clock : clock); await AdvanceClock(clockName : clockName, by : 30.Days()); //assert var target = await Get <Order>(create.AggregateId); target.Should().NotBeNull(); }
public void SetUp() { disposables = new CompositeDisposable { VirtualClock.Start() }; clockName = Any.CamelCaseName(); targetId = Any.Word(); target = new CommandTarget(targetId); store = new InMemoryStore <CommandTarget>( _ => _.Id, id => new CommandTarget(id)) { target }; CommandSchedulerDbContext.NameOrConnectionString = @"Data Source=(localdb)\MSSQLLocalDB; Integrated Security=True; MultipleActiveResultSets=False; Initial Catalog=ItsCqrsTestsCommandScheduler"; configuration = new Configuration() .UseInMemoryCommandScheduling() .UseDependency <IStore <CommandTarget> >(_ => store) .UseDependency <GetClockName>(c => _ => clockName) .TraceScheduledCommands(); scheduler = configuration.CommandScheduler <CommandTarget>(); Command <CommandTarget> .AuthorizeDefault = (commandTarget, command) => true; disposables.Add(ConfigurationContext.Establish(configuration)); disposables.Add(configuration); }
public override async Task When_a_clock_is_set_on_a_command_then_it_takes_precedence_over_default_clock() { // arrange var clockName = Any.CamelCaseName(); var targetId = Any.CamelCaseName(); var clock = new CommandScheduler.Clock { Name = clockName, UtcNow = DateTimeOffset.Parse("2016-03-01 02:00:00 AM") }; using (var commandScheduler = CommandSchedulerDbContext()) { commandScheduler.Clocks.Add(clock); commandScheduler.SaveChanges(); } var dueTime = DateTimeOffset.Parse("2016-03-20 09:00:00 AM"); // act await Schedule(new CreateCommandTarget(targetId), dueTime, clock : clock); await AdvanceClock(clockName : clockName, by : 30.Days()); //assert var target = await Get <NonEventSourcedCommandTarget>(targetId); target.Should().NotBeNull(); }
public override async Task Immediately_scheduled_commands_triggered_by_a_scheduled_command_have_their_due_time_set_to_the_causative_command_clock() { var aggregate = new CommandSchedulerTestAggregate(); await Save(aggregate); var dueTime = Clock.Now().AddMinutes(5); await Schedule( aggregate.Id, dueTime : dueTime, command : new CommandSchedulerTestAggregate.CommandThatSchedulesAnotherCommand { NextCommandAggregateId = aggregate.Id, NextCommand = new CommandSchedulerTestAggregate.Command { CommandId = Any.CamelCaseName() } }); VirtualClock.Current.AdvanceBy(TimeSpan.FromDays(1)); using (var db = CommandSchedulerDbContext()) { foreach (var command in db.ScheduledCommands.Where(c => c.AggregateId == aggregate.Id)) { command.AppliedTime .IfNotNull() .ThenDo(v => v.Should().BeCloseTo(dueTime, 10)); } } }
public void When_a_read_only_user_connects_to_the_event_store_Then_migrations_should_not_be_run() { var userName = Any.CamelCaseName(); var password = "******"; var loginName = userName; var user = new DbReadonlyUser(userName, loginName); var builder = new SqlConnectionStringBuilder(); var migrated = false; using (var db = new EventStoreDbContext()) { db.Database.ExecuteSqlCommand(string.Format("CREATE LOGIN [{0}] WITH PASSWORD = '******';", userName, password)); db.CreateReadonlyUser(user); builder.ConnectionString = db.Database.Connection.ConnectionString; builder.IntegratedSecurity = false; builder.UserID = user.LoginName; builder.Password = password; } using (var db = new EventStoreDbContext(builder.ConnectionString)) { IDbMigrator[] migrations = { new AnonymousMigrator(c => migrated = true, new Version(1, 0), Any.CamelCaseName()) }; var initializer = new EventStoreDatabaseInitializer <EventStoreDbContext>(migrations); initializer.InitializeDatabase(db); } migrated.Should().BeFalse(); }
public override async Task When_a_clock_is_advanced_and_a_command_fails_to_be_deserialized_then_other_commands_are_still_applied() { var failedTargetId = Any.CamelCaseName(); var successfulTargetId = Any.CamelCaseName(); await Schedule(failedTargetId, new CreateCommandTarget(failedTargetId), Clock.Now().AddHours(1)); await Schedule(successfulTargetId, new CreateCommandTarget(successfulTargetId), Clock.Now().AddHours(1.5)); using (var db = CommandSchedulerDbContext()) { var aggregateId = failedTargetId.ToGuidV3(); var command = db.ScheduledCommands.Single(c => c.AggregateId == aggregateId); var commandBody = command.SerializedCommand.FromJsonTo <dynamic>(); commandBody.Command.CommandName = "not a command name"; command.SerializedCommand = commandBody.ToString(); db.SaveChanges(); } // act Action advanceClock = () => AdvanceClock(clockName: clockName, by: TimeSpan.FromHours(2)).Wait(); // assert advanceClock.ShouldNotThrow(); var successfulAggregate = await Get <NonEventSourcedCommandTarget>(successfulTargetId); successfulAggregate.Should().NotBeNull(); }
public override async Task When_a_constructor_command_fails_with_a_ConcurrencyException_it_is_not_retried() { // arrange var deliveredEtags = new List <string>(); Configuration.Current.AddToCommandSchedulerPipeline <NonEventSourcedCommandTarget>( deliver: async(scheduled, next) => { deliveredEtags.Add(scheduled.Command.ETag); await next(scheduled); }); var id = Any.CamelCaseName(); var etag1 = Any.Word().ToETag(); var etag2 = Any.Word().ToETag(); var command1 = new CreateCommandTarget(id, etag: etag1); var command2 = new CreateCommandTarget(id, etag: etag2); // act await Schedule(id, command1); await Schedule(id, command2); await AdvanceClock(1.Days()); await AdvanceClock(1.Days()); await AdvanceClock(1.Days()); // assert deliveredEtags.Should() .ContainSingle(e => e == etag1) .And .ContainSingle(e => e == etag2); }
public override async Task When_an_immediately_scheduled_command_depends_on_a_precondition_that_has_not_been_met_yet_then_there_is_not_initially_an_attempt_recorded() { // arrange var targetId = Any.CamelCaseName(); var precondition = new EventHasBeenRecordedPrecondition( Guid.NewGuid().ToString().ToETag(), Guid.NewGuid()); // act await Schedule(targetId, new CreateCommandTarget(targetId), deliveryDependsOn : precondition); // assert using (var db = CommandSchedulerDbContext()) { var aggregateId = targetId.ToGuidV3(); var command = db.ScheduledCommands.Single(c => c.AggregateId == aggregateId); command.AppliedTime .Should() .NotHaveValue(); command.Attempts .Should() .Be(0); } }
public override async Task When_a_clock_is_set_on_a_command_then_it_takes_precedence_over_GetClockName() { // arrange var clockName = Any.CamelCaseName(); var targetId = Any.CamelCaseName(); var command = new CreateCommandTarget(targetId); var scheduledCommand = new ScheduledCommand <CommandTarget>( targetId: targetId, command: command, dueTime: DateTimeOffset.Parse("2016-03-20 09:00:00 AM")) { Clock = new CommandScheduler.Clock { Name = clockName, UtcNow = DateTimeOffset.Parse("2016-03-01 02:00:00 AM") } }; // act await scheduler.Schedule(scheduledCommand); await Configuration.Current.SchedulerClockTrigger() .AdvanceClock(clockName, by: 30.Days()); //assert var target = await store.Get(targetId); target.Should().NotBeNull(); }
public override async Task Scheduled_commands_with_no_due_time_are_delivered_at_Clock_Now_when_delivery_is_deferred() { // arrange var deliveredTime = new DateTimeOffset(); var target = new NonEventSourcedCommandTarget(Any.CamelCaseName()); await Save(target); Configuration .Current .UseCommandHandler <NonEventSourcedCommandTarget, TestCommand>(async(_, __) => deliveredTime = Clock.Now()); // act await Schedule(target.Id, new TestCommand { CanBeDeliveredDuringScheduling = false }, dueTime : null); await AdvanceClock( clockName : clockName, by : 1.Hours()); // assert deliveredTime .Should() .Be(Clock.Now()); }
public override async Task When_triggering_specific_commands_then_the_result_can_be_used_to_evaluate_failures() { // arrange var target = new CommandTarget(Any.CamelCaseName()); await store.Put(target); var schedulerAdvancedResult = new SchedulerAdvancedResult(); // act await scheduler.Schedule(target.Id, new TestCommand(isValid : false), Clock.Now().AddDays(2)); using (var db = new CommandSchedulerDbContext()) { var aggregateId = target.Id.ToGuidV3(); var command = db.ScheduledCommands .Single(c => c.AggregateId == aggregateId); await Configuration.Current .SchedulerClockTrigger() .Trigger(command, schedulerAdvancedResult, db); } //assert schedulerAdvancedResult .FailedCommands .Should() .HaveCount(1); }
public override async Task Specific_scheduled_commands_can_be_triggered_directly_by_target_id() { // arrange var target = new CommandTarget(Any.CamelCaseName()); await store.Put(target); // act await scheduler.Schedule(target.Id, new TestCommand(), Clock.Now().AddDays(2)); using (var db = new CommandSchedulerDbContext()) { var aggregateId = target.Id.ToGuidV3(); var command = db.ScheduledCommands .Single(c => c.AggregateId == aggregateId); await Configuration.Current .SchedulerClockTrigger() .Trigger(command, new SchedulerAdvancedResult(), db); } //assert target = await store.Get(target.Id); target.CommandsEnacted.Should().HaveCount(1); }
public override async Task Scheduled_commands_with_no_due_time_set_the_correct_clock_time_when_delivery_is_deferred() { // arrange var deliveredTime = new DateTimeOffset(); var target = new CommandTarget(Any.CamelCaseName()); await store.Put(target); var clockRepository = Configuration.Current.SchedulerClockRepository(); var schedulerClockTime = DateTimeOffset.Parse("2016-02-13 01:00:00 AM"); clockRepository.CreateClock(clockName, schedulerClockTime); Configuration.Current.UseCommandHandler <CommandTarget, TestCommand>(async(_, __) => deliveredTime = Clock.Now()); // act await scheduler.Schedule(target.Id, new TestCommand { CanBeDeliveredDuringScheduling = false }, dueTime : null); await Configuration.Current .SchedulerClockTrigger() .AdvanceClock(clockName, by: 1.Hours()); // assert deliveredTime.Should().Be(DateTimeOffset.Parse("2016-02-13 01:00:00 AM")); }
public void When_a_migration_throws_then_the_change_is_rolled_back() { InitializeDatabase <MigrationsTestEventStore>(); var columnName = Any.CamelCaseName(3); try { InitializeDatabase <MigrationsTestEventStore>( new AnonymousMigrator(c => { c.Execute(string.Format(@"alter table [eventstore].[events] add {0} nvarchar(50) null", columnName)); throw new DataMisalignedException(); }, version)); } catch (DataMisalignedException) { } GetAppliedVersions <MigrationsTestEventStore>().Should().NotContain(s => s == version.ToString()); using (var context = new MigrationsTestEventStore()) { var result = context.QueryDynamic( @"SELECT * FROM sys.columns WHERE name='@columnName'", new Dictionary <string, object> { { "columnName", columnName } }).Single(); result.Should().BeEmpty(); } }
public override async Task When_a_scheduled_command_depends_on_an_event_that_never_arrives_it_is_eventually_abandoned() { // arrange var deliveryAttempts = 0; Configuration.Current.AddToCommandSchedulerPipeline <NonEventSourcedCommandTarget>( deliver: async(command, next) => { deliveryAttempts++; await next(command); }); var precondition = new EventHasBeenRecordedPrecondition( Guid.NewGuid().ToString().ToETag(), Guid.NewGuid()); // act await Schedule(Any.CamelCaseName(), new TestCommand(), deliveryDependsOn : precondition); for (var i = 0; i < 10; i++) { await AdvanceClock(1.Days()); } //assert deliveryAttempts.Should().Be(6); }
public async Task Migrations_in_a_race_do_not_throw() { InitializeDatabase <MigrationsTestEventStore>(); var columnName = Any.CamelCaseName(3); var barrier = new Barrier(2); var migrator = new AnonymousMigrator(c => { c.Execute(string.Format(@"alter table [eventstore].[events] add {0} nvarchar(50) null", columnName)); barrier.SignalAndWait(10000); }, version); var task1 = Task.Run(() => InitializeDatabase <MigrationsTestEventStore>(migrator)); var task2 = Task.Run(() => InitializeDatabase <MigrationsTestEventStore>(migrator)); await Task.WhenAll(task1, task2); using (var context = new MigrationsTestEventStore()) { var result = context.QueryDynamic( @"SELECT * FROM sys.columns WHERE name='@columnName'", new Dictionary <string, object> { { "columnName", columnName } }).Single(); result.Should().BeEmpty(); } }
public void SetUp() { aggregateId = Any.Guid(); sequenceNumber = Any.PositiveInt(); disposables = new CompositeDisposable { ConfigurationContext.Establish(new Configuration() .UseSqlStorageForScheduledCommands(c => c.UseConnectionString(TestDatabases.CommandScheduler.ConnectionString))) }; if (clockName == null) { clockName = Any.CamelCaseName(); using (var db = Configuration.Current.CommandSchedulerDbContext()) { db.Clocks.Add(new CommandScheduler.Clock { Name = clockName, StartTime = Clock.Now(), UtcNow = Clock.Now() }); db.SaveChanges(); } } using (var db = Configuration.Current.CommandSchedulerDbContext()) { db.Database.ExecuteSqlCommand("delete from PocketMigrator.AppliedMigrations where MigrationScope = 'CommandSchedulerCleanup'"); } }
public async Task When_the_VirtualClock_is_advanced_past_a_commands_due_time_then_in_EnactCommand_ClockNow_returns_the_commands_due_time() { var dueTime = DateTimeOffset.Parse("2019-09-01 +00:00"); VirtualClock.Start(DateTimeOffset.Parse("2019-01-01 +00:00")); var target = new NonEventSourcedCommandTarget(Any.CamelCaseName()); var configuration = Configuration.Current; await configuration.Store <NonEventSourcedCommandTarget>().Put(target); var clockNowAtCommandDeliveryTime = default(DateTimeOffset); configuration.UseCommandHandler <NonEventSourcedCommandTarget, TestCommand>( enactCommand: async(_, __) => { clockNowAtCommandDeliveryTime = Clock.Now(); }); var scheduler = configuration.CommandScheduler <NonEventSourcedCommandTarget>(); await scheduler.Schedule(target.Id, new TestCommand(), dueTime : dueTime); VirtualClock.Current.AdvanceBy(365.Days()); clockNowAtCommandDeliveryTime.Should().Be(dueTime); }
public async Task When_a_clock_is_advanced_then_unassociated_commands_are_not_triggered() { // arrange var clockOne = CreateClock(Any.CamelCaseName(), Clock.Now()); var clockTwo = CreateClock(Any.CamelCaseName(), Clock.Now()); var deliveryAttempts = new ConcurrentBag <IScheduledCommand>(); Configuration.Current.TraceScheduledCommands(onDelivering: command => { deliveryAttempts.Add(command); }); await Schedule( new CreateCommandTarget(Any.CamelCaseName()), Clock.Now().AddDays(1), clock : clockOne); await Schedule( new CreateCommandTarget(Any.CamelCaseName()), Clock.Now().AddDays(1), clock : clockTwo); // act await AdvanceClock(TimeSpan.FromDays(2), clockOne.Name); //assert deliveryAttempts .Should().HaveCount(1) .And .OnlyContain(c => ((CommandScheduler.Clock)c.Clock).Name == clockOne.Name); }
public void SetUp() { disposables = new CompositeDisposable { VirtualClock.Start() }; clockName = Any.CamelCaseName(); targetId = Any.Word(); target = new CommandTarget(targetId); store = new InMemoryStore <CommandTarget>( _ => _.Id, id => new CommandTarget(id)) { target }; configuration = new Configuration() .UseInMemoryCommandScheduling() .UseDependency <IStore <CommandTarget> >(_ => store) .UseDependency <GetClockName>(c => _ => clockName) .TraceScheduledCommands(); scheduler = configuration.CommandScheduler <CommandTarget>(); Command <CommandTarget> .AuthorizeDefault = (commandTarget, command) => true; disposables.Add(ConfigurationContext.Establish(configuration)); disposables.Add(configuration); }
public async Task When_a_command_reserves_a_unique_value_but_it_expires_then_a_subsequent_request_by_a_different_actor_succeeds() { // arrange var username = Any.CamelCaseName(5); var scope = "UserName"; await Configuration.Current.ReservationService().Reserve(username, scope, Any.CamelCaseName(), TimeSpan.FromMinutes(30)); // act VirtualClock.Current.AdvanceBy(TimeSpan.FromMinutes(32)); var attempt = new CustomerAccount() .Validate(new RequestUserName { UserName = username, Principal = new Customer { Name = Any.CamelCaseName() } }); // assert attempt.ShouldBeValid(); var reservation = await GetReservedValue(username, scope); reservation.Expiration.Should().Be(Clock.Now().AddMinutes(1)); }
public void When_a_command_reserves_a_unique_value_then_a_subsequent_request_by_a_different_owner_fails() { // arrange var username = Any.CamelCaseName(5); // act var account1 = new CustomerAccount(); account1.Apply(new RequestUserName { UserName = username, Principal = new Customer { Name = Any.CamelCaseName() } }); account1.ConfirmSave(); var account2 = new CustomerAccount(); var secondPrincipal = new Customer { Name = Any.CamelCaseName() }; var secondAttempt = account2.Validate(new RequestUserName { UserName = username, Principal = secondPrincipal }); // assert secondAttempt.ShouldBeInvalid($"The user name {username} is taken. Please choose another."); }
public void When_a_command_reserves_a_unique_value_then_a_subsequent_request_by_the_same_owner_succeeds() { // arrange var username = Any.CamelCaseName(5); // act var account1 = new CustomerAccount(); var principal = new Customer { Name = Any.CamelCaseName() }; account1.Apply(new RequestUserName { UserName = username, Principal = principal }); account1.ConfirmSave(); var account2 = new CustomerAccount(); var secondAttempt = account2.Validate(new RequestUserName { UserName = username, Principal = principal }); // assert secondAttempt.ShouldBeValid(); }
public void Reserving_a_unique_value_can_happen_during_command_validation() { // arrange var name = Any.CamelCaseName(); var firstCall = true; Configuration.Current.ReservationService = new FakeReservationService((value, scope, actor) => { if (firstCall) { firstCall = false; return(true); } return(false); }); // act var account1 = new CustomerAccount(); account1.Apply(new RequestUserName { UserName = name }); account1.ConfirmSave(); var account2 = new CustomerAccount(); var secondAttempt = account2.Validate(new RequestUserName { UserName = name }); // assert secondAttempt.ShouldBeInvalid(string.Format("The user name {0} is taken. Please choose another.", name)); }
public async Task A_DuckTypeProjector_can_access_the_metadata_of_unknown_event_types_dynamically() { StorableEvent storableEvent = null; using (var db = EventStoreDbContext()) { storableEvent = new StorableEvent { AggregateId = Guid.NewGuid(), StreamName = Any.CamelCaseName(), Type = Any.CamelCaseName(), Body = new { }.ToJson() }; db.Events.Add(storableEvent); await db.SaveChangesAsync(); } IEvent receivedEvent = null; var projector = Projector.CreateFor <dynamic>(e => receivedEvent = e); using (var catchup = CreateReadModelCatchup(projector)) { await catchup.Run(); } long absoluteSequenceNumber = ((IHaveExtensibleMetada)receivedEvent).Metadata.AbsoluteSequenceNumber; absoluteSequenceNumber.Should().Be(storableEvent.Id); }
public async Task A_dynamic_projector_can_access_the_properties_of_unknown_event_types_dynamically() { using (var db = EventStoreDbContext()) { Enumerable.Range(1, 10).ForEach(i => db.Events.Add(new StorableEvent { AggregateId = Guid.NewGuid(), StreamName = Any.CamelCaseName(), Type = Any.CamelCaseName(), Body = new { SomeValue = i }.ToJson() })); await db.SaveChangesAsync(); } var total = 0; using (var catchup = CreateReadModelCatchup(Projector.CreateDynamic(e => { total += (int)e.SomeValue; }))) { await catchup.Run(); } total.Should().Be(55); }
public async Task When_ReserveAny_is_called_for_a_scope_that_has_no_entries_at_all_then_it_returns_false() { var reservationService = Configuration.Current.ReservationService(); var value = await reservationService.ReserveAny(Any.CamelCaseName(), Any.CamelCaseName(), TimeSpan.FromMinutes(1)); value.Should().BeNull(); }
public async Task When_Confirm_is_called_for_a_nonexistent_reservation_then_it_returns_false() { var reservationService = Configuration.Current.ReservationService(); var value = await reservationService.Confirm(Any.CamelCaseName(), Any.CamelCaseName(), Any.CamelCaseName()); value.Should().BeFalse(); }
public void When_a_constructor_command_fails_authorization_then_it_throws() { Command <Order> .AuthorizeDefault = (o, c) => false; Action apply = () => new Order(new CreateOrder(Any.CamelCaseName())); apply.ShouldThrow <CommandAuthorizationException>(); }