diff --git a/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordRewardServiceTest.cs b/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordRewardServiceTest.cs new file mode 100644 index 000000000..7b2f2a6f3 --- /dev/null +++ b/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordRewardServiceTest.cs @@ -0,0 +1,98 @@ +using DragaliaAPI.Database.Entities; +using DragaliaAPI.Features.Dungeon; +using DragaliaAPI.Features.Dungeon.Record; +using DragaliaAPI.Features.Event; +using DragaliaAPI.Features.Reward; +using DragaliaAPI.Models; +using DragaliaAPI.Models.Generated; +using DragaliaAPI.Shared.Definitions.Enums; +using DragaliaAPI.Shared.MasterAsset; +using Microsoft.Extensions.Logging; + +namespace DragaliaAPI.Test.Features.Dungeon.Record; + +public class DungeonRecordRewardServiceTest +{ + private readonly Mock mockQuestCompletionService; + private readonly Mock mockRewardService; + private readonly Mock mockAbilityCrestMultiplierService; + private readonly Mock mockEventDropService; + private readonly Mock> mockLogger; + + private readonly IDungeonRecordRewardService dungeonRecordRewardService; + + public DungeonRecordRewardServiceTest() + { + this.mockQuestCompletionService = new(MockBehavior.Strict); + this.mockRewardService = new(MockBehavior.Strict); + this.mockAbilityCrestMultiplierService = new(MockBehavior.Strict); + this.mockEventDropService = new(MockBehavior.Strict); + this.mockLogger = new(MockBehavior.Loose); + + this.dungeonRecordRewardService = new DungeonRecordRewardService( + this.mockQuestCompletionService.Object, + this.mockRewardService.Object, + this.mockAbilityCrestMultiplierService.Object, + this.mockEventDropService.Object, + this.mockLogger.Object + ); + } + + [Fact] + public async Task ProcessQuestMissionCompletion_SetsEntityProperties() + { + int questId = 225021101; + DbQuest questEntity = + new() + { + DeviceAccountId = "id", + QuestId = questId, + PlayCount = 0, + IsMissionClear1 = false, + IsMissionClear2 = false, + IsMissionClear3 = false, + }; + + List firstClearRewards = + new() + { + new() + { + id = 0, + quantity = 2, + type = EntityTypes.Wyrmite + } + }; + + PlayRecord playRecord = new(); + DungeonSession session = + new() { QuestData = MasterAsset.QuestData[questId], Party = null! }; + QuestMissionStatus status = + new( + new[] { true, true, true }, + new List(), + new List() + ); + + this.mockQuestCompletionService + .Setup(x => x.CompleteQuestMissions(session, new[] { false, false, false }, playRecord)) + .ReturnsAsync(status); + this.mockQuestCompletionService + .Setup(x => x.GrantFirstClearRewards(questId)) + .ReturnsAsync(firstClearRewards); + + ( + await this.dungeonRecordRewardService.ProcessQuestMissionCompletion( + playRecord, + session, + questEntity + ) + ) + .Should() + .Be(status); + + questEntity.IsMissionClear1.Should().BeTrue(); + questEntity.IsMissionClear2.Should().BeTrue(); + questEntity.IsMissionClear3.Should().BeTrue(); + } +} diff --git a/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordServiceTest.cs b/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordServiceTest.cs new file mode 100644 index 000000000..1293f3c66 --- /dev/null +++ b/DragaliaAPI.Test/Features/Dungeon/Record/DungeonRecordServiceTest.cs @@ -0,0 +1,204 @@ +using Castle.Core.Logging; +using DragaliaAPI.Database.Entities; +using DragaliaAPI.Database.Repositories; +using DragaliaAPI.Features.Dungeon.Record; +using DragaliaAPI.Features.Missions; +using DragaliaAPI.Features.Player; +using DragaliaAPI.Models; +using DragaliaAPI.Models.Generated; +using DragaliaAPI.Services; +using DragaliaAPI.Shared.Definitions.Enums; +using DragaliaAPI.Shared.MasterAsset; +using DragaliaAPI.Test.Utils; +using Humanizer; +using Microsoft.Extensions.Logging; +using StackExchange.Redis; + +namespace DragaliaAPI.Test.Features.Dungeon.Record; + +public class DungeonRecordServiceTest +{ + private readonly Mock mockDungeonRewardService; + private readonly Mock mockQuestRepository; + private readonly Mock mockMissionProgressionService; + private readonly Mock mockUserService; + private readonly Mock mockTutorialService; + private readonly Mock> mockLogger; + + private readonly IDungeonRecordService dungeonRecordService; + + public DungeonRecordServiceTest() + { + this.mockDungeonRewardService = new(MockBehavior.Strict); + this.mockQuestRepository = new(MockBehavior.Strict); + this.mockMissionProgressionService = new(MockBehavior.Strict); + this.mockUserService = new(MockBehavior.Strict); + this.mockTutorialService = new(MockBehavior.Strict); + this.mockLogger = new(MockBehavior.Loose); + + this.dungeonRecordService = new DungeonRecordService( + this.mockDungeonRewardService.Object, + this.mockQuestRepository.Object, + this.mockMissionProgressionService.Object, + this.mockUserService.Object, + this.mockTutorialService.Object, + this.mockLogger.Object + ); + + this.mockTutorialService.Setup(x => x.AddTutorialFlag(1022)).ReturnsAsync(new List()); + + CommonAssertionOptions.ApplyTimeOptions(); + } + + [Fact] + public async Task GenerateIngameResultData_CallsExpectedMethods() + { + int lSurtrSoloId = 232031101; + + DungeonSession session = + new() + { + QuestData = MasterAsset.QuestData[lSurtrSoloId], + Party = new List(), + StartTime = DateTimeOffset.UtcNow + }; + PlayRecord playRecord = new() { time = 10, }; + + DbQuest mockQuest = + new() + { + DeviceAccountId = "id", + QuestId = lSurtrSoloId, + State = 0, + BestClearTime = 999 + }; + + List dropList = + new() + { + new() + { + id = (int)Materials.FirestormRuby, + quantity = 10, + type = EntityTypes.Material + } + }; + + List eventDrops = + new() + { + new() + { + id = (int)Materials.WoodlandHerbs, + quantity = 20, + type = EntityTypes.Material + } + }; + + List scoreMissionSuccessLists = + new() + { + new() + { + score_mission_complete_type = QuestCompleteType.LimitFall, + score_target_value = 100, + } + }; + + List passiveUpLists = + new() + { + new() { passive_id = 1, progress = 2 } + }; + + int takeCoin = 10; + int takeMana = 20; + int takeAccumulatePoint = 30; + int takeBoostAccumulatePoint = 40; + + this.mockQuestRepository + .Setup(x => x.GetQuestDataAsync(lSurtrSoloId)) + .ReturnsAsync(mockQuest); + + this.mockMissionProgressionService.Setup(x => x.OnQuestCleared(lSurtrSoloId)); + + this.mockUserService + .Setup(x => x.RemoveStamina(StaminaType.Single, 40)) + .Returns(Task.CompletedTask); + this.mockUserService + .Setup(x => x.AddExperience(400)) + .ReturnsAsync(new PlayerLevelResult(true, 100, 50)); + + this.mockDungeonRewardService + .Setup(x => x.ProcessEnemyDrops(playRecord, session)) + .ReturnsAsync((dropList, takeMana, takeCoin)); + this.mockDungeonRewardService + .Setup(x => x.ProcessEventRewards(playRecord, session)) + .ReturnsAsync( + new DungeonRecordRewardService.EventRewardData( + scoreMissionSuccessLists, + takeAccumulatePoint, + takeBoostAccumulatePoint, + passiveUpLists, + eventDrops + ) + ); + + IngameResultData ingameResultData = + await this.dungeonRecordService.GenerateIngameResultData( + "dungeonKey", + playRecord, + session + ); + + ingameResultData + .Should() + .BeEquivalentTo( + new IngameResultData() + { + dungeon_key = "dungeonKey", + play_type = QuestPlayType.Default, + quest_id = lSurtrSoloId, + is_host = true, + quest_party_setting_list = session.Party, + start_time = session.StartTime, + end_time = DateTimeOffset.UtcNow, + reborn_count = playRecord.reborn_count, + total_play_damage = playRecord.total_play_damage, + is_clear = true, + current_play_count = 1, + reward_record = new() + { + drop_all = dropList.Concat(eventDrops).ToList(), + take_boost_accumulate_point = takeBoostAccumulatePoint, + take_accumulate_point = takeAccumulatePoint, + take_coin = takeCoin, + take_astral_item_quantity = 0, + player_level_up_fstone = 50, + }, + grow_record = new() + { + take_mana = takeMana, + take_player_exp = 400, + take_chara_exp = 1, + bonus_factor = 1, + mana_bonus_factor = 1, + chara_grow_record = new List() + }, + event_passive_up_list = passiveUpLists, + score_mission_success_list = scoreMissionSuccessLists, + is_best_clear_time = true, + clear_time = playRecord.time, + } + ); + + mockQuest.State.Should().Be(3); + + this.mockDungeonRewardService.VerifyAll(); + this.mockQuestRepository.VerifyAll(); + this.mockMissionProgressionService.VerifyAll(); + this.mockUserService.VerifyAll(); + this.mockTutorialService.VerifyAll(); + this.mockLogger.VerifyAll(); + } +} diff --git a/DragaliaAPI.Test/Services/HelperServiceTest.cs b/DragaliaAPI.Test/Services/HelperServiceTest.cs index 68a2995b7..a10793ae9 100644 --- a/DragaliaAPI.Test/Services/HelperServiceTest.cs +++ b/DragaliaAPI.Test/Services/HelperServiceTest.cs @@ -1,12 +1,21 @@ using AutoMapper; +using DragaliaAPI.Database.Repositories; +using DragaliaAPI.Features.Dungeon; +using DragaliaAPI.Features.Dungeon.Record; using DragaliaAPI.Models.Generated; using DragaliaAPI.Services; using DragaliaAPI.Services.Game; +using Microsoft.Extensions.Logging; namespace DragaliaAPI.Test.Services; public class HelperServiceTest { + private readonly Mock mockPartyRepository; + private readonly Mock mockDungeonRepository; + private readonly Mock mockUserDataRepository; + private readonly Mock> mockLogger; + private readonly IHelperService helperService; private readonly IMapper mapper; @@ -16,7 +25,18 @@ public HelperServiceTest() cfg => cfg.AddMaps(typeof(Program).Assembly) ).CreateMapper(); - this.helperService = new HelperService(this.mapper); + this.mockPartyRepository = new(MockBehavior.Strict); + this.mockDungeonRepository = new(MockBehavior.Strict); + this.mockUserDataRepository = new(MockBehavior.Strict); + this.mockLogger = new(MockBehavior.Loose); + + this.helperService = new HelperService( + this.mockPartyRepository.Object, + this.mockDungeonRepository.Object, + this.mockUserDataRepository.Object, + this.mapper, + this.mockLogger.Object + ); } [Fact] diff --git a/DragaliaAPI/Features/Dungeon/Record/DungeonRecordRewardService.cs b/DragaliaAPI/Features/Dungeon/Record/DungeonRecordRewardService.cs index 9b2958f6d..cee91ba6c 100644 --- a/DragaliaAPI/Features/Dungeon/Record/DungeonRecordRewardService.cs +++ b/DragaliaAPI/Features/Dungeon/Record/DungeonRecordRewardService.cs @@ -95,7 +95,7 @@ EnemyDropList enemyDropList in enemyList drops.Add(reward.ToDropAll()); - await rewardService.GrantReward(reward, log: false); + await rewardService.GrantReward(reward); } } } diff --git a/DragaliaAPI/Features/Dungeon/Record/DungeonRecordService.cs b/DragaliaAPI/Features/Dungeon/Record/DungeonRecordService.cs index 301a3717b..3086948de 100644 --- a/DragaliaAPI/Features/Dungeon/Record/DungeonRecordService.cs +++ b/DragaliaAPI/Features/Dungeon/Record/DungeonRecordService.cs @@ -60,7 +60,11 @@ DungeonSession session this.ProcessMissionProgression(session); await this.ProcessGrowth(ingameResultData.grow_record, session); await this.ProcessStaminaConsumption(session); - await this.ProcessPlayerLevel(ingameResultData.reward_record, session); + await this.ProcessPlayerLevel( + ingameResultData.grow_record, + ingameResultData.reward_record, + session + ); (IEnumerable dropList, int manaDrop, int coinDrop) = await dungeonRecordRewardService.ProcessEnemyDrops(playRecord, session); @@ -143,7 +147,11 @@ private async Task ProcessStaminaConsumption(DungeonSession session) await userService.RemoveStamina(type, amount); } - private async Task ProcessPlayerLevel(RewardRecord rewardRecord, DungeonSession session) + private async Task ProcessPlayerLevel( + GrowRecord growRecord, + RewardRecord rewardRecord, + DungeonSession session + ) { // Constant for quests with no stamina usage, wip? int experience = @@ -156,5 +164,6 @@ private async Task ProcessPlayerLevel(RewardRecord rewardRecord, DungeonSession PlayerLevelResult playerLevelResult = await userService.AddExperience(experience); // TODO: Exp boost rewardRecord.player_level_up_fstone = playerLevelResult.RewardedWyrmite; + growRecord.take_player_exp = experience; } } diff --git a/DragaliaAPI/Features/Reward/IRewardService.cs b/DragaliaAPI/Features/Reward/IRewardService.cs index 1383ea5f5..2c3fc8cff 100644 --- a/DragaliaAPI/Features/Reward/IRewardService.cs +++ b/DragaliaAPI/Features/Reward/IRewardService.cs @@ -10,11 +10,10 @@ public interface IRewardService /// Grant a reward of an arbitrary entity type to the player. /// /// The entity to grant. - /// Log the rewards. Disable if making a large number of calls. /// /// An enum indicating the result of the add operation. /// - Task GrantReward(Entity entity, bool log = true); + Task GrantReward(Entity entity); Task<(RewardGrantResult Result, DbTalisman? Talisman)> GrantTalisman( Talismans id, diff --git a/DragaliaAPI/Features/Reward/RewardService.cs b/DragaliaAPI/Features/Reward/RewardService.cs index 34f3cb186..a7f5a3c52 100644 --- a/DragaliaAPI/Features/Reward/RewardService.cs +++ b/DragaliaAPI/Features/Reward/RewardService.cs @@ -34,7 +34,7 @@ ITicketRepository ticketRepository private readonly List newEntities = new(); private readonly List convertedEntities = new(); - public async Task GrantReward(Entity entity, bool log = true) + public async Task GrantReward(Entity entity) { if (entity.Quantity <= 0) { @@ -42,7 +42,7 @@ public async Task GrantReward(Entity entity, bool log = true) return RewardGrantResult.Added; } - logger.LogDebug("Granting reward {@rewardEntity}", entity); + logger.LogTrace("Granting reward {@rewardEntity}", entity); switch (entity.Type) {