Example #1
0
        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));
        }
Example #2
0
        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");
        }
Example #4
0
        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 안됨");
        }
Example #7
0
        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!");
        }
Example #8
0
        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);
        }
Example #9
0
        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");
        }
Example #10
0
        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");
        }
Example #11
0
        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);
        }
Example #14
0
        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));
            }
        }
Example #17
0
        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");
        }
Example #20
0
        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));
        }
Example #21
0
        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");
        }
Example #23
0
        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));
        }
Example #26
0
        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가 없음.");
        }
Example #28
0
        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!");
        }
Example #29
0
        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");
        }