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 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 새로운_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 안됨");
        }
Beispiel #4
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));
        }
        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));
            }
        }
Beispiel #6
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상태로 돌아와야 함.");
        }
Beispiel #7
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 상태가 되면 안된다");
        }
Beispiel #8
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));
        }
        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));
        }