Skip to content

Commit

Permalink
Merge branch 'development' into feature/code-sweep-dev-extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
eugene-doobu authored Dec 13, 2024
2 parents 9ae8f08 + af4013a commit a5065a9
Show file tree
Hide file tree
Showing 42 changed files with 8,435 additions and 591 deletions.
64 changes: 64 additions & 0 deletions .Lib9c.Tests/Action/BattleArenaTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1119,6 +1119,70 @@ public void Execute_ValidateDuplicateTicketPurchaseException()
}));
}

[Fact]
public void ExecuteRuneNotFoundException()
{
var previousStates = _initialStates;
var context = new ActionContext();
Assert.True(
previousStates.GetSheet<ArenaSheet>().TryGetValue(
1,
out var row));

if (!row.TryGetRound(1, out var roundData))
{
throw new RoundNotFoundException(
$"[{nameof(BattleArena)}] ChampionshipId({row.ChampionshipId}) - round({1})");
}

if (roundData.ArenaType != ArenaType.OffSeason)
{
throw new InvalidSeasonException(
$"[{nameof(BattleArena)}] This test is only for OffSeason. ArenaType : {roundData.ArenaType}");
}

var random = new TestRandom();
previousStates = JoinArena(
context,
previousStates,
_agent1Address,
_avatar1Address,
roundData.StartBlockIndex,
1,
1,
random);
previousStates = JoinArena(
context,
previousStates,
_agent2Address,
_avatar2Address,
roundData.StartBlockIndex,
1,
1,
random);

var action = new BattleArena
{
myAvatarAddress = _avatar1Address,
enemyAvatarAddress = _avatar2Address,
championshipId = 1,
round = 1,
ticket = 1,
costumes = new List<Guid>(),
equipments = new List<Guid>(),
runeInfos = new List<RuneSlotInfo> { new (0, 10035), },
};
Assert.Throws<RuneNotFoundException>(
() => action.Execute(
new ActionContext
{
BlockIndex = roundData.StartBlockIndex + 1,
PreviousState = previousStates,
Signer = _agent1Address,
RandomSeed = 0,
}));
}

[Theory]
[InlineData(8, null)]
[InlineData(100, null)]
Expand Down
198 changes: 177 additions & 21 deletions .Lib9c.Tests/Action/SynthesizeTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
using Nekoyume.Model.EnumType;
using Nekoyume.Module;
using Nekoyume.TableData;
using Nekoyume.Helper;
using Xunit;

using Sheets = System.Collections.Generic.Dictionary<System.Type, (Libplanet.Crypto.Address, Nekoyume.TableData.ISheet)>;
Expand All @@ -32,6 +33,14 @@ public class SynthesizeTest
private readonly Currency _goldCurrency = Currency.Legacy("NCG", 2, null);
#pragma warning restore CS0618

/// <summary>
/// Initializes the game state for testing.
/// Sets up an initial agent, avatar, and world state with relevant table sheets.
/// </summary>
/// <param name="agentAddress">The address of the agent to be created.</param>
/// <param name="avatarAddress">The address of the avatar to be created.</param>
/// <param name="blockIndex">The initial block index.</param>
/// <returns>The initialized world state.</returns>
public IWorld Init(out Address agentAddress, out Address avatarAddress, out long blockIndex)
{
agentAddress = new PrivateKey().Address;
Expand All @@ -47,7 +56,9 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long
.SetAgentState(agentAddress, new AgentState(agentAddress));

foreach (var (key, value) in Sheets)
{
state = state.SetLegacyState(Addresses.TableSheet.Derive(key), value.Serialize());
}

var gameConfigState = new GameConfigState(Sheets[nameof(GameConfigSheet)]);
var avatarState = AvatarState.Create(
Expand All @@ -61,6 +72,12 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long
return state.SetLegacyState(gameConfigState.address, gameConfigState.Serialize());
}

/// <summary>
/// Tests the synthesis process for a single item.
/// Verifies that the resulting item matches the expected grade and type.
/// </summary>
/// <param name="grade">The grade of the material used in synthesis.</param>
/// <param name="itemSubType">The subtype of the item to synthesize.</param>
[Theory]
[InlineData((Grade)3, ItemSubType.FullCostume)]
[InlineData((Grade)4, ItemSubType.FullCostume)]
Expand All @@ -80,8 +97,7 @@ public IWorld Init(out Address agentAddress, out Address avatarAddress, out long
[InlineData((Grade)6, ItemSubType.Aura)]
public void ExecuteSingle(Grade grade, ItemSubType itemSubType)
{
var context = new ActionContext();
var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade));
var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(itemSubType, grade));

