public async Task Valid_command_succeeds() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); } }
public async Task <Result <Nothing, Error> > Handle(SubmitRecruitmentForm.Command request, CancellationToken cancellationToken) { var options = _options.Value; var preferredTrainings = await _repo.GetByIds(request.PreferredTrainingIds); var enrollment = _enrollmentRepo.Query().Where(x => x.Email == request.Email).FirstOrDefault(); var enrollmentId = enrollment != null ? enrollment.Id : EnrollmentId.New; var result = await _aggregateStore.Update <EnrollmentAggregate, EnrollmentId, Result <Nothing, Error> >( enrollmentId, SourceId.New, (aggregate) => aggregate.SubmitRecruitmentForm(request, preferredTrainings, _clock.GetCurrentInstant()), cancellationToken); return(await result.Unwrap() .Tap(async _ => { await _aggregateStore.UpdateAsync <EnrollmentAggregate, EnrollmentId>( enrollmentId, SourceId.New, async(aggregate, token) => { var body = await BuildMessageBody(options.GreetingEmail, aggregate); var message = _emailService.CreateMessage(request.Email, options.GreetingEmail.Subject, body, isBodyHtml: options.GreetingEmail.IsBodyHtml); await _emailService.Send(message, cancellationToken) .Tap(() => aggregate.RecordEmailSent(_clock.GetCurrentInstant(), message)) .OnFailure(() => aggregate.RecordEmailSendingFailed(_clock.GetCurrentInstant(), message)); }, cancellationToken); })); }
public async Task Valid_command_emits_single_EmailSent_event_when_EmailService_sends_email() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); var eventStore = scope.ServiceProvider.GetRequiredService <EventFlow.EventStores.IEventStore>(); var events = await eventStore.LoadAllEventsAsync(EventFlow.EventStores.GlobalPosition.Start, 2, System.Threading.CancellationToken.None); var domainEvent = Assert.Single(events.DomainEvents, ev => ev.EventType == typeof(EmailSent)); var ev = Assert.IsType <EmailSent>(domainEvent.GetAggregateEvent()); Assert.Equal(SubmitRecruitmentFormHandler.MessageTitle, ev.Subject); } }
public async Task Command_must_be_issued_during_campaign() { var clock = new NodaTime.Testing.FakeClock(NodaTime.Instant.FromDateTimeUtc(DateTime.UtcNow).Plus(NodaTime.Duration.FromDays(30))); var sp = new ServiceProviderBuilder().BuildServiceProvider(services => { ConfigureServices(services); services.Remove <NodaTime.IClock>(); services.AddSingleton <NodaTime.IClock>(clock); }); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredTrainingIds = new[] { 1 }, PreferredLecturingCities = new[] { "Wolne Miasto Gdańsk" }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Domain.Error.DomainError>(result.Error); Assert.Equal(SubmitRecruitmentForm_ErrorMessages.SubmissionMustOccurDuringCampaign, error.Message); } }
public async Task Command_cannot_specify_duplicate_preferred_cities() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wolne Miasto Gdańsk", "Wolne Miasto Gdańsk" }, PreferredTrainingIds = new[] { 1, 2, 3, 5, 8, 13 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Domain.Error.ValidationFailed>(result.Error); var failure = Assert.Single(error.Failures); Assert.Equal(nameof(command.PreferredLecturingCities), failure.PropertyName); Assert.Single(failure.Errors); Assert.Equal(SubmitRecruitmentForm_ErrorMessages.PreferredLecturingCities_cannot_have_duplicates, failure.Errors.Single()); } }
public async Task All_specified_trainings_must_exist() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wolne Miasto Gdańsk" }, PreferredTrainingIds = new[] { 1, 21, 37 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Error.ResourceNotFound>(result.Error); Assert.Equal(SubmitRecruitmentForm_ErrorMessages.SomeTrainingsWereNotFound, error.Message); } }
public async Task All_tranings_in_command_must_belong_to_the_same_campaign() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wolne Miasto Gdańsk" }, PreferredTrainingIds = new[] { 1, 2 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Domain.Error.DomainError>(result.Error); Assert.Equal(SubmitRecruitmentForm_ErrorMessages.PreferredTrainingsMustBelongToTheSameCampaign, error.Message); } }
public async Task Command_must_specify_at_least_one_training() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wolne Miasto Gdańsk" }, PreferredTrainingIds = Array.Empty <int>(), GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Error.ValidationFailed>(result.Error); AssertHelpers.SingleError(nameof(command.PreferredTrainingIds), SubmitRecruitmentForm_ErrorMessages.PreferredTrainingsMustBeSpecified, error.Failures); } }
public async Task Command_MustContain_SubmissionDate_FirstName_LastName_EmailAddress_PhoneNumber_AboutMe() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsFailure); var error = Assert.IsType <Error.ValidationFailed>(result.Error); Assert.Collection(error.Failures, error => AssertHelpers.SingleError(nameof(command.FirstName), SubmitRecruitmentForm_ErrorMessages.FirstNameIsRequired, error), error => AssertHelpers.SingleError(nameof(command.LastName), SubmitRecruitmentForm_ErrorMessages.LastNameIsRequired, error), error => AssertHelpers.SingleError(nameof(command.Email), SubmitRecruitmentForm_ErrorMessages.EmailIsRequired, error), error => AssertHelpers.SingleError(nameof(command.PhoneNumber), SubmitRecruitmentForm_ErrorMessages.PhoneNumberIsRequired, error), error => AssertHelpers.SingleError(nameof(command.AboutMe), SubmitRecruitmentForm_ErrorMessages.AboutMeIsRequired, error), error => AssertHelpers.SingleError(nameof(command.Region), SubmitRecruitmentForm_ErrorMessages.RegionIsRequired, error), error => AssertHelpers.SingleError(nameof(command.PreferredLecturingCities), SubmitRecruitmentForm_ErrorMessages.PreferredLecturingCities_must_be_specified, error) ); } }
public void After_executing_command__aggregate_contains_name_email_phone_region_cities_trainings_and_campaign() { // Arrange var now = NodaTime.SystemClock.Instance.GetOffsetDateTime(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "małopolskie", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var enrollment = new EnrollmentAggregate(EnrollmentAggregate.EnrollmentId.New); var campaign = new Campaign( startDateTime: now.Minus(Duration.FromDays(3)), endDateTime: now.Plus(Duration.FromDays(3)), editionId: 1, name: "kampania testowa"); campaign.GetType().GetProperty(nameof(campaign.Id)).SetValue(campaign, 1); var training = new Training( address: "Papieska 21/37", city: "Wadowice", startDateTime: now.Plus(Duration.FromDays(7)), endDateTime: now.Plus(Duration.FromDays(7) + Duration.FromHours(4)), coordinatorId: Guid.NewGuid()); training.GetType().GetProperty(nameof(training.ID)).SetValue(training, 1); campaign.ScheduleTraining(training); var trainings = new[] { training }; // Act var result = enrollment.SubmitRecruitmentForm(command, trainings, now.ToInstant()); // Assert result.IsSuccess.Should().BeTrue(); enrollment.FirstName.Should().Be("Andrzej"); enrollment.LastName.Should().Be("Strzelba"); enrollment.FullName.Should().Be("Andrzej Strzelba"); enrollment.Email.ToString().Should().Be("*****@*****.**"); enrollment.PhoneNumber.Should().Be(Consts.FakePhoneNumber); enrollment.Region.Should().Be("małopolskie"); enrollment.PreferredLecturingCities.Should().BeEquivalentTo(new[] { "Wadowice" }); enrollment.PreferredTrainingIds.Should().BeEquivalentTo(new[] { 1 }); enrollment.CampaignId.Should().Be(1); }
public static RecruitmentFormSubmitted From(SubmitRecruitmentForm.Command command, Instant submissionDate, int campaignID) { return(new RecruitmentFormSubmitted( submissionDate, command.FirstName, command.LastName, command.Email, command.PhoneNumber, command.AboutMe, campaignID, command.Region, command.PreferredLecturingCities, command.PreferredTrainingIds, command.GdprConsentGiven)); }
public async Task After_executing_command_enrollments_contain_that_submission() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); } using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var results = await engine.Query(new GetSubmissions.Query()); var readModel = Assert.Single(results); var id = EnrollmentAggregate.EnrollmentId.With(readModel.Id); Assert.Equal("Andrzej", readModel.FirstName); Assert.Equal("Strzelba", readModel.LastName); Assert.Equal(EmailAddress.Parse("*****@*****.**"), readModel.Email); Assert.Equal(Consts.FakePhoneNumber, readModel.PhoneNumber); Assert.Equal("ala ma kota", readModel.AboutMe); Assert.Equal(1, readModel.Campaign.ID); Assert.Equal("Wolne Miasto Gdańsk", readModel.Region); var preferredTraining = Assert.Single(readModel.PreferredTrainings); Assert.Equal(1, preferredTraining.ID); Assert.Single(readModel.PreferredLecturingCities, "Wadowice"); } }
public async Task Valid_command_emits_single_EmailSendingFailed_event_when_EmailService_fails_to_send_email() { var sp = new ServiceProviderBuilder().BuildServiceProvider(services => { ConfigureServices(services); var failingEmailMock = new Mock <IEmailService>(); failingEmailMock .Setup(x => x.CreateMessage(It.IsAny <EmailAddress>(), It.IsAny <string>(), It.IsAny <string>(), It.IsAny <bool>(), It.IsAny <IEnumerable <EmailAttachment> >())) .Returns((EmailAddress email, string subject, string body, bool isHtml, IEnumerable <EmailAttachment> attachments) => new SucceedingEmailService().CreateMessage(email, subject, body, isHtml, attachments)); failingEmailMock.Setup(x => x.Send(It.IsAny <EmailMessage>(), CancellationToken.None)).Returns(Task.FromResult(Result.Failure("error"))); services.Remove <IEmailService>(); services.AddSingleton(failingEmailMock.Object); }); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "woj. małopolskie", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); var eventStore = scope.ServiceProvider.GetRequiredService <EventFlow.EventStores.IEventStore>(); var events = await eventStore.LoadAllEventsAsync(EventFlow.EventStores.GlobalPosition.Start, 2, CancellationToken.None); var domainEvent = Assert.Single(events.DomainEvents, ev => ev.EventType == typeof(EmailSendingFailed)); var ev = Assert.IsType <EmailSendingFailed>(domainEvent.GetAggregateEvent()); Assert.Equal(SubmitRecruitmentFormHandler.MessageTitle, ev.Subject); } }
public async Task Querying_for_existing_enrollment_returns_that_enrollment_events() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); } using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var enrollments = await engine.Query(new GetSubmissions.Query()); var result = await engine.Query(new GetEnrollmentDetails.QueryByEnrollmentId() { EnrollmentID = enrollments.Single().Id }); Assert.True(result.IsSuccess); var enrollment = result.Value; Assert.Contains(enrollment.Events, evt => evt.GetType() == typeof(GetEnrollmentDetails.RecruitmentFormSubmittedEventData)); } }
public async Task Valid_command_emits_single_RecruitmentFormSubmitted_event() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "małopolskie", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); var eventStore = scope.ServiceProvider.GetRequiredService <EventFlow.EventStores.IEventStore>(); var events = await eventStore.LoadAllEventsAsync(EventFlow.EventStores.GlobalPosition.Start, 2, System.Threading.CancellationToken.None); var domainEvent = Assert.Single(events.DomainEvents, ev => ev.EventType == typeof(RecruitmentFormSubmitted)); var e = Assert.IsType <RecruitmentFormSubmitted>(domainEvent.GetAggregateEvent()); Assert.Equal("Andrzej", e.FirstName); Assert.Equal("Strzelba", e.LastName); Assert.Equal("*****@*****.**", e.Email.ToString()); Assert.Equal(Consts.FakePhoneNumber, e.PhoneNumber); Assert.Equal("ala ma kota", e.AboutMe); Assert.Equal("małopolskie", e.Region); Assert.Single(e.PreferredLecturingCities, "Wadowice"); Assert.Single(e.PreferredTrainingIds, 1); Assert.True(e.GdprConsentGiven); } }
public Result <Nothing, Error> SubmitRecruitmentForm(SubmitRecruitmentForm.Command command, IReadOnlyCollection <Training> preferredTrainings, Instant now) { Guard.Against.Null(command, nameof(command)); Guard.Against.Null(preferredTrainings, nameof(preferredTrainings)); Guard.Against.Default(now, nameof(now)); return (Validate(new SubmitRecruitmentForm.Validator(), command) .Ensure( _ => preferredTrainings.Select(x => x.ID).OrderBy(x => x).SequenceEqual(command.PreferredTrainingIds.OrderBy(x => x)), new Error.ResourceNotFound(SubmitRecruitmentForm_ErrorMessages.SomeTrainingsWereNotFound)) .Ensure( _ => preferredTrainings.Select(x => x.Campaign).Distinct().Count() == 1, new Error.DomainError(SubmitRecruitmentForm_ErrorMessages.PreferredTrainingsMustBelongToTheSameCampaign)) .Ensure( _ => preferredTrainings.First().Campaign.Interval.Contains(now), new Error.DomainError(SubmitRecruitmentForm_ErrorMessages.SubmissionMustOccurDuringCampaign)) .Ensure( _ => preferredTrainings.All(x => x.StartDateTime.ToInstant() > now), new Error.DomainError(SubmitRecruitmentForm_ErrorMessages.PreferredTrainingsMustOccurInTheFuture)) .Tap(() => Emit(RecruitmentFormSubmitted.From(command, now, preferredTrainings.First().Campaign.Id)))); }
public async Task <IActionResult> Create(SubmitRecruitmentForm.Command command) { var result = await _engine.Execute(command); return(result.MatchToActionResult(success => Ok())); }
public async Task Querying_for_existing_enrollment_returns_that_enrollment_summary() { var sp = new ServiceProviderBuilder().BuildServiceProvider(ConfigureServices); using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var command = new SubmitRecruitmentForm.Command() { FirstName = "Andrzej", LastName = "Strzelba", Email = EmailAddress.Parse("*****@*****.**"), PhoneNumber = Consts.FakePhoneNumber, AboutMe = "ala ma kota", Region = "Wolne Miasto Gdańsk", PreferredLecturingCities = new[] { "Wadowice" }, PreferredTrainingIds = new[] { 1, 2 }, GdprConsentGiven = true }; var result = await engine.Execute(command); Assert.True(result.IsSuccess); } using (var scope = sp.CreateScope()) { var engine = scope.ServiceProvider.GetRequiredService <ISzlemEngine>(); var enrollments = await engine.Query(new GetSubmissions.Query()); var result = await engine.Query(new GetEnrollmentDetails.QueryByEnrollmentId() { EnrollmentID = enrollments.Single().Id }); Assert.True(result.IsSuccess); var enrollment = result.Value; Assert.Equal(enrollments.Single().Id, enrollment.ID); Assert.Equal("Andrzej", enrollment.FirstName); Assert.Equal("Strzelba", enrollment.LastName); Assert.Equal(EmailAddress.Parse("*****@*****.**"), enrollment.Email); Assert.Equal(Consts.FakePhoneNumber, enrollment.PhoneNumber); Assert.Single(enrollment.PreferredLecturingCities, "Wadowice"); Assert.Collection(enrollment.PreferredTrainings, first => { Assert.Equal(1, first.ID); Assert.Equal(trainerID, first.CoordinatorID); Assert.Equal("Jan Paweł II", first.CoordinatorName); Assert.Equal("Wadowice", first.City); }, second => { Assert.Equal(2, second.ID); Assert.Equal(trainerID, second.CoordinatorID); Assert.Equal("Jan Paweł II", second.CoordinatorName); Assert.Equal("Wadowice", second.City); }); enrollment.IsCurrentSubmission.Should().BeTrue(); enrollment.IsOldSubmission.Should().BeFalse(); enrollment.CanRecordTrainingResults.Should().BeFalse(); } }