public async Task ignore_duplicate_month_same_rank()
        {
            // GIVEN
            User user = MockUser("user", monthsSubscribed: 2, SubscriptionTier.Tier1, loyaltyLeague: 2);
            Mock <IBank <User> >        bankMock                = new();
            Mock <IUserRepo>            userRepoMock            = new();
            Mock <ISubscriptionLogRepo> subscriptionLogRepoMock = new();
            ISubscriptionProcessor      subscriptionProcessor   = new SubscriptionProcessor(
                NullLogger <SubscriptionProcessor> .Instance,
                bankMock.Object, userRepoMock.Object, subscriptionLogRepoMock.Object, Mock.Of <ILinkedAccountRepo>());

            // WHEN
            ISubscriptionProcessor.SubResult subResult = await subscriptionProcessor.ProcessSubscription(
                new SubscriptionInfo(
                    user, NumMonths : 2, StreakMonths : 2, SubscriptionTier.Tier1, PlanName : "Sub Plan Name",
                    Instant.MinValue, Message : "Repeated", ImmutableList <EmoteOccurrence> .Empty));

            // THEN
            // negative result
            Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.SameMonth>(subResult);
            var sameMonthResult = (ISubscriptionProcessor.SubResult.SameMonth)subResult;

            Assert.That(sameMonthResult.Month, Is.EqualTo(2));
            // no tokens were awarded
            bankMock.VerifyNoOtherCalls();
            // no user data was adjusted
            userRepoMock.Verify(r => r.SetIsSubscribed(It.IsAny <User>(), It.IsAny <bool>()), Times.Never);
            userRepoMock.Verify(r => r.SetSubscriptionInfo(It.IsAny <User>(), It.IsAny <int>(),
                                                           It.IsAny <SubscriptionTier>(), It.IsAny <int>(), It.IsAny <Instant>()), Times.Never);
            // no subscription was logged
            subscriptionLogRepoMock.VerifyNoOtherCalls();
        }
        public async Task gracefully_handle_subbed_user_without_sub_tier_same_month()
        {
            // GIVEN
            Instant subscribedAt    = Instant.FromUnixTimeSeconds(123);
            User    userWithoutTier = MockUser("user", monthsSubscribed: 1, null, loyaltyLeague: 1);
            User    userWithTier    = MockUser("user", monthsSubscribed: 1, SubscriptionTier.Tier1, loyaltyLeague: 1);

            Mock <ILogger <SubscriptionProcessor> > logMock     = new();
            Mock <IUserRepo>            userRepoMock            = new();
            Mock <ISubscriptionLogRepo> subscriptionLogRepoMock = new();
            ISubscriptionProcessor      subscriptionProcessor   = new SubscriptionProcessor(
                logMock.Object, Mock.Of <IBank <User> >(), userRepoMock.Object,
                subscriptionLogRepoMock.Object, Mock.Of <ILinkedAccountRepo>());

            userRepoMock.Setup(r => r.SetSubscriptionInfo(userWithoutTier, It.IsAny <int>(), It.IsAny <SubscriptionTier>(),
                                                          It.IsAny <int>(), It.IsAny <Instant?>())).ReturnsAsync(userWithTier);

            // WHEN
            ISubscriptionProcessor.SubResult subResult = await subscriptionProcessor.ProcessSubscription(
                new SubscriptionInfo(
                    userWithoutTier, NumMonths : 1, StreakMonths : 1, SubscriptionTier.Tier1, PlanName : "Tier 1",
                    subscribedAt, Message : "HeyGuys", ImmutableList <EmoteOccurrence> .Empty));

            // THEN
            Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.SameMonth>(subResult);
            logMock.VerifyLog(l => l.LogWarning(
                                  $"Subscriber {userWithoutTier} has no subscription tier recorded. Assuming Tier 1"));
            userRepoMock.Verify(r => r.SetSubscriptionInfo(userWithoutTier, 1,
                                                           SubscriptionTier.Tier1, userWithoutTier.LoyaltyLeague, userWithoutTier.SubscriptionUpdatedAt));
        }
        public async Task accept_duplicate_month_higher_rank()
        {
            // GIVEN
            User    user         = MockUser("user", monthsSubscribed: 2, SubscriptionTier.Prime, loyaltyLeague: 2);
            Instant subscribedAt = Instant.FromUnixTimeSeconds(123);
            Mock <IBank <User> >        bankMock                = new();
            Mock <IUserRepo>            userRepoMock            = new();
            Mock <ISubscriptionLogRepo> subscriptionLogRepoMock = new();
            ISubscriptionProcessor      subscriptionProcessor   = new SubscriptionProcessor(
                NullLogger <SubscriptionProcessor> .Instance,
                bankMock.Object, userRepoMock.Object, subscriptionLogRepoMock.Object, Mock.Of <ILinkedAccountRepo>());

            userRepoMock.Setup(r => r.SetIsSubscribed(user, It.IsAny <bool>())).ReturnsAsync(user);
            userRepoMock.Setup(r => r.SetSubscriptionInfo(user, It.IsAny <int>(), It.IsAny <SubscriptionTier>(),
                                                          It.IsAny <int>(), It.IsAny <Instant>())).ReturnsAsync(user);

            // WHEN
            ISubscriptionProcessor.SubResult subResult = await subscriptionProcessor.ProcessSubscription(
                new SubscriptionInfo(
                    user, NumMonths : 2, StreakMonths : 2, SubscriptionTier.Tier3, PlanName : "Sub Plan Name",
                    subscribedAt, Message : "Repeated", ImmutableList <EmoteOccurrence> .Empty));

            // THEN
            const int expectedTokens = 14 + 16 + 18 + 20; // Tier 1 -> Tier 3: 4 loyalty completions difference

            // negative result
            Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.Ok>(subResult);
            var okResult = (ISubscriptionProcessor.SubResult.Ok)subResult;

            Assert.That(okResult.NewLoyaltyLeague, Is.EqualTo(6));
            Assert.That(okResult.CumulativeMonths, Is.EqualTo(2));
            // only tokens for rank upgrade were awarded
            IDictionary <string, object?> expectedData = new Dictionary <string, object?>
            {
                ["previous_months_subscribed"] = 2,
                ["new_months_subscribed"]      = 2,
                ["months_difference"]          = 0,
                ["previous_loyalty_tier"]      = 2,
                ["new_loyalty_tier"]           = 6,
                ["loyalty_completions"]        = 4,
            };

            bankMock.Verify(b => b.PerformTransaction(
                                new Transaction <User>(user, expectedTokens, "subscription", expectedData),
                                CancellationToken.None), Times.Once);
            // verify user data was adjusted
            userRepoMock.Verify(r => r.SetIsSubscribed(user, true), Times.Once);
            userRepoMock.Verify(r => r.SetSubscriptionInfo(user, 2, SubscriptionTier.Tier3, 6, subscribedAt),
                                Times.Once);
            // verify subscription was logged
            subscriptionLogRepoMock.Verify(r => r.LogSubscription(
                                               user.Id, subscribedAt,
                                               2, 2, 2, 0,
                                               2, 6, 4, expectedTokens,
                                               "Repeated", SubscriptionTier.Tier3, "Sub Plan Name"),
                                           Times.Once);
        }