var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex);
(state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress);
Expand All @@ -90,7 +106,10 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType)
var action = new Synthesize()
{
AvatarAddress = avatarAddress,
MaterialIds = Synthesize.GetItemGuids(items),
MaterialIds = SynthesizeSimulator.GetItemGuids(items),
ChargeAp = false,
MaterialGradeId = (int)grade,
MaterialItemSubTypeId = (int)itemSubType,
};

var ctx = new ActionContext
Expand Down Expand Up @@ -122,7 +141,7 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType)
}

resultGrade = (Grade)costume.Grade;
expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet);
expectedGrade = SynthesizeSimulator.GetUpgradeGrade(grade, subType, TableSheets.CostumeItemSheet);
break;
case ItemSubType.Aura:
case ItemSubType.Grimoire:
Expand All @@ -132,30 +151,154 @@ public void ExecuteSingle(Grade grade, ItemSubType itemSubType)
}

resultGrade = (Grade)itemUsable.Grade;
expectedGrade = Synthesize.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet);
expectedGrade = SynthesizeSimulator.GetUpgradeGrade(grade, subType, TableSheets.EquipmentItemSheet);
break;
}

// TODO: if success, grade should be exceptedGrade, but sometimes it is not.
// Assert.Equal(expectedGrade, resultGrade);
Assert.True(expectedGrade == resultGrade || resultGrade == grade);
var inputData = new SynthesizeSimulator.InputData()
{
Grade = grade,
ItemSubType = itemSubType,
MaterialCount = itemSubTypes.Length,
SynthesizeSheet = TableSheets.SynthesizeSheet,
SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet,
CostumeItemSheet = TableSheets.CostumeItemSheet,
EquipmentItemSheet = TableSheets.EquipmentItemSheet,
EquipmentItemRecipeSheet = TableSheets.EquipmentItemRecipeSheet,
EquipmentItemSubRecipeSheetV2 = TableSheets.EquipmentItemSubRecipeSheetV2,
EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet,
SkillSheet = TableSheets.SkillSheet,
RandomObject = new TestRandom(),
};

var result = SynthesizeSimulator.Simulate(inputData)[0];
if (result.IsSuccess)
{
Assert.Equal(expectedGrade, resultGrade);
}
else
{
Assert.Equal(resultGrade, grade);
}
}

/// <summary>
/// Tests the synthesis process for multiple items.
/// Verifies that the resulting items match the expected grades and types.
/// The test case also checks whether the equipment has a recipe.
/// </summary>
/// <param name="grade">The grade of the material used in synthesis.</param>
/// <param name="itemSubType">The subtype of the items to synthesize.</param>
[Theory]
[InlineData((Grade)3, ItemSubType.FullCostume)]
[InlineData((Grade)4, ItemSubType.FullCostume)]
[InlineData((Grade)5, ItemSubType.FullCostume)]
[InlineData((Grade)3, ItemSubType.Title)]
[InlineData((Grade)4, ItemSubType.Title)]
[InlineData((Grade)5, ItemSubType.Title)]
[InlineData((Grade)3, ItemSubType.Grimoire)]
[InlineData((Grade)4, ItemSubType.Grimoire)]
[InlineData((Grade)5, ItemSubType.Grimoire)]
[InlineData((Grade)6, ItemSubType.Grimoire)]
[InlineData((Grade)1, ItemSubType.Aura)]
[InlineData((Grade)2, ItemSubType.Aura)]
[InlineData((Grade)3, ItemSubType.Aura)]
[InlineData((Grade)4, ItemSubType.Aura)]
[InlineData((Grade)5, ItemSubType.Aura)]
[InlineData((Grade)6, ItemSubType.Aura)]
public void ExecuteMultiple(Grade grade, ItemSubType itemSubType)
{
var testCount = 100;
var itemSubTypes = GetSubTypeArray(itemSubType, testCount * GetSucceededMaterialCount(itemSubType, grade));

var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex);
(state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress);
state = state.SetActionPoint(avatarAddress, 120);

