public async Task Reservation이_더_이상_Reserved_상태가_아님() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var isbn = "1234567"; var bookAddedAt = new DateTime(2020, 12, 11); var bookRequestedAt = new DateTime(2020, 12, 25); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Timestamp = bookAddedAt, Isbn = isbn, Title = "Gone with the Wind" }); var existId = await BookSagaHarness.Exists(bookId, machine => machine.Available); Assert.IsTrue(existId.HasValue); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. // 이런식으로 하면, Timing Issue가 있을 수 있다. // Assert.IsNotNull(SagaHarness.Sagas.ContainsInState(reservationId, StateMachine, x => x.Reserved)); var reservationSaga = SagaHarness.SagaOf(reservationId); var bookSaga = BookSagaHarness.SagaOf(bookId); Assert.IsTrue(await reservationSaga.ExistsAs(m => m.Reserved), "ReservationSaga 가 Reserved 상태가 아님"); // Assert.IsTrue((await SagaHarness.Exists(reservationId, machine => machine.Reserved)).HasValue, // "ReservationSaga 가 Reserved 상태가 아님"); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Reserved), "BookSaga 가 Reserved 상태가 아님."); // Assert.IsTrue((await BookSagaHarness.Exists(bookId, machine => machine.Reserved)).HasValue, // "BookSaga 가 Reserved 상태가 아님."); await Time.Advance(TimeSpan.FromDays(1)); //await Task.Delay(5_000); // TODO CHECK 아래에서 SagaHarness.Exists() 구문을 써서 reservation 인 것이 없는지 확인하는 방식으로는 테스트 불가!!! // TODO --> 잘 생각해보면, 아주 짧은 시간동안에는 아직 Schedule 된 메시지가 처리가 미처 안된상태이므로.. // TODO --> 따라서, 이런 경우에는 SagaHarness.NotExists() 구문을 사용해야 harness 가 "대기" 를 할 수 있음. // existId = await SagaHarness.NotExists(reservationId); // Assert.IsFalse(existId.HasValue, // "ReservationSaga 가 아직도 Reserved 상태임"); // Assert.IsTrue((await BookSagaHarness.Exists(bookId, machine => machine.Available)).HasValue, // "BookSaga 가 Available 상태가 아님."); Assert.IsTrue(await reservationSaga.NotExists()); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Available)); }
public async Task ReservationSaga가_생성된다() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = InVar.Timestamp, MemberId = memberId, BookId = bookId }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. Assert.IsTrue(await TestHarness.Consumed.Any <ReservationRequested>(), "메시지 수신이 안됨"); Assert.IsTrue(await SagaHarness.Consumed.Any <ReservationRequested>(), "Saga에 의해 메시지 처리가 안됨"); Assert.That(await SagaHarness.Created.Any(x => x.CorrelationId == reservationId), "생성된 Saga의 CorrelationId 는 Reservation Id 여야 함"); // var instance = SagaHarness.Created.ContainsInState(bookId, StateMachine, StateMachine.Available); // var wrongBookId = NewId.NextGuid(); Assert.That(await SagaHarness.Exists(reservationId, machine => machine.Requested), Is.EqualTo(reservationId), "수신된 메시지의 BookId 에 해당하는 Requested 상태의 Saga가 없음."); }
public async Task Should_create_a_saga_instance() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, InVar.Timestamp, MemberId = memberId, BookId = bookId, }); Assert.IsTrue(await TestHarness.Consumed.Any <ReservationRequested>(), "Message not consumed"); Assert.IsTrue(await SagaHarness.Consumed.Any <ReservationRequested>(), "Message not consumed by saga"); Assert.That(await SagaHarness.Created.Any(x => x.CorrelationId == reservationId)); var instance = SagaHarness.Created.ContainsInState(reservationId, Machine, Machine.Requested); Assert.IsNotNull(instance, "Saga instance not found"); var existsId = await SagaHarness.Exists(reservationId, x => x.Requested); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); }
public async Task Should_create_a_saga_instance() { var bookId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); Assert.IsTrue(await TestHarness.Consumed.Any <BookAdded>(), "Message not consumed"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookAdded>(), "Message not consumed by saga"); Assert.That(await SagaHarness.Created.Any(x => x.CorrelationId == bookId)); var instance = SagaHarness.Created.ContainsInState(bookId, Machine, Machine.Available); Assert.IsNotNull(instance, "Saga instance not found"); var existsId = await SagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); }
public async Task 새로운_BookId_메시지를_받으면_새로운_Saga_Instance가_만들어지고_Activity가_수행된다() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, Timestamp = InVar.Timestamp, MemberId = memberId }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. Assert.IsTrue(await TestHarness.Consumed.Any <BookCheckedOut>(), "메시지 수신이 안됨"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookCheckedOut>(), "메시지 수신이 안됨"); var checkOutSaga = SagaHarness.SagaOf(checkOutId); Assert.IsTrue(await checkOutSaga.Created(), "Saga 생성 안됨"); Assert.IsTrue(await checkOutSaga.ExistsAs(m => m.CheckedOut), "CheckOut 상태가 아님."); Assert.IsTrue(await TestHarness.Published.Any <NotifyMemberDueDate>(), "NotifyMemberDueDate 메시지 publish 안됨"); }
public async Task 현재시각을_시각을_기준으로_만료기간이_갱신된다() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); var memberId = NewId.NextGuid(); DateTime checkOutTime = InVar.Timestamp; await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, Timestamp = checkOutTime, MemberId = memberId }); var checkOutSaga = SagaHarness.SagaOf(checkOutId); Assert.IsTrue(await checkOutSaga.ExistsAs(m => m.CheckedOut), "CheckOut 상태가 아님."); // RenewCheckOut 전송. var settings = Provider.GetRequiredService <CheckOutSettings>(); var requestClient = Provider.GetRequiredService <IRequestClient <RenewCheckOut> >(); var now = Time.UtcNow; var renewed = await requestClient.GetResponse <CheckOutRenewed>(new { CheckOutId = checkOutId }); Assert.That(renewed.Message.DueDate, Is.GreaterThanOrEqualTo(now + settings.DefaultCheckOutDuration)); Assert.That(await TestHarness.Published.SelectAsync <NotifyMemberDueDate>().Count(), Is.EqualTo(2), "NotifyMemberDueDate 메시지 2번 publish 안됨"); }
public async Task Should_Create_A_Saga_Instance() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <IReservationRequestedGlobalEvent>(new { ReservationId = reservationId, Timestamp = InVar.Timestamp, MemberId = memberId, BookId = bookId }); (await TestHarness.Consumed.Any <IReservationRequestedGlobalEvent>()).Should() .BeTrue("Message should be consumed by normal bus!"); (await SagaHarness.Consumed.Any <IReservationRequestedGlobalEvent>()).Should() .BeTrue("Message should be consumed by saga!"); //(await SagaHarness.Created.Any(x => x.CorrelationId == reservationId)).Should() // .BeTrue("A reservation saga state machine instance should be created!"); SagaHarness.Created.ContainsInState(reservationId, Machine, Machine.Requested).Should() .NotBeNull("A reservation saga state machine instance should be created!"); (await SagaHarness.Exists(reservationId, x => x.Requested)).HasValue.Should() .BeTrue("Reservation saga instance should exist!"); }
public async Task Should_renew_an_existing_checkout() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); DateTime now = DateTime.UtcNow; await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, InVar.Timestamp, MemberId = NewId.NextGuid() }); var existsId = await SagaHarness.Exists(checkOutId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); using var scope = Provider.CreateScope(); var requestClient = scope.ServiceProvider.GetRequiredService <IRequestClient <RenewCheckOut> >(); now = DateTime.UtcNow; var(renewed, notFound) = await requestClient.GetResponse <CheckOutRenewed, CheckOutNotFound>(new { checkOutId }); if (renewed.IsCompletedSuccessfully) { var response = await renewed; Assert.That(response.Message.DueDate, Is.GreaterThanOrEqualTo(now + TimeSpan.FromDays(14))); } Assert.That(notFound.IsCompletedSuccessfully, Is.False); }
public async Task Should_handle_in_other_order() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = InVar.Id, bookId, memberId, InVar.Timestamp }); Assert.IsTrue(await SagaHarness.Consumed.Any <BookCheckedOut>(x => x.Context.Message.BookId == bookId), "Message not consumed by saga"); Assert.That(await SagaHarness.Created.Any(x => x.Saga.BookId == bookId), "Saga not created"); ISagaInstance <ThankYou> instance = SagaHarness.Created.Select(x => x.Saga.BookId == bookId).First(); await TestHarness.Bus.Publish <BookReserved>(new { bookId, memberId, reservationId, Duration = TimeSpan.FromDays(14), InVar.Timestamp }); Assert.IsTrue(await SagaHarness.Consumed.Any <BookReserved>(x => x.Context.Message.BookId == bookId), "Message not consumed by saga"); Guid?existsId = await SagaHarness.Exists(instance.Saga.CorrelationId, x => x.Ready); Assert.IsTrue(existsId.HasValue, "Saga did not transition to Ready"); }
public async Task Should_create_a_saga_instance() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); DateTime now = DateTime.UtcNow; await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, InVar.Timestamp, MemberId = NewId.NextGuid() }); Assert.IsTrue(await TestHarness.Consumed.Any <BookCheckedOut>(), "Message not consumed"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookCheckedOut>(), "Message not consumed by saga"); Assert.That(await SagaHarness.Created.Any(x => x.CorrelationId == checkOutId)); var instance = SagaHarness.Created.ContainsInState(checkOutId, Machine, Machine.CheckedOut); Assert.IsNotNull(instance, "Saga instance not found"); Assert.That(instance.DueDate, Is.GreaterThanOrEqualTo(now + TimeSpan.FromDays(14))); var existsId = await SagaHarness.Exists(checkOutId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); Assert.IsTrue(await TestHarness.Published.Any <NotifyMemberDueDate>(), "Due Date Event Not Published"); }
public async Task Should_handle_the_fault_message_via_correlation() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, InVar.Timestamp, MemberId = memberId }); var existsId = await SagaHarness.Exists(checkOutId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); Assert.IsTrue(await TestHarness.Published.Any <AddBookToMemberCollection>(), "Add to collection not published"); Assert.IsTrue(await TestHarness.Consumed.Any <AddBookToMemberCollection>(), "Add not consumed"); Assert.IsTrue(await TestHarness.Published.Any <Fault <AddBookToMemberCollection> >(), "Fault not published"); Assert.IsTrue(await TestHarness.Consumed.Any <Fault <AddBookToMemberCollection> >(), "Fault not consumed"); }
public async Task BookSaga는_CheckedOut상태가_된다() { var checkOutId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307959123", Title = "Gone with the Wind" }); var bookSaga = SagaHarness.SagaOf(bookId); Assert.IsTrue(await bookSaga.ExistsAs(machine => machine.Available), "책이 추가된 BookSaga는 Available상태여야 함"); await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, Timestamp = InVar.Timestamp, MemberId = memberId }); Assert.IsTrue(await bookSaga.ExistsAs(machine => machine.CheckedOut), "BookSaga에서 책이 CheckOut된 상태여야함"); }
public async Task Should_mark_state_as_captured_when_payment_is_captured() { var paymentId = NewId.NextGuid(); await TestHarness.Bus.Publish <IPaymentAuthorized>(new { PaymentId = paymentId, AuthorizationCode = "PASS", TransactionId = "2452354325", AuthorizationExpiration = TimeSpan.FromHours(6) }); Assert.That(SagaHarness.Created.Select(x => x.CorrelationId == paymentId).Any(), Is.True); var instanceId = await SagaHarness.Exists(paymentId, x => x.Authorized); Assert.That(instanceId, Is.Not.Null); var instance = SagaHarness.Sagas.Contains(instanceId.Value); Assert.That(instance.AuthorizationCode, Is.EqualTo("PASS")); Assert.That(instance.TransactionId, Is.EqualTo("2452354325")); await TestHarness.Bus.Publish <IPaymentCaptured>(new { TransactionId = "2452354325" }); instanceId = await SagaHarness.Exists(paymentId, x => x.Captured); Assert.That(instanceId, Is.Not.Null); }
public async Task Should_change_state_to_checked_out() { var bookId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); var existsId = await SagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await TestHarness.Bus.Publish <BookCheckedOut>(new { BookId = bookId, InVar.Timestamp }); existsId = await SagaHarness.Exists(bookId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Saga was not checked out"); }
public async Task Should_handle_the_request_fault() { var checkOutId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var now = DateTime.UtcNow; await TestHarness.Bus.Publish <BookReturned>(new { CheckOutId = checkOutId, InVar.Timestamp, BookId = bookId, MemberId = memberId, CheckOutDate = now - TimeSpan.FromDays(28), DueDate = now - TimeSpan.FromDays(14), ReturnDate = now, }); Guid?existsId = await SagaHarness.Exists(checkOutId, x => x.ChargingFine); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); Assert.IsTrue(await TestHarness.Consumed.Any <ChargeMemberFine>(), "Fine not consumed"); Assert.IsTrue(await TestHarness.Consumed.Any <Fault <ChargeMemberFine> >(), "Fine charged"); }
public async Task 현재시각을_시각을_기준으로_만료기간이_갱신되지만_최대_허용_납기는_넘기지_않는다() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); var memberId = NewId.NextGuid(); DateTime checkOutTime = InVar.Timestamp; await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, Timestamp = checkOutTime, MemberId = memberId }); var checkOutSaga = SagaHarness.SagaOf(checkOutId); Assert.IsTrue(await checkOutSaga.ExistsAs(m => m.CheckedOut), "CheckOut 상태가 아님."); // RenewCheckOut 전송. var settings = Provider.GetRequiredService <CheckOutSettings>(); var requestClient = Provider.GetRequiredService <IRequestClient <RenewCheckOut> >(); var prev = Time.UtcNow; await Time.Advance(TimeSpan.FromDays(16)); var now = Time.UtcNow; Assert.That(now - prev, Is.GreaterThanOrEqualTo(TimeSpan.FromDays(16))); // 원래는 아래처럼 시도했는데, Chris는 다른 방법을 사용. // var (renewed, limitReached) = await requestClient.GetResponse<CheckOutRenewed, CheckOutDurationLimitReached>(new // { // CheckOutId = checkOutId // }); // Assert.IsTrue(limitReached.IsCompletedSuccessfully); // Assert.That((await limitReached).Message.DueDate, Is.LessThanOrEqualTo(now + settings.DefaultCheckOutDuration)); using (var request = requestClient.Create(new { CheckOutId = checkOutId })) { var renewed = request.GetResponse <CheckOutRenewed>(false); // false = "아직 보내지 마. 그냥 CheckOutRenewed 형 응답이 올지도 모른다는 것만 알아둬" var notFound = request.GetResponse <CheckOutNotFound>(false); var limitReached = request.GetResponse <CheckOutDurationLimitReached>(); // true = "응. 이제 Send 해" await Task.WhenAny(renewed, notFound, limitReached); Assert.That(renewed.IsCompletedSuccessfully, Is.False); Assert.That(notFound.IsCompletedSuccessfully, Is.False); Assert.That(limitReached.IsCompletedSuccessfully, Is.True); Assert.That((await limitReached).Message.DueDate, Is.LessThanOrEqualTo(now + settings.DefaultCheckOutDuration)); } }
public async Task Reservation이_더_이상_Reserved_상태가_아님() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var isbn = "1234567"; var bookAddedAt = new DateTime(2020, 12, 11); var bookRequestedAt = new DateTime(2020, 12, 25); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Timestamp = bookAddedAt, Isbn = isbn, Title = "Gone with the Wind" }); var existId = await BookSagaHarness.Exists(bookId, machine => machine.Available); Assert.IsTrue(existId.HasValue); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId, Duration = TimeSpan.FromDays(2) }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. // 이런식으로 하면, Timing Issue가 있을 수 있다. // Assert.IsNotNull(SagaHarness.Sagas.ContainsInState(reservationId, StateMachine, x => x.Reserved)); var reservationSaga = SagaHarness.SagaOf(reservationId); var bookSaga = BookSagaHarness.SagaOf(bookId); Assert.IsTrue(await reservationSaga.ExistsAs(m => m.Reserved), "ReservationSaga 가 Reserved 상태가 아님"); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Reserved), "BookSaga 가 Reserved 상태가 아님."); await Time.Advance(TimeSpan.FromDays(1)); Assert.IsTrue(await reservationSaga.ExistsAs(m => m.Reserved), "ReservationSaga 는 2일 중 1일만 지났으므로, 아직 Reserved 상태여야 함"); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Reserved), "BookSaga 는 여전히 Reserved 상태여야 함(2일이 만료일인데, 1일만 지난상태에서..)"); await Time.Advance(TimeSpan.FromDays(1)); // 하루 더 지나면... Assert.IsTrue(await reservationSaga.NotExists(), "ReservationSaga 는 Reserve 만기되면 사라져야 함."); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Available), "BookSaga 는 2일뒤 Available상태로 돌아와야 함."); }
public async Task Should_not_reserve_the_book() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); var existsId = await _bookSagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, InVar.Timestamp, MemberId = memberId, BookId = bookId, }); existsId = await SagaHarness.Exists(reservationId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); var reservation = SagaHarness.Sagas.ContainsInState(reservationId, Machine, x => x.Reserved); Assert.IsNotNull(reservation, "Reservation did not exist"); var secondReservationId = NewId.NextGuid(); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = secondReservationId, InVar.Timestamp, MemberId = NewId.NextGuid(), BookId = bookId, }); existsId = await SagaHarness.Exists(secondReservationId, x => x.Requested); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); var secondReservation = SagaHarness.Sagas.ContainsInState(secondReservationId, Machine, x => x.Requested); Assert.IsNotNull(secondReservation, "Reservation did not exist"); }
public async Task Should_mark_book_as_available() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); var existsId = await _bookSagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, InVar.Timestamp, Duration = TimeSpan.FromDays(2), MemberId = memberId, BookId = bookId, }); existsId = await SagaHarness.Exists(reservationId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Reservation was not reserved"); existsId = await _bookSagaHarness.Exists(bookId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await AdvanceSystemTime(TimeSpan.FromHours(24)); existsId = await SagaHarness.Exists(reservationId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Reservation was not still reserved"); await AdvanceSystemTime(TimeSpan.FromHours(24)); Guid?notExists = await SagaHarness.NotExists(reservationId); Assert.IsFalse(notExists.HasValue); existsId = await _bookSagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); }
public async Task Reserved상태에서_Finalize상태로_되어야함() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var isbn = "1234567"; var bookAddedAt = new DateTime(2020, 12, 11); var bookRequestedAt = new DateTime(2020, 12, 25); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Timestamp = bookAddedAt, Isbn = isbn, Title = "Gone with the Wind" }); var existId = await BookSagaHarness.Exists(bookId, machine => machine.Available); Assert.IsTrue(existId.HasValue); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. // 이런식으로 하면, Timing Issue가 있을 수 있다. // Assert.IsNotNull(SagaHarness.Sagas.ContainsInState(reservationId, StateMachine, x => x.Reserved)); var reservationSaga = SagaHarness.SagaOf(reservationId); var bookSaga = BookSagaHarness.SagaOf(bookId); Assert.IsTrue(await reservationSaga.ExistsAs(m => m.Reserved), "ReservationSaga 가 Reserved 상태가 아님"); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Reserved), "BookSaga 가 Reserved 상태가 아님."); await TestHarness.Bus.Publish <BookCheckedOut>(new { BookId = bookId, Timestamp = InVar.Timestamp, MemberId = memberId }); Assert.IsTrue(await reservationSaga.NotExists()); Assert.IsTrue(await bookSaga.ExistsAs(m => m.CheckedOut)); }
public async Task 중복해서_Book_Reserve_하지_않는다() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var isbn = "1234567"; var bookAddedAt = new DateTime(2020, 12, 11); var bookRequestedAt = new DateTime(2020, 12, 25); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Timestamp = bookAddedAt, Isbn = isbn, Title = "Gone with the Wind" }); var existId = await BookSagaHarness.Exists(bookId, machine => machine.Available); Assert.IsTrue(existId.HasValue); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId }); var reservationSaga = SagaHarness.SagaOf(reservationId); var bookSaga = BookSagaHarness.SagaOf(bookId); Assert.IsTrue(await reservationSaga.ExistsAs(m => m.Reserved), "ReservationSaga 가 Reserved 상태가 아님"); Assert.IsTrue(await bookSaga.ExistsAs(m => m.Reserved), "BookSaga 가 Reserved 상태가 아님."); // 중복 ReservationRequested! var reservationId2 = NewId.NextGuid(); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId2, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId }); var reservationSaga2 = SagaHarness.SagaOf(reservationId2); Assert.IsTrue(await reservationSaga2.ExistsAs(m => m.Requested), // Not Reserved! "이미 Reserve된 책에 대한 중복 Reservation요청에는 Reserved 상태가 되면 안된다"); }
public async Task The_reservation_should_be_removed() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); var existsId = await _bookSagaHarness.Exists(bookId, x => x.Available); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, InVar.Timestamp, MemberId = memberId, BookId = bookId, }); existsId = await SagaHarness.Exists(reservationId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Reservation was not reserved"); existsId = await _bookSagaHarness.Exists(bookId, x => x.Reserved); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); await TestHarness.Bus.Publish <BookCheckedOut>(new { BookId = bookId, InVar.Timestamp }); Guid?notExists = await SagaHarness.NotExists(reservationId); Assert.IsFalse(notExists.HasValue); existsId = await _bookSagaHarness.Exists(bookId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Book was not checked out"); }
public async Task Should_renew_an_existing_checkout_up_to_the_limit() { var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); DateTime now = DateTime.UtcNow; DateTime checkedOutAt = DateTime.UtcNow; await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, InVar.Timestamp, MemberId = NewId.NextGuid() }); var existsId = await SagaHarness.Exists(checkOutId, x => x.CheckedOut); Assert.IsTrue(existsId.HasValue, "Saga did not exist"); using var scope = Provider.CreateScope(); var requestClient = scope.ServiceProvider.GetRequiredService <IRequestClient <RenewCheckOut> >(); now = DateTime.UtcNow; using var request = requestClient.Create(new { checkOutId }); var renewed = request.GetResponse <CheckOutRenewed>(false); var notFound = request.GetResponse <CheckOutNotFound>(false); var limitReached = request.GetResponse <CheckOutDurationLimitReached>(); Task whenAny = await Task.WhenAny(renewed, notFound, limitReached); Assert.That(notFound.IsCompletedSuccessfully, Is.False); Assert.That(renewed.IsCompletedSuccessfully, Is.False); Assert.That(limitReached.IsCompletedSuccessfully, Is.True); if (limitReached.IsCompletedSuccessfully) { var response = await limitReached; Assert.That(response.Message.DueDate, Is.LessThan(now + TimeSpan.FromDays(14))); Assert.That(response.Message.DueDate, Is.GreaterThanOrEqualTo(checkedOutAt + TimeSpan.FromDays(13))); } }
public async Task BookCheckedOut_과_BookReserved_을_수신하면_Ready_상태가_된다() { var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var checkOutId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookCheckedOut>(new { CheckOutId = checkOutId, BookId = bookId, Timestamp = InVar.Timestamp, MemberId = memberId }); var message = TestHarness.Published.Select <BookCheckedOut>().Last(); var messageId = message.Context.MessageId ?? Guid.Empty; Assert.That(messageId, Is.Not.EqualTo(Guid.Empty)); Assert.IsTrue(await TestHarness.Consumed.Any <BookCheckedOut>(), "Bus 메시지 수신안됨"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookCheckedOut>(), "Saga에서 메시지 수신안됨"); await TestHarness.Bus.Publish <BookReserved>(new { ReservationId = reservationId, Timestamp = InVar.Timestamp, Duration = TimeSpan.FromDays(14), MemberId = memberId, BookId = bookId }); await Task.Delay(200); // TODO 어떻게 이런 Sleep 을 하지 않을 수 있을까. 이게 없으면 아래 수신 테스트가 실패함. Assert.IsTrue(await TestHarness.Consumed.Any <BookReserved>(), "Bus 메시지 수신안됨"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookReserved>(), "Saga에서 메시지 수신안됨"); var saga = SagaHarness.SagaOf(messageId); Assert.IsTrue(await saga.Exists(), "Saga 생성되지 않음"); Assert.IsTrue(await saga.ExistsAs(m => m.Ready), "Saga가 Ready 상태가 아님"); Assert.That(saga.Instance.BookId, Is.EqualTo(bookId)); Assert.That(saga.Instance.MemberId, Is.EqualTo(memberId)); Assert.That(saga.Instance.ReservationId, Is.EqualTo(reservationId)); }
public async Task 납기를_넘은_경우_벌금을_물린다(bool fineOverriden) { var checkOutId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var now = Time.UtcNow; var dueDate = now - TimeSpan.FromDays(3); var returnedAt = now; var messageId = NewId.NextGuid(); _fineCharger.NextTimeOverride = fineOverriden; await TestHarness.Bus.Publish <BookReturned>(new { CheckOutId = checkOutId, Timestamp = InVar.Timestamp, BookId = bookId, MemberId = memberId, DueDate = dueDate, ReturnedAt = returnedAt, __MessageId = messageId }); // 이렇게 해도 되네.. 콕 찝어서 딱 그 메시지! 라고 하려면, message id 를 사용해야 겠다. Assert.IsTrue(await TestHarness.Consumed.Any <BookReturned>(x => x.Context.MessageId == messageId)); Assert.IsTrue(await SagaHarness.Consumed.Any <BookReturned>(x => x.Context.MessageId == messageId)); Assert.IsTrue(await TestHarness.Published.Any <ChargeMemberFine>()); Assert.IsTrue(await TestHarness.Consumed.Any <ChargeMemberFine>()); if (fineOverriden) { Assert.IsTrue(await TestHarness.Sent.Any <FineOverriden>()); Assert.IsTrue(await TestHarness.Consumed.Any <FineOverriden>()); } else { Assert.IsTrue(await TestHarness.Sent.Any <FineCharged>()); Assert.IsTrue(await TestHarness.Consumed.Any <FineCharged>()); } var saga = SagaHarness.SagaOf(checkOutId); Assert.IsTrue(await saga.ExistsAs(m => m.Complete)); }
public async Task Book을_예약해야_한다() { var bookId = NewId.NextGuid(); var reservationId = NewId.NextGuid(); var memberId = NewId.NextGuid(); var isbn = "1234567"; var bookAddedAt = new DateTime(2020, 12, 11); var bookRequestedAt = new DateTime(2020, 12, 25); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Timestamp = bookAddedAt, Isbn = isbn, Title = "Gone with the Wind" }); var existId = await BookSagaHarness.Exists(bookId, machine => machine.Available); Assert.IsTrue(existId.HasValue); await TestHarness.Bus.Publish <ReservationRequested>(new { ReservationId = reservationId, Timestamp = bookRequestedAt, MemberId = memberId, BookId = bookId }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. // 이런식으로 하면, Timing Issue가 있을 수 있다. // Assert.IsNotNull(SagaHarness.Sagas.ContainsInState(reservationId, StateMachine, x => x.Reserved)); Assert.That(await SagaHarness.Exists(reservationId, machine => machine.Reserved), Is.EqualTo(reservationId), "ReservationSaga 가 Reserved 상태가 아님"); // Assert.IsNotEmpty(SagaHarness.Sagas.Select(context => // context.CorrelationId == reservationId && // context.RequestedAt == bookRequestedAt // )); }
public async Task 새로운_BookId_메시지를_받으면_새로운_Saga_Instance가_만들어진다() { var bookId = NewId.NextGuid(); await TestHarness.Bus.Publish <BookAdded>(new { BookId = bookId, Isbn = "0307959123", Title = "Gone with the Wind" }); // 실제로 Message를 Serialize 하고, 전송하는 모든 과정이 Simulation 된다. Assert.IsTrue(await TestHarness.Consumed.Any <BookAdded>(), "메시지 수신이 안됨"); Assert.IsTrue(await SagaHarness.Consumed.Any <BookAdded>(), "Saga에 의해 메시지 처리가 안됨"); Assert.That(await SagaHarness.Created.Any(x => x.CorrelationId == bookId), "생성된 Saga의 CorrelationId 는 Book Id 여야 함"); // var instance = SagaHarness.Created.ContainsInState(bookId, StateMachine, StateMachine.Available); // var wrongBookId = NewId.NextGuid(); Assert.That(await SagaHarness.Exists(bookId, machine => machine.Available), Is.EqualTo(bookId), "수신된 메시지의 BookId 에 해당하는 Saga가 없음."); }
public async Task Should_Mark_Book_As_Available() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <IBookAddedGlobalEvent>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); (await _bookSagaHarness.Exists(bookId, x => x.Available)).HasValue.Should() .BeTrue("Book Saga state machine should be existed at Available State!"); await TestHarness.Bus.Publish <IReservationRequestedGlobalEvent>(new { ReservationId = reservationId, Timestamp = InVar.Timestamp, MemberId = memberId, BookId = bookId }); (await SagaHarness.Exists(reservationId, x => x.Reserved)).HasValue.Should() .BeTrue("Reservation Saga state machine should be existed at Reserved State!"); (await _bookSagaHarness.Exists(bookId, x => x.Reserved)).HasValue.Should() .BeTrue("Book Saga state machine should be existed at Reserved State!"); // Turn the clock toward 24 hours await AdvanceSystemTime(TimeSpan.FromHours(24)); (await SagaHarness.NotExists(reservationId)).HasValue.Should().BeFalse( "The Reservation state machine should be finalized already cause of reservation expired itself!"); (await _bookSagaHarness.Exists(bookId, x => x.Available)).HasValue.Should().BeTrue( "Book Saga state machine should be existed at Available State cause the reservation was expired!"); }
public async Task Should_Reserved_The_Book() { var reservationId = NewId.NextGuid(); var bookId = NewId.NextGuid(); var memberId = NewId.NextGuid(); await TestHarness.Bus.Publish <IBookAddedGlobalEvent>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); var existsId = await _bookSagaHarness.Exists(bookId, x => x.Available); existsId.HasValue.Should().BeTrue("Book saga state instance should exists!"); await TestHarness.Bus.Publish <IReservationRequestedGlobalEvent>(new { ReservationId = reservationId, Timestamp = InVar.Timestamp, MemberId = memberId, BookId = bookId }); (await SagaHarness.Consumed.Any <IReservationRequestedGlobalEvent>()).Should() .BeTrue("Message should be consumed by Reservation Saga!"); (await _bookSagaHarness.Consumed.Any <IReservationRequestedGlobalEvent>()).Should() .BeTrue("Message should be consumed by Book Saga!"); existsId = await SagaHarness.Exists(reservationId, x => x.Reserved); existsId.HasValue.Should().BeTrue("Reservation saga state instance should exists!"); SagaHarness.Sagas.ContainsInState(reservationId, Machine, x => x.Reserved).Should() .NotBeNull("Reservation saga state instance should exists on [Reserved] state!"); }
public async Task Should_Create_A_Saga_Instance() { var bookId = NewId.NextGuid(); await TestHarness.Bus.Publish <IBookAddedGlobalEvent>(new { BookId = bookId, Isbn = "0307969959", Title = "Neuromancer" }); (await TestHarness.Consumed.Any <IBookAddedGlobalEvent>()).Should() .BeTrue($"The {nameof(IBookAddedGlobalEvent)} should be consumed by Masstransit!"); (await SagaHarness.Consumed.Any <IBookAddedGlobalEvent>()).Should() .BeTrue($"The {nameof(IBookAddedGlobalEvent)} should be consumed by Saga!"); (await SagaHarness.Created.Any(x => x.CorrelationId == bookId)).Should() .BeTrue("The correlationId of the book should be match!"); SagaHarness.Created.ContainsInState(bookId, Machine, Machine.Available).Should() .NotBeNull("Saga instance of the book should be exists!"); (await SagaHarness.Exists(bookId, x => x.Available)).HasValue.Should() .BeTrue("Saga instance should exists!"); Output.Information("Ran in here"); }