Exemplo n.º 4
0
        public async Task <(ISubscriptionProcessor.SubResult, ISubscriptionProcessor.SubGiftResult)> ProcessSubscriptionGift(
            SubscriptionGiftInfo subscriptionGiftInfo)
        {
            SubscriptionInfo subscriptionInfo = subscriptionGiftInfo.SubscriptionInfo;

            ISubscriptionProcessor.SubResult subResult =
                await ProcessSubscription(subscriptionInfo);

            bool isLinkedAccount = await _linkedAccountRepo.AreLinked(
                subscriptionGiftInfo.Gifter.Id,
                subscriptionInfo.Subscriber.Id);

            if (isLinkedAccount)
            {
                return(subResult, new ISubscriptionProcessor.SubGiftResult.LinkedAccount());
            }

            if (subResult is ISubscriptionProcessor.SubResult.SameMonth {
                Month : var month
            })
        public async Task handle_regular_subscription()
        {
            // GIVEN
            const SubscriptionTier subscriptionTier = SubscriptionTier.Tier2;
            Instant subscribedAt = Instant.FromUnixTimeSeconds(123);
            User    user         = MockUser("user", monthsSubscribed: 2, subscriptionTier, loyaltyLeague: 4);

            Mock <IBank <User> >        bankMock                = new();
            Mock <IUserRepo>            userRepoMock            = new();
            Mock <ISubscriptionLogRepo> subscriptionLogRepoMock = new();
            ISubscriptionProcessor      subscriptionProcessor   = new SubscriptionProcessor(
                NullLogger <SubscriptionProcessor> .Instance,
                bankMock.Object, userRepoMock.Object, subscriptionLogRepoMock.Object, Mock.Of <ILinkedAccountRepo>());

            userRepoMock.Setup(r => r.SetIsSubscribed(user, It.IsAny <bool>())).ReturnsAsync(user);
            userRepoMock.Setup(r => r.SetSubscriptionInfo(user, It.IsAny <int>(), It.IsAny <SubscriptionTier>(),
                                                          It.IsAny <int>(), It.IsAny <Instant>())).ReturnsAsync(user);

            // WHEN
            ISubscriptionProcessor.SubResult subResult = await subscriptionProcessor.ProcessSubscription(
                new SubscriptionInfo(
                    user, NumMonths : 3, StreakMonths : 2, subscriptionTier, PlanName : "Tier 2",
                    subscribedAt, Message : "HeyGuys", ImmutableList <EmoteOccurrence> .Empty));

            // THEN
            const int expectedTokens = 10 + (2 * 4) + 10 + (2 * 5); // per rank: 10 base tokens + 2 tokens per league

            // verify result
            Assert.IsInstanceOf <ISubscriptionProcessor.SubResult.Ok>(subResult);
            var okResult = (ISubscriptionProcessor.SubResult.Ok)subResult;

            Assert.That(okResult.CumulativeMonths, Is.EqualTo(3));
            Assert.That(okResult.DeltaTokens, Is.EqualTo(expectedTokens));
            Assert.That(okResult.OldLoyaltyLeague, Is.EqualTo(4));
            Assert.That(okResult.NewLoyaltyLeague, Is.EqualTo(6));
            Assert.IsFalse(okResult.SubCountCorrected);

            // verify tokens were awarded
            IDictionary <string, object?> expectedData = new Dictionary <string, object?>
            {
                ["previous_months_subscribed"] = 2,
                ["new_months_subscribed"]      = 3,
                ["months_difference"]          = 1,
                ["previous_loyalty_tier"]      = 4,
                ["new_loyalty_tier"]           = 6,
                ["loyalty_completions"]        = 2,
            };

            bankMock.Verify(b =>
                            b.PerformTransaction(new Transaction <User>(user, expectedTokens, "subscription", expectedData),
                                                 CancellationToken.None), Times.Once);

            // verify user data was adjusted
            userRepoMock.Verify(r => r.SetIsSubscribed(user, true), Times.Once);
            userRepoMock.Verify(r => r.SetSubscriptionInfo(user, 3, subscriptionTier, 6, subscribedAt), Times.Once);

            // verify subscription was logged
            subscriptionLogRepoMock.Verify(r => r.LogSubscription(
                                               user.Id, subscribedAt,
                                               2, 2, 3, 1,
                                               4, 6, 2, expectedTokens,
                                               "HeyGuys", subscriptionTier, "Tier 2"),
                                           Times.Once);
        }