var action = new Synthesize()
{
AvatarAddress = avatarAddress,
MaterialIds = SynthesizeSimulator.GetItemGuids(items),
ChargeAp = false,
MaterialGradeId = (int)grade,
MaterialItemSubTypeId = (int)itemSubType,
};

var ctx = new ActionContext
{
BlockIndex = blockIndex,
PreviousState = state,
RandomSeed = 0,
Signer = agentAddress,
};

action.Execute(ctx);
var inputData = new SynthesizeSimulator.InputData()
{
Grade = grade,
ItemSubType = itemSubType,
MaterialCount = itemSubTypes.Length,
SynthesizeSheet = TableSheets.SynthesizeSheet,
SynthesizeWeightSheet = TableSheets.SynthesizeWeightSheet,
CostumeItemSheet = TableSheets.CostumeItemSheet,
EquipmentItemSheet = TableSheets.EquipmentItemSheet,
EquipmentItemRecipeSheet = TableSheets.EquipmentItemRecipeSheet,
EquipmentItemSubRecipeSheetV2 = TableSheets.EquipmentItemSubRecipeSheetV2,
EquipmentItemOptionSheet = TableSheets.EquipmentItemOptionSheet,
SkillSheet = TableSheets.SkillSheet,
RandomObject = new TestRandom(),
};

var resultList = SynthesizeSimulator.Simulate(inputData);
foreach (var result in resultList)
{
// Check Grade
if (result.IsSuccess)
{
Assert.Equal((int)grade + 1, result.ItemBase.Grade);

var weightSheet = TableSheets.SynthesizeWeightSheet;
var weightRow = weightSheet.Values.FirstOrDefault(r => r.ItemId == result.ItemBase.Id);

if (weightRow != null)
{
Assert.True(weightRow.Weight != 0);
}
}
else
{
Assert.Equal((int)grade, result.ItemBase.Grade);
}

if (result.IsEquipment)
{
Assert.True(result.RecipeId != 0);
Assert.True(result.SubRecipeId != 0);
}
}
}

/// <summary>
/// Tests the synthesis action when there are not enough action points.
/// Verifies that the action throws a NotEnoughActionPointException.
/// </summary>
[Fact]
public void ExecuteNotEnoughActionPoint()
{
var grade = Grade.Rare;
var itemSubType = ItemSubType.FullCostume;
var context = new ActionContext();
var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(grade));
var itemSubTypes = GetSubTypeArray(itemSubType, GetSucceededMaterialCount(itemSubType, grade));

var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex);
(state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress);

var action = new Synthesize()
{
AvatarAddress = avatarAddress,
MaterialIds = Synthesize.GetItemGuids(items),
MaterialIds = SynthesizeSimulator.GetItemGuids(items),
ChargeAp = false,
MaterialGradeId = (int)grade,
MaterialItemSubTypeId = (int)itemSubType,
};

var ctx = new ActionContext
Expand All @@ -169,6 +312,11 @@ public void ExecuteNotEnoughActionPoint()
Assert.Throws<NotEnoughActionPointException>(() => action.Execute(ctx));
}

/// <summary>
/// Tests the synthesis of multiple items of the same type.
/// Verifies that the resulting items are of the correct type and grade.
/// </summary>
/// <param name="testCount">The number of items to synthesize.</param>
[Theory]
[InlineData(2)]
[InlineData(3)]
Expand All @@ -177,7 +325,7 @@ public void ExecuteMultipleSameType(int testCount)
{
var grade = Grade.Rare;
var itemSubType = ItemSubType.FullCostume;
var materialCount = GetSucceededMaterialCount(grade) * testCount;
var materialCount = GetSucceededMaterialCount(itemSubType, grade) * testCount;
var itemSubTypes = GetSubTypeArray(itemSubType, materialCount);

var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex);
Expand All @@ -187,7 +335,10 @@ public void ExecuteMultipleSameType(int testCount)
var action = new Synthesize()
{
AvatarAddress = avatarAddress,
MaterialIds = Synthesize.GetItemGuids(items),
MaterialIds = SynthesizeSimulator.GetItemGuids(items),
ChargeAp = false,
MaterialGradeId = (int)grade,
MaterialItemSubTypeId = (int)itemSubType,
};

var ctx = new ActionContext
Expand All @@ -206,30 +357,35 @@ public void ExecuteMultipleSameType(int testCount)
foreach (var item in inventory.Items.Select(i => i.item))
{
Assert.Equal(itemSubType, item.ItemSubType);
var expectedGrade = Synthesize.GetTargetGrade((int)grade);
var expectedGrade = SynthesizeSimulator.GetTargetGrade((int)grade);
Assert.True(item.Grade == expectedGrade || item.Grade == (int)grade);
}
}

// TODO: Add Simulator for test and client
// TODO: Exception Tests
// TODO: ExecuteMultiple
/// <summary>
/// Tests the synthesis action with invalid material combinations.
/// Verifies that the action throws an InvalidMaterialException.
/// </summary>
/// <param name="grade">The grade of the material used in synthesis.</param>
/// <param name="itemSubTypes">An array of invalid item subtypes to use in synthesis.</param>
[Theory]
[InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.FullCostume, ItemSubType.FullCostume })]
[InlineData((Grade)3, new[] { ItemSubType.Title, ItemSubType.Grimoire, ItemSubType.Title })]
[InlineData((Grade)3, new[] { ItemSubType.Grimoire, ItemSubType.Title, ItemSubType.Grimoire })]
[InlineData((Grade)3, new[] { ItemSubType.Aura, ItemSubType.Aura, ItemSubType.Grimoire })]
public void ExecuteInvalidMaterial(Grade grade, ItemSubType[] itemSubTypes)
{
var context = new ActionContext();
var state = Init(out var agentAddress, out var avatarAddress, out var blockIndex);
(state, var items) = UpdateItemsFromSubType(grade, itemSubTypes, state, avatarAddress);
state = state.SetActionPoint(avatarAddress, 120);

var action = new Synthesize()
{
AvatarAddress = avatarAddress,
MaterialIds = Synthesize.GetItemGuids(items),
MaterialIds = SynthesizeSimulator.GetItemGuids(items),
ChargeAp = false,
MaterialGradeId = (int)grade,
MaterialItemSubTypeId = (int)itemSubTypes[0],
};

var ctx = new ActionContext
Expand Down Expand Up @@ -340,10 +496,10 @@ private static ItemSubType[] GetSubTypeArray(ItemSubType subtype, int count)
return subTypes;
}

private static int GetSucceededMaterialCount(Grade grade)
private static int GetSucceededMaterialCount(ItemSubType itemSubType, Grade grade)
{
var synthesizeSheet = TableSheets.SynthesizeSheet;
var row = synthesizeSheet.Values.First(r => (Grade)r.GradeId == grade);
return row.RequiredCount;
return row.RequiredCountDict[itemSubType].RequiredCount;
}
}
Loading

0 comments on commit a5065a9

Please sign in to comment.