Merge pull request #539 from CJ-SPT/test-mod

Intoduce test mod and restructure test suites into their own solution folder
This commit is contained in:
Cj
2025-08-09 15:26:06 -04:00
committed by GitHub
parent dd67098734
commit c16c988fda
25 changed files with 92 additions and 22 deletions
+68
View File
@@ -0,0 +1,68 @@
using Microsoft.Extensions.DependencyInjection;
using NUnit.Framework;
using SPTarkov.DI;
using SPTarkov.Server.Core.DI;
using SPTarkov.Server.Core.Models.Spt.Mod;
using SPTarkov.Server.Core.Utils;
using SPTarkov.Server.Core.Utils.Logger.Handlers;
using UnitTests.Mock;
namespace UnitTests;
[TestFixture]
public class DI
{
private static IServiceProvider _serviceProvider;
private static DI? _instance;
private DI()
{
ConfigureServices();
}
public static DI GetInstance()
{
return _instance ??= new DI();
}
private void ConfigureServices()
{
if (_serviceProvider != null)
{
return;
}
var services = new ServiceCollection();
var diHandler = new DependencyInjectionHandler(services);
diHandler.AddInjectableTypesFromTypeAssembly(typeof(App));
diHandler.AddInjectableTypesFromTypeList(
[
typeof(MockLogger<>), // TODO: this needs to be enabled but the randomizer needs to NOT be random, typeof(MockRandomUtil)
]
);
diHandler.InjectAll();
services.AddSingleton<IReadOnlyList<SptMod>>(_ => []);
_serviceProvider = services.BuildServiceProvider();
foreach (var onLoad in _serviceProvider.GetServices<IOnLoad>())
{
if (onLoad is FileLogHandler)
{
continue;
}
onLoad.OnLoad().Wait();
}
}
public T GetService<T>()
where T : notnull
{
return _serviceProvider.GetRequiredService<T>();
}
}
+82
View File
@@ -0,0 +1,82 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Logging;
using SPTarkov.Server.Core.Models.Spt.Logging;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Utils.Logger;
namespace UnitTests.Mock;
[Injectable(TypeOverride = typeof(SptLogger<>))]
public class MockLogger<T> : ISptLogger<T>
{
public void LogWithColor(string data, LogTextColor? textColor = null, LogBackgroundColor? backgroundColor = null, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Success(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Error(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Warning(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Info(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Debug(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Critical(string data, Exception? ex = null)
{
Console.WriteLine(data);
}
public void Log(
LogLevel level,
string data,
LogTextColor? textColor = null,
LogBackgroundColor? backgroundColor = null,
Exception? ex = null
)
{
throw new NotImplementedException();
}
public void WriteToLogFile(string body, LogLevel level = LogLevel.Info)
{
throw new NotImplementedException();
}
public bool IsLogEnabled(LogLevel level)
{
return true;
}
public void DumpAndStop()
{
throw new NotImplementedException();
}
public void LogWithColor(string data, Exception? ex = null, LogTextColor? textColor = null, LogBackgroundColor? backgroundColor = null)
{
Console.WriteLine(data);
}
public void WriteToLogFile(object body)
{
Console.WriteLine(body);
}
}
+122
View File
@@ -0,0 +1,122 @@
using SPTarkov.DI.Annotations;
using SPTarkov.Server.Core.Models.Utils;
using SPTarkov.Server.Core.Utils;
using SPTarkov.Server.Core.Utils.Cloners;
namespace UnitTests.Mock;
[Injectable(TypeOverride = typeof(RandomUtil))]
public class MockRandomUtil(ISptLogger<RandomUtil> _logger, ICloner _cloner) : RandomUtil(_logger, _cloner)
{
public override int GetInt(int min, int max = Int32.MaxValue, bool exclusive = false)
{
return min;
}
public override double GetDouble(double min, double max)
{
return min;
}
public override bool GetBool()
{
return true;
}
public override void NextBytes(Span<byte> bytes)
{
// TODO: No idea what this does
base.NextBytes(bytes);
}
public override double GetPercentOfValue(double percent, double number, int toFixed = 2)
{
// TODO: No idea what this does
return base.GetPercentOfValue(percent, number, toFixed);
}
public override double ReduceValueByPercent(double number, double percentage)
{
// TODO: No idea what this does
return base.ReduceValueByPercent(number, percentage);
}
public override bool GetChance100(double? chancePercent)
{
return true;
}
public override T GetRandomElement<T>(IEnumerable<T> collection)
{
if (!collection.Any())
{
throw new InvalidOperationException("Sequence contains no elements.");
}
return collection.First();
}
public override TKey GetKey<TKey, TVal>(Dictionary<TKey, TVal> dictionary)
{
return GetRandomElement(dictionary.Keys);
}
public override TVal GetVal<TKey, TVal>(Dictionary<TKey, TVal> dictionary)
{
return GetRandomElement(dictionary.Values);
}
public override double GetNormallyDistributedRandomNumber(double mean, double sigma, int attempt = 0)
{
// TODO: No idea what to do with this
return base.GetNormallyDistributedRandomNumber(mean, sigma, attempt);
}
public override int RandInt(int low, int? high = null)
{
return low;
}
public override double RandNum(double val1, double val2 = 0, int precision = 6)
{
return val1;
}
public override List<T> DrawRandomFromList<T>(List<T> originalList, int count = 1, bool replacement = true)
{
return originalList.Slice(0, count);
}
public override List<TKey> DrawRandomFromDict<TKey, TVal>(Dictionary<TKey, TVal> dict, int count = 1, bool replacement = true)
{
// TODO: derandomize
return base.DrawRandomFromDict(dict, count, replacement);
}
public override double GetBiasedRandomNumber(double min, double max, double shift, double n)
{
return min;
}
public override List<T> Shuffle<T>(List<T> originalList)
{
return originalList;
}
public override int GetNumberPrecision(double num)
{
// TODO: derandomize
return base.GetNumberPrecision(num);
}
public override T? GetArrayValue<T>(IEnumerable<T> list)
where T : default
{
return GetRandomElement(list);
}
public override bool RollChance(double chance, double scale = 1)
{
return true;
}
}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,306 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
namespace UnitTests.Tests.Extensions;
[TestFixture]
public partial class ContainerExtensionsTests
{
[SetUp]
public void Initialize() { }
[Test]
public void CanItemBePlacedInContainerAtPosition_1x1_Item_Fits_1x2_Container_At_0x0()
{
var container = new int[1, 2];
var itemStartXPos = 0;
var itemStartYPos = 0;
var itemWidth = 1;
var itemHeight = 1;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsTrue(result);
}
[Test]
public void CanItemBePlacedInContainerAtPosition_1x1_Item_Fails_1x2_Container_At_0x0_With_Item_At_0x0()
{
var container = new int[1, 2];
container[0, 0] = 1;
var itemStartXPos = 0;
var itemStartYPos = 0;
var itemWidth = 1;
var itemHeight = 1;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsFalse(result);
}
[Test]
public void CanItemBePlacedInContainerAtPosition_1x2_Item_Fits_1x2_Container_At_0x0()
{
var container = new int[2, 1];
var itemStartXPos = 0;
var itemStartYPos = 0;
var itemWidth = 1;
var itemHeight = 2;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsTrue(result);
}
[Test]
public void CanItemBePlacedInContainerAtPosition_1x2_Item_Fails_1x2_Container_At_0x0_With_Item_At_0x0()
{
var container = new int[1, 2];
container[0, 0] = 1;
var itemStartXPos = 0;
var itemStartYPos = 0;
var itemWidth = 1;
var itemHeight = 2;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsFalse(result);
}
[Test]
public void CanItemBePlacedInContainerAtPosition_2x2_Item_Fits_2x2_Container_At_0x0()
{
var container = new int[2, 2];
var itemStartXPos = 0;
var itemStartYPos = 0;
var itemWidth = 2;
var itemHeight = 2;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsTrue(result);
}
[Test]
public void CanItemBePlacedInContainerAtPosition_1x2_Item_Fits_2x2_Container_At_0x1()
{
var container = new int[2, 2];
var itemStartXPos = 0;
var itemStartYPos = 1;
var itemWidth = 1;
var itemHeight = 2;
var result = container.CanItemBePlacedInContainerAtPosition(itemStartXPos, itemStartYPos, itemWidth, itemHeight);
Assert.IsTrue(result);
}
}
public partial class ContainerExtensionsTests
{
[Test]
public void FindSlotForItem_1x1_item_fits_1x1_container_no_rotation()
{
var container = new int[1, 1];
var itemWidth = 1;
var itemHeight = 1;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsTrue(result.Success);
Assert.IsFalse(result.Rotation);
Assert.AreEqual(result.X, 0);
Assert.AreEqual(result.Y, 0);
}
[Test]
public void FindSlotForItem_1x2_item_fits_3x3_container_rotated_with_items()
{
// |1|1|1|
// |1|0|0|
// |1|1|1|
var container = new int[3, 3];
container[0, 0] = 1;
container[0, 1] = 1;
container[0, 2] = 1;
container[1, 0] = 1;
container[2, 0] = 1;
container[2, 1] = 1;
container[2, 2] = 1;
var itemWidth = 1;
var itemHeight = 2;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsTrue(result.Success);
Assert.IsTrue(result.Rotation);
Assert.AreEqual(result.X, 1);
Assert.AreEqual(result.Y, 1);
}
[Test]
public void FindSlotForItem_1x1_item_fails_1x1_container_no_space()
{
var container = new int[1, 1];
container[0, 0] = 1;
var itemWidth = 1;
var itemHeight = 1;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsFalse(result.Success);
}
[Test]
public void FindSlotForItem_1x2_item_fits_1x2_container_no_rotation()
{
var container = new int[2, 1];
var itemWidth = 1;
var itemHeight = 2;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsTrue(result.Success);
Assert.IsFalse(result.Rotation);
Assert.AreEqual(result.X, 0);
Assert.AreEqual(result.Y, 0);
}
[Test]
public void FindSlotForItem_1x2_item_fails_1x2_container_no_space()
{
var container = new int[1, 1];
container[0, 0] = 1;
container[0, 0] = 1;
var itemWidth = 1;
var itemHeight = 2;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsFalse(result.Success);
}
[Test]
public void FindSlotForItem_2x2_item_fits_2x2_container_no_rotation()
{
var container = new int[2, 2];
var itemWidth = 2;
var itemHeight = 2;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsTrue(result.Success);
Assert.IsFalse(result.Rotation);
Assert.AreEqual(result.X, 0);
Assert.AreEqual(result.Y, 0);
}
[Test]
public void FindSlotForItem_1x2_item_fits_2x2_container_no_rotation_with_item_at_0x0()
{
var container = new int[2, 2];
container[0, 0] = 1;
var itemWidth = 1;
var itemHeight = 2;
var result = container.FindSlotForItem(itemWidth, itemHeight);
Assert.IsTrue(result.Success);
Assert.IsFalse(result.Rotation);
Assert.AreEqual(result.X, 1);
Assert.AreEqual(result.Y, 0);
}
}
public partial class ContainerExtensionsTests
{
[Test]
public void FillContainerMapWithItem_1x1_at_0x0_in_1x1_no_rotation()
{
var container = new int[1, 1];
var itemWidth = 1;
var itemHeight = 1;
var destinationPosX = 0;
var destinationPosY = 0;
var isRotated = false;
container.FillContainerMapWithItem(destinationPosX, destinationPosY, itemWidth, itemHeight, isRotated);
Assert.AreEqual(container[0, 0], 1);
}
[Test]
public void FillContainerMapWithItem_1x2_at_0x0_in_1x2_no_rotation()
{
var container = new int[2, 1];
var itemWidth = 1;
var itemHeight = 2;
var destinationPosX = 0;
var destinationPosY = 0;
var isRotated = false;
container.FillContainerMapWithItem(destinationPosX, destinationPosY, itemWidth, itemHeight, isRotated);
Assert.AreEqual(container[0, 0], 1);
Assert.AreEqual(container[1, 0], 1);
}
[Test]
public void FillContainerMapWithItem_2x2_at_0x0_in_2x2_no_rotation()
{
var container = new int[2, 2];
var itemWidth = 2;
var itemHeight = 2;
var destinationPosX = 0;
var destinationPosY = 0;
var isRotated = false;
container.FillContainerMapWithItem(destinationPosX, destinationPosY, itemWidth, itemHeight, isRotated);
Assert.AreEqual(container[0, 0], 1);
Assert.AreEqual(container[1, 1], 1);
}
[Test]
public void FillContainerMapWithItem_1x2_at_0x0_in_2x2_with_rotation()
{
var container = new int[2, 2];
var itemWidth = 1;
var itemHeight = 2;
var destinationPosX = 0;
var destinationPosY = 0;
var isRotated = true;
container.FillContainerMapWithItem(destinationPosX, destinationPosY, itemWidth, itemHeight, isRotated);
Assert.AreEqual(container[0, 0], 1);
Assert.AreEqual(container[0, 1], 1);
}
[Test]
public void FillContainerMapWithItem_1x2_at_1x0_in_2x2_with_rotation_with_existing_item()
{
var container = new int[2, 2];
container[0, 0] = 1;
var itemWidth = 1;
var itemHeight = 2;
var destinationPosX = 0;
var destinationPosY = 1;
var isRotated = true;
container.FillContainerMapWithItem(destinationPosX, destinationPosY, itemWidth, itemHeight, isRotated);
Assert.AreEqual(container[1, 0], 1);
Assert.AreEqual(container[1, 1], 1);
}
}
@@ -0,0 +1,327 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
namespace UnitTests.Tests.Extensions;
[TestFixture]
public class ItemTests
{
[SetUp]
public void Initialize() { }
[Test]
public void GetItemWithChildren_one_child_mods_only()
{
var testData = new List<Item>();
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = rootItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
var result = testData.GetItemWithChildren(rootItem.Id, true);
Assert.AreEqual(result[1].Id, childItem.Id);
}
[Test]
public void GetItemWithChildren_mods_only_one_inventory_item()
{
var testData = new List<Item>();
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = rootItem.Id,
Location = 1,
};
var childItem2 = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_26X75_GREEN,
ParentId = rootItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
testData.Add(childItem2);
var result = testData.GetItemWithChildren(rootItem.Id, true);
Assert.AreEqual(result[1].Id, childItem2.Id);
Assert.AreEqual(result.Count, 2);
}
[Test]
public void GetItemWithChildren_mods_and_inventory_item()
{
var testData = new List<Item>();
var rootItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND,
ParentId = new MongoId(),
};
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = rootItem.Id,
Location = 1,
};
var childItem2 = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_26X75_GREEN,
ParentId = rootItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
testData.Add(childItem2);
var result = testData.GetItemWithChildren(rootItem.Id, false);
Assert.Contains(childItem, result);
Assert.Contains(childItem2, result);
Assert.AreEqual(result.Count, 3);
}
[Test]
public void GetItemWithChildren_mod_with_child()
{
var testData = new List<Item>();
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = rootItem.Id,
};
var childOfChild = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_26X75_GREEN,
ParentId = childItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
testData.Add(childOfChild);
var result = testData.GetItemWithChildren(rootItem.Id, true);
Assert.AreEqual(result[1].Id, childItem.Id);
Assert.AreEqual(result.Count, 3);
}
[Test]
public void GetItemWithChildren_no_matching_children()
{
var testData = new List<Item>();
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = new MongoId(),
};
var childOfChild = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_26X75_GREEN,
ParentId = childItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
testData.Add(childOfChild);
var result = testData.GetItemWithChildren(rootItem.Id, true);
Assert.AreEqual(result[0].Id, rootItem.Id);
Assert.AreEqual(result.Count, 1);
}
[Test]
public void RemoveFiRStatusFromItemsInContainer_twoItemsInBackpack()
{
var profile = new PmcData() { Inventory = new BotBaseInventory() { Items = [] } };
profile.Inventory.Equipment = new MongoId();
// Add backpack
var backpackId = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = backpackId,
Template = ItemTpl.BACKPACK_HAZARD_4_PILLBOX_BACKPACK_BLACK,
ParentId = profile.Inventory.Equipment,
SlotId = "Backpack",
}
);
// Add ifak to first slot in backpack
var item1Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item1Id,
Template = ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT,
ParentId = backpackId,
SlotId = "main",
Upd = new Upd { SpawnedInSession = true },
Location = new ItemLocation
{
X = 0,
Y = 0,
R = ItemRotation.Horizontal,
},
}
);
// Add wrench to first slot of ifak
var item2Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item2Id,
Template = ItemTpl.BARTER_WRENCH,
ParentId = backpackId,
SlotId = "main",
Upd = new Upd { SpawnedInSession = true },
Location = new ItemLocation
{
X = 1,
Y = 0,
R = ItemRotation.Horizontal,
},
}
);
// Add armband to armband slot as root
var item3Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item3Id,
Template = ItemTpl.ARMBAND_RED,
ParentId = profile.Inventory.Equipment,
SlotId = "Armband",
Upd = new Upd { SpawnedInSession = true },
}
);
profile.RemoveFiRStatusFromItemsInContainer("Backpack");
Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item1Id).Upd.SpawnedInSession);
Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item2Id).Upd.SpawnedInSession);
Assert.AreEqual(true, profile.Inventory.Items.FirstOrDefault(item => item.Id == item3Id).Upd.SpawnedInSession);
}
[Test]
public void RemoveFiRStatusFromItemsInContainer_oneItemWithChildInBackpack()
{
var profile = new PmcData { Inventory = new BotBaseInventory { Items = [] } };
profile.Inventory.Equipment = new MongoId();
// Add backpack
var backpackId = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = backpackId,
Template = ItemTpl.BACKPACK_HAZARD_4_PILLBOX_BACKPACK_BLACK,
ParentId = profile.Inventory.Equipment,
SlotId = "Backpack",
}
);
// Add ifak to first slot in backpack
var item1Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item1Id,
Template = ItemTpl.MEDKIT_AFAK_TACTICAL_INDIVIDUAL_FIRST_AID_KIT,
ParentId = backpackId,
SlotId = "main",
Upd = new Upd { SpawnedInSession = true },
Location = new ItemLocation
{
X = 0,
Y = 0,
R = ItemRotation.Horizontal,
},
}
);
// Add wrench to first slot of ifak as child
var item2Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item2Id,
Template = ItemTpl.BARTER_WRENCH,
ParentId = item1Id,
SlotId = "main",
Upd = new Upd { SpawnedInSession = true },
Location = new ItemLocation
{
X = 1,
Y = 0,
R = ItemRotation.Horizontal,
},
}
);
// Add armband to armband slot as root
var item3Id = new MongoId();
profile.Inventory.Items.Add(
new Item
{
Id = item3Id,
Template = ItemTpl.ARMBAND_RED,
ParentId = profile.Inventory.Equipment,
SlotId = "Armband",
Upd = new Upd { SpawnedInSession = true },
}
);
profile.RemoveFiRStatusFromItemsInContainer("Backpack");
Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item1Id).Upd.SpawnedInSession);
Assert.AreEqual(false, profile.Inventory.Items.FirstOrDefault(item => item.Id == item2Id).Upd.SpawnedInSession);
Assert.AreEqual(true, profile.Inventory.Items.FirstOrDefault(item => item.Id == item3Id).Upd.SpawnedInSession);
}
[Test]
public void GetItemWithChildren_rootIdNotFound()
{
var testData = new List<Item>();
var rootItem = new Item { Id = new MongoId(), Template = ItemTpl.AMMOBOX_127X33_COPPER_20RND };
var childItem = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_127X33_COPPER,
ParentId = rootItem.Id,
};
var childOfChild = new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_26X75_GREEN,
ParentId = childItem.Id,
};
testData.Add(rootItem);
testData.Add(childItem);
testData.Add(childOfChild);
var result = testData.GetItemWithChildren(new MongoId(), true);
Assert.AreEqual(result.Count, 0);
}
}
@@ -0,0 +1,72 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Generators;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Profile;
using SPTarkov.Server.Core.Servers;
using SPTarkov.Server.Core.Services;
namespace UnitTests.Tests.Generators;
[TestFixture]
public class BotWeaponGeneratorTests
{
private BotWeaponGenerator _botWeaponGenerator;
private DatabaseService _databaseService;
private InventoryHelper _inventoryHelper;
private SaveServer _saveServer;
[OneTimeSetUp]
public void Initialize()
{
_botWeaponGenerator = DI.GetInstance().GetService<BotWeaponGenerator>();
_databaseService = DI.GetInstance().GetService<DatabaseService>();
_inventoryHelper = DI.GetInstance().GetService<InventoryHelper>();
_saveServer = DI.GetInstance().GetService<SaveServer>();
}
[Test]
public void GenerateWeaponByTpl_generate_m4_pmc()
{
var usecTemplate = _databaseService.GetBots().Types["usec"];
var botTemplateInventory = usecTemplate.BotInventory;
// Create profile stub to allow `GenerateWeaponByTpl` to work
var sessionId = new MongoId();
_saveServer.CreateProfile(new Info() { ProfileId = sessionId });
var weaponTpl = ItemTpl.ASSAULTRIFLE_COLT_M4A1_556X45_ASSAULT_RIFLE;
const string slotName = "FirstPrimaryWeapon";
var weaponModChances = usecTemplate.BotChances.WeaponModsChances;
foreach (var (key, _) in weaponModChances)
{
// Set all mods to 100%
weaponModChances[key] = 100d;
}
var weaponParentId = new MongoId();
for (var i = 0; i < 100; i++)
{
var result = _botWeaponGenerator.GenerateWeaponByTpl(
sessionId,
weaponTpl,
slotName,
botTemplateInventory,
weaponParentId,
weaponModChances,
"pmcUSEC",
true,
69
);
var itemSize = _inventoryHelper.GetItemSize(weaponTpl, result.Weapon[0].Id, result.Weapon);
Assert.AreEqual(weaponTpl, result.WeaponTemplate.Id);
// Ensure it's bigger than just weapon lower
Assert.AreNotEqual(2, itemSize.Item1);
Assert.AreNotEqual(1, itemSize.Item2);
}
}
}
@@ -0,0 +1,500 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Generators;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
using SPTarkov.Server.Core.Models.Enums;
namespace UnitTests.Tests.Helpers;
[TestFixture]
public class BotGeneratorHelperTests
{
private BotGeneratorHelper _botGeneratorHelper;
private BotLootGenerator _botLootGenerator;
[OneTimeSetUp]
public void Initialize()
{
_botGeneratorHelper = DI.GetInstance().GetService<BotGeneratorHelper>();
_botLootGenerator = DI.GetInstance().GetService<BotLootGenerator>();
}
#region AddItemWithChildrenToEquipmentSlot
[Test]
public void AddItemWithChildrenToEquipmentSlot_fit_vertical()
{
var stashId = new MongoId();
var equipmentId = new MongoId();
var botInventory = new BotBaseInventory
{
Items = [],
Stash = stashId,
Equipment = equipmentId,
};
// Create backpack on player
var backpack = new Item
{
Id = new MongoId(),
// Has a 3grids, first is a 3hx5v grid
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
ParentId = equipmentId,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
var rootWeaponId = new MongoId();
var weaponWithChildren = CreateMp18(rootWeaponId);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
weaponWithChildren,
botInventory
);
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
var weaponRoot = weaponWithChildren.FirstOrDefault(item => item.Id == rootWeaponId);
Assert.AreEqual((weaponRoot.Location as ItemLocation).X, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).Y, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).R, ItemRotation.Vertical);
}
private static List<Item> CreateMp18(MongoId rootWeaponId)
{
var weaponWithChildren = new List<Item>();
var weaponRoot = new Item { Id = rootWeaponId, Template = ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE };
weaponWithChildren.Add(weaponRoot);
var weaponStock = new Item
{
Id = new MongoId(),
Template = ItemTpl.STOCK_MP18_WOODEN,
ParentId = weaponRoot.Id,
SlotId = "mod_stock",
};
weaponWithChildren.Add(weaponStock);
var weaponBarrel = new Item
{
Id = new MongoId(),
Template = ItemTpl.BARREL_MP18_762X54R_600MM,
ParentId = weaponRoot.Id,
SlotId = "mod_barrel",
};
weaponWithChildren.Add(weaponBarrel);
return weaponWithChildren;
}
[Test]
public void AddItemWithChildrenToEquipmentSlot_fit_horizontal()
{
var stashId = new MongoId();
var equipmentId = new MongoId();
var botInventory = new BotBaseInventory
{
Items = [],
Stash = stashId,
Equipment = equipmentId,
};
// Create backpack on player
var backpack = new Item
{
Id = new MongoId(),
Template = ItemTpl.BACKPACK_ANA_TACTICAL_BETA_2_BATTLE_BACKPACK_OLIVE_DRAB,
ParentId = equipmentId,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
var rootWeaponId = new MongoId();
var weaponWithChildren = CreateMp18(rootWeaponId);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
weaponWithChildren,
botInventory
);
var tplsToAdd = new Dictionary<MongoId, double>
{
{ ItemTpl.BARTER_MALBORO_CIGARETTES, 1 },
{ ItemTpl.FOREGRIP_SAKO_TRG_M10_GRIP_PAD, 1 },
{ ItemTpl.BARTER_GOLD_SKULL_RING, 1 },
{ ItemTpl.BARTER_PACK_OF_NAILS, 1 },
};
_botLootGenerator.AddLootFromPool(tplsToAdd, [EquipmentSlots.Backpack], 4, botInventory, "assault", null);
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
var weaponRoot = weaponWithChildren.FirstOrDefault(item => item.Id == rootWeaponId);
Assert.AreEqual((weaponRoot.Location as ItemLocation).X, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).Y, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).R, ItemRotation.Horizontal);
foreach (var item in botInventory.Items.Where(i => tplsToAdd.ContainsKey(i.Template)))
{
var location = item.Location as ItemLocation;
Assert.True(location.X >= 0 && location.X <= 3, "Error! An item was misplaced on the X axis inside the item grid!");
Assert.AreEqual(1, location.Y, "Error! An item was misplaced on the Y axis inside the item grid!");
}
}
/// <summary>
/// Backpack with one bullet in top row, blocking gun from being placed at 0,0
/// </summary>
[Test]
public void AddItemWithChildrenToEquipmentSlot_fit_vertical_with_items_in_backpack()
{
var botInventory = new BotBaseInventory { Items = [] };
var backpack = new Item
{
Id = new MongoId(),
// Has a 3hx5v grid first
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
botInventory.Items.Add(
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 0,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
}
);
var rootWeaponId = new MongoId();
var weaponWithChildren = CreateMp18(rootWeaponId);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
weaponWithChildren,
botInventory
);
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
var weaponRoot = weaponWithChildren.FirstOrDefault(item => item.Id == rootWeaponId);
Assert.AreEqual((weaponRoot.Location as ItemLocation).X, 1);
Assert.AreEqual((weaponRoot.Location as ItemLocation).Y, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).R, ItemRotation.Vertical);
}
/// <summary>
/// No space for gun
/// </summary>
[Test]
public void AddItemWithChildrenToEquipmentSlot_no_space_in_first_grid_choose_second_grid()
{
var botInventory = new BotBaseInventory { Items = [] };
var backpack = new Item
{
Id = new MongoId(),
// Has a 3hx5v grid first
Template = ItemTpl.BACKPACK_EBERLESTOCK_G2_GUNSLINGER_II_BACKPACK_DRY_EARTH,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
botInventory.Items.AddRange(
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 0,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
},
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 1,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
},
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 2,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
}
);
var rootWeaponId = new MongoId();
var weaponWithChildren = CreateMp18(rootWeaponId);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
weaponWithChildren,
botInventory
);
Assert.AreEqual(ItemAddedResult.SUCCESS, result);
var weaponRoot = weaponWithChildren.FirstOrDefault(item => item.Id == rootWeaponId);
Assert.AreEqual("1", weaponRoot.SlotId);
Assert.AreEqual((weaponRoot.Location as ItemLocation).X, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).Y, 0);
Assert.AreEqual((weaponRoot.Location as ItemLocation).R, ItemRotation.Vertical);
}
/// <summary>
/// No space for gun
/// </summary>
[Test]
public void AddItemWithChildrenToEquipmentSlot_no_space()
{
var botInventory = new BotBaseInventory { Items = [] };
var backpack = new Item
{
Id = new MongoId(),
// Has a 4hx5v grid first
Template = ItemTpl.BACKPACK_WARTECH_BERKUT_BB102_BACKPACK_ATACS_FG,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
botInventory.Items.AddRange(
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 0,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
},
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 1,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
},
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 2,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
},
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = 3,
Y = 0,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
}
);
var rootWeaponId = new MongoId();
var weaponWithChildren = CreateMp18(rootWeaponId);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
ItemTpl.SHOTGUN_MP18_762X54R_SINGLESHOT_RIFLE,
weaponWithChildren,
botInventory
);
Assert.AreEqual(ItemAddedResult.NO_SPACE, result);
}
/// <summary>
/// Fill all slots except for a 2x6 rectangle, with the top right corner filled, result should be no space
/// </summary>
[Test]
public void AddItemWithChildrenToEquipmentSlot_custom_gun_no_space()
{
var botInventory = new BotBaseInventory { Items = [] };
var backpack = new Item
{
Id = new MongoId(),
// Has a 4hx5v grid first
Template = ItemTpl.BACKPACK_GRUPPA_99_T30_BACKPACK_BLACK,
SlotId = "Backpack",
};
botInventory.Items.Add(backpack);
var takenSlots = new List<XY>
{
new() { X = 1, Y = 0 },
new() { X = 2, Y = 0 },
new() { X = 3, Y = 0 },
new() { X = 4, Y = 0 },
new() { X = 2, Y = 1 },
new() { X = 3, Y = 1 },
new() { X = 4, Y = 1 },
new() { X = 2, Y = 2 },
new() { X = 3, Y = 2 },
new() { X = 4, Y = 2 },
new() { X = 2, Y = 3 },
new() { X = 3, Y = 3 },
new() { X = 4, Y = 3 },
new() { X = 2, Y = 4 },
new() { X = 3, Y = 4 },
new() { X = 4, Y = 4 },
new() { X = 2, Y = 5 },
new() { X = 3, Y = 5 },
new() { X = 4, Y = 5 },
};
foreach (var takenSlot in takenSlots)
{
botInventory.Items.Add(
new Item
{
Id = new MongoId(),
Template = ItemTpl.AMMO_762X25TT_AKBS,
ParentId = backpack.Id,
SlotId = "main",
Location = new ItemLocation
{
X = (int)takenSlot.X.Value,
Y = (int)takenSlot.Y.Value,
R = ItemRotation.Horizontal,
},
Upd = new Upd { StackObjectsCount = 1 },
}
);
}
var rootWeaponId = new MongoId();
var weaponWithChildren = new List<Item>();
var root = new Item { Id = rootWeaponId, Template = ItemTpl.ASSAULTRIFLE_MOLOT_ARMS_VPO136_VEPRKM_762X39_CARBINE };
weaponWithChildren.Add(root);
var stock = new Item
{
Id = new MongoId(),
Template = ItemTpl.STOCK_VPO136_VEPRKM_WOODEN,
ParentId = root.Id,
SlotId = "mod_stock",
};
weaponWithChildren.Add(stock);
var magazine = new Item
{
Id = new MongoId(),
Template = ItemTpl.MAGAZINE_366TKM_AK_AL_10RND,
ParentId = root.Id,
SlotId = "mod_magazine",
};
weaponWithChildren.Add(magazine);
var muzzle = new Item
{
Id = new MongoId(),
Template = ItemTpl.SILENCER_AKM_HEXAGON_762X39_SOUND_SUPPRESSOR,
ParentId = root.Id,
SlotId = "mod_muzzle",
};
weaponWithChildren.Add(muzzle);
var result = _botGeneratorHelper.AddItemWithChildrenToEquipmentSlot(
[EquipmentSlots.Backpack],
rootWeaponId,
root.Template,
weaponWithChildren,
botInventory
);
Assert.AreEqual(ItemAddedResult.NO_SPACE, result);
}
#endregion
#region GetBotEquipmentRole
[Test]
public void GetBotEquipmentRole_assault()
{
var result = _botGeneratorHelper.GetBotEquipmentRole("assault");
Assert.AreEqual("assault", result);
}
[Test]
public void GetBotEquipmentRole_pmcBEAR()
{
var result = _botGeneratorHelper.GetBotEquipmentRole("pmcBEAR");
Assert.AreEqual("pmc", result);
}
[Test]
public void GetBotEquipmentRole_pmcBEAR_lowercase()
{
var result = _botGeneratorHelper.GetBotEquipmentRole("pmcbear");
Assert.AreEqual("pmc", result);
}
#endregion
}
@@ -0,0 +1,125 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
namespace UnitTests.Tests.Helpers;
[TestFixture]
public class InRaidHelperTests
{
private InRaidHelper _helper;
[OneTimeSetUp]
public void Initialize()
{
_helper = DI.GetInstance().GetService<InRaidHelper>();
}
[Test]
public void DeleteInventory_ShouldNotThrowCollectionModifiedException()
{
// Arrange
var pmcData = CreateTestPmcData();
var sessionId = new MongoId();
// Act & Assert
Assert.DoesNotThrow(() => _helper.DeleteInventory(pmcData, sessionId));
}
[Test]
public void DeleteInventory_ShouldRemoveSomeItems()
{
// Arrange
var pmcData = CreateTestPmcData();
var sessionId = new MongoId();
var initialItemCount = pmcData.Inventory.Items.Count;
// Act
_helper.DeleteInventory(pmcData, sessionId);
// Assert
// The main goal is to verify that the collection modification bug is fixed
// We expect some items to be removed, but the exact count depends on configuration
Assert.LessOrEqual(pmcData.Inventory.Items.Count, initialItemCount);
// Verify that the method completed without throwing collection modification exception
Assert.Pass("DeleteInventory completed successfully without collection modification exception");
}
private static PmcData CreateTestPmcData()
{
var equipmentId = new MongoId();
var questRaidItemsId = new MongoId();
var items = new List<Item>
{
// Equipment items (should be removed)
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439011"), // weapon_ak74
ParentId = equipmentId.ToString(),
SlotId = "FirstPrimaryWeapon",
},
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439012"), // ammo_545x39
ParentId = equipmentId.ToString(),
SlotId = "pocket1",
},
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439013"), // medkit
ParentId = equipmentId.ToString(),
SlotId = "pocket2",
},
// Quest raid items (should be removed)
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439014"), // quest_item
ParentId = questRaidItemsId.ToString(),
SlotId = "quest",
},
// Stash items (should be kept) - these have ParentId = null
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439015"), // money
ParentId = null,
SlotId = "hideout",
},
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439016"), // another stash item
ParentId = null,
SlotId = "hideout",
},
new()
{
Id = new MongoId(),
Template = new MongoId("507f1f77bcf86cd799439017"), // third stash item
ParentId = null,
SlotId = "hideout",
},
};
return new PmcData
{
Id = new MongoId(),
Inventory = new BotBaseInventory
{
Items = items,
Equipment = equipmentId,
QuestRaidItems = questRaidItemsId,
FastPanel = new Dictionary<string, MongoId>(),
},
InsuredItems = [],
};
}
}
@@ -0,0 +1,166 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Helpers;
using SPTarkov.Server.Core.Models.Common;
using SPTarkov.Server.Core.Models.Eft.Common.Tables;
namespace UnitTests.Tests.Helpers;
[TestFixture]
public class InventoryHelperTests
{
private InventoryHelper _helper;
private PresetHelper _presetHelper;
[OneTimeSetUp]
public void Initialize()
{
_helper = DI.GetInstance().GetService<InventoryHelper>();
_presetHelper = DI.GetInstance().GetService<PresetHelper>();
}
[Test]
public void GetItemSize_vss_val()
{
var vssValPreset = _presetHelper.GetDefaultPreset(ItemTpl.MARKSMANRIFLE_VSS_VINTOREZ_9X39_SPECIAL_SNIPER_RIFLE);
var result = _helper.GetItemSize(
ItemTpl.MARKSMANRIFLE_VSS_VINTOREZ_9X39_SPECIAL_SNIPER_RIFLE,
vssValPreset.Parent,
vssValPreset.Items
);
Assert.AreEqual(5, result.Item1);
Assert.AreEqual(2, result.Item2);
}
[Test]
public void GetItemSize_m4a1()
{
var vssValPreset = _presetHelper.GetDefaultPreset(ItemTpl.ASSAULTRIFLE_COLT_M4A1_556X45_ASSAULT_RIFLE);
var result = _helper.GetItemSize(ItemTpl.ASSAULTRIFLE_COLT_M4A1_556X45_ASSAULT_RIFLE, vssValPreset.Parent, vssValPreset.Items);
Assert.AreEqual(5, result.Item1);
Assert.AreEqual(2, result.Item2);
}
[Test]
public void GetItemSize_glock_17()
{
var vssValPreset = _presetHelper.GetDefaultPreset(ItemTpl.PISTOL_GLOCK_17_9X19);
var result = _helper.GetItemSize(ItemTpl.PISTOL_GLOCK_17_9X19, vssValPreset.Parent, vssValPreset.Items);
Assert.AreEqual(2, result.Item1);
Assert.AreEqual(1, result.Item2);
}
[Test]
public void GetItemSize_custom_vpo_136_6x2()
{
var rootWeaponId = new MongoId();
var weaponWithChildren = new List<Item>();
var root = new Item { Id = rootWeaponId, Template = ItemTpl.ASSAULTRIFLE_MOLOT_ARMS_VPO136_VEPRKM_762X39_CARBINE };
weaponWithChildren.Add(root);
var stock = new Item
{
Id = new MongoId(),
Template = ItemTpl.STOCK_VPO136_VEPRKM_WOODEN,
ParentId = root.Id,
SlotId = "mod_stock",
};
weaponWithChildren.Add(stock);
var magazine = new Item
{
Id = new MongoId(),
Template = ItemTpl.MAGAZINE_366TKM_AK_AL_10RND,
ParentId = root.Id,
SlotId = "mod_magazine",
};
weaponWithChildren.Add(magazine);
var muzzle = new Item
{
Id = new MongoId(),
Template = ItemTpl.SILENCER_AKM_HEXAGON_762X39_SOUND_SUPPRESSOR,
ParentId = root.Id,
SlotId = "mod_muzzle",
};
weaponWithChildren.Add(muzzle);
var result = _helper.GetItemSize(root.Template, rootWeaponId, weaponWithChildren);
Assert.AreEqual(6, result.Item1);
Assert.AreEqual(2, result.Item2);
}
[Test]
public void GetItemSize_uzi_unfolded()
{
var rootWeaponId = new MongoId();
var weaponWithChildren = new List<Item>();
var root = new Item { Id = rootWeaponId, Template = ItemTpl.SMG_IWI_UZI_9X19_SUBMACHINE_GUN };
weaponWithChildren.Add(root);
var stock = new Item
{
Id = new MongoId(),
Template = "6699249f3c4fda6471005cba",
ParentId = root.Id,
SlotId = "mod_stock",
};
weaponWithChildren.Add(stock);
var magazine = new Item
{
Id = new MongoId(),
Template = "669927203c4fda6471005cbe",
ParentId = root.Id,
SlotId = "mod_magazine",
};
weaponWithChildren.Add(magazine);
var result = _helper.GetItemSize(ItemTpl.SMG_IWI_UZI_9X19_SUBMACHINE_GUN, rootWeaponId, weaponWithChildren);
Assert.AreEqual(3, result.Item1);
Assert.AreEqual(2, result.Item2);
}
[Test]
public void GetItemSize_uzi_folded()
{
var rootWeaponId = new MongoId();
var weaponWithChildren = new List<Item>();
var root = new Item { Id = rootWeaponId, Template = ItemTpl.SMG_IWI_UZI_9X19_SUBMACHINE_GUN };
weaponWithChildren.Add(root);
var stock = new Item
{
Id = new MongoId(),
Template = "6699249f3c4fda6471005cba",
ParentId = root.Id,
SlotId = "mod_stock",
Upd = new Upd { Foldable = new UpdFoldable { Folded = true } },
};
weaponWithChildren.Add(stock);
var magazine = new Item
{
Id = new MongoId(),
Template = "669927203c4fda6471005cbe",
ParentId = root.Id,
SlotId = "mod_magazine",
};
weaponWithChildren.Add(magazine);
var result = _helper.GetItemSize(ItemTpl.SMG_IWI_UZI_9X19_SUBMACHINE_GUN, rootWeaponId, weaponWithChildren);
Assert.AreEqual(2, result.Item1);
Assert.AreEqual(2, result.Item2);
}
}
+69
View File
@@ -0,0 +1,69 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
namespace UnitTests.Tests;
[TestFixture]
public class MongoIDTests
{
[OneTimeSetUp]
public void Initialize() { }
[Test]
public void GenerateTest()
{
// Generate 100 MongoId's
for (var i = 0; i < 100; i++)
{
// Invalid mongoId character
var result = new MongoId();
// Invalid mongoId length
var test = result.IsValidMongoId();
Assert.IsTrue(test, $"IsValidMongoId(): `{result}` is not a valid MongoId.");
}
}
[TestCase("677ddb67406e9918a0264bbz", false, "677ddb67406e9918a0264bbz contains invalid char `z`, but result was true")]
[TestCase("677ddb67406e9918a0264bbcc", false, "677ddb67406e9918a0264bbcc is 25 characters, but result was true")]
[TestCase("677ddb67406e9918a0264bbc", true, "IsValidMongoId() `677ddb67406e9918a0264bbc` is a valid mongoId, but result was false")]
public void IsValidMongoIdTest(string mongoId, bool passes, string failMessage)
{
var result = new MongoId(mongoId);
Assert.AreEqual(passes, passes, result, failMessage);
}
[Test]
public void MultiThreadedMongoIDGenerationTest()
{
var concurrentBag = new ConcurrentBag<string>();
var random = new Random();
var stopwatch = new Stopwatch();
stopwatch.Start();
Parallel.For(
0,
1000,
i =>
{
Thread.Sleep(random.Next(0, 10));
var mongoId = new MongoId();
concurrentBag.Add(mongoId);
}
);
stopwatch.Stop();
Console.WriteLine($"Elapsed time: {stopwatch.ElapsedMilliseconds} ms");
var uniqueCount = concurrentBag.Distinct().Count();
var totalCount = concurrentBag.Count;
Assert.AreEqual(
totalCount,
uniqueCount,
$"Expected all generated MongoId's to be unique, but found: {totalCount - uniqueCount} duplicates."
);
}
}
@@ -0,0 +1,27 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Models.Enums;
using SPTarkov.Server.Core.Utils;
namespace UnitTests.Tests.Utils;
[TestFixture]
public class JsonUtilTests
{
private JsonUtil _jsonUtil;
[OneTimeSetUp]
public void Initialize()
{
_jsonUtil = DI.GetInstance().GetService<JsonUtil>();
}
[Test]
public void SerializeAndDeserialize_WithDictionaryOfETFEnum_ExpectCorrectParsing()
{
var value = new Dictionary<QuestStatusEnum, int> { { QuestStatusEnum.AvailableForStart, 1 } };
var result = _jsonUtil.Deserialize<Dictionary<QuestStatusEnum, int>>(_jsonUtil.Serialize(value));
Assert.AreEqual(value.Count, result?.Count);
Assert.AreEqual(value.First().Key, result?.First().Key);
Assert.AreEqual(value.First().Value, result?.First().Value);
}
}
@@ -0,0 +1,101 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Utils;
namespace UnitTests.Tests.Utils;
[TestFixture]
public class MathUtilTests
{
private MathUtil _mathUtil;
[OneTimeSetUp]
public void Initialize()
{
_mathUtil = DI.GetInstance().GetService<MathUtil>();
}
[Test]
public void ListSumTest()
{
var test = new List<float> { 1.1f, 2.1f, 3.3f };
const double expected = 6.5f;
var actual = test.Sum();
Assert.AreEqual(expected, actual, $"ListSum() Expected: {expected}, Actual: {actual}");
}
[Test]
public void ListCumSumTest()
{
var test = new List<double> { 1f, 2f, 3f, 4f };
var expected = new List<double> { 1f, 3f, 6f, 10f };
var actual = test.CumulativeSum().ToList();
for (var i = 0; i < actual.Count; i++)
{
if (Math.Abs(expected[i] - actual[i]) > 0.00001f)
{
Assert.Fail($"ListCumSum() Expected: {string.Join(", ", expected)}, Actual: {string.Join(", ", actual)}");
}
}
}
[Test]
public void ListProductTest()
{
var test = new List<double> { 1f, 2f, 3f, 4f };
var expected = new List<double> { 2f, 4f, 6f, 8f };
var actual = test.Product(2).ToList();
for (var i = 0; i < actual.Count; i++)
{
if (Math.Abs(expected[i] - actual[i]) > 0.00001f)
{
Assert.Fail($"ListProduct() Expected: {string.Join(", ", expected)}, Actual: {string.Join(", ", actual)}");
}
}
}
[Test]
public void ListAddTest()
{
var test = new List<double> { 1f, 2f, 3f, 4f };
var expected = new List<double> { 3f, 4f, 5f, 6f };
var actual = _mathUtil.ListAdd(test, 2).ToList();
for (var i = 0; i < actual.Count; i++)
{
if (Math.Abs(expected[i] - actual[i]) > 0.00001f)
{
Assert.Fail($"ListProduct() Expected: {string.Join(", ", expected)}, Actual: {string.Join(", ", actual)}");
}
}
}
[Test]
public void MapToRangeTest()
{
const double expected = 2;
var actual = _mathUtil.MapToRange(0.5, 0, 1, 1, 3);
Assert.AreEqual(expected, actual, $"MapToRange() Expected: {expected}, Actual: {actual}");
}
[TestCase(15d, new double[] { 1, 10, 20, 30, 40, 50, 60 }, new double[] { 11000, 20000, 32000, 45000, 58000, 70000, 82000 }, 26000d)]
[TestCase(5d, new double[] { 1, 10 }, new double[] { 0, 1000 }, 444.44444444444446d)]
[TestCase(12d, new double[] { 1, 10, 500, 510 }, new double[] { 0, 10, 20, 30 }, 10.040816326530612d)]
[TestCase(1d, new double[] { 1, 10, 500, 510 }, new double[] { 2, 10, 20, 30 }, 2d)]
[TestCase(11d, new double[] { 1, 10 }, new double[] { 2, 10 }, 10d)]
public void InterpTest(double input, double[] x, double[] y, double expected)
{
var actual = _mathUtil.Interp1(input, x.ToList(), y.ToList());
Assert.AreEqual(expected, actual, $"Interp1() Expected: {expected}, Actual: {actual}");
}
}
@@ -0,0 +1,35 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Extensions;
using SPTarkov.Server.Core.Models.Common;
namespace UnitTests.Tests.Utils;
/// <summary>
/// Unit tests for the <see cref="MongoId"/> struct.
/// </summary>
[TestFixture]
public class MongoIdTests
{
/// <summary>
/// Test that generates 1000 <see cref="MongoId"/> and ensures they are all valid. <br/>
/// Validity is checked by ensuring the ID is non-empty, exactly 24 characters, and matches the expected format.
/// </summary>
[Test]
public void Generate_ShouldProduceValidMongoIds()
{
var invalidIds = new List<string>();
for (var i = 0; i < 1000; i++)
{
var id = new MongoId();
var idStr = id.ToString();
if (string.IsNullOrWhiteSpace(idStr) || idStr.Length != 24 || !id.IsValidMongoId())
{
invalidIds.Add(idStr);
}
}
Assert.AreEqual(0, invalidIds.Count, $"Invalid MongoIds found: {string.Join(", ", invalidIds)}");
}
}
@@ -0,0 +1,205 @@
using NUnit.Framework;
using SPTarkov.Server.Core.Utils;
namespace UnitTests.Tests.Utils;
[TestFixture]
[TestFixture]
public sealed class RandomUtilTests
{
private RandomUtil _randomUtil;
[OneTimeSetUp]
public void Initialize()
{
_randomUtil = DI.GetInstance().GetService<RandomUtil>();
}
[Test]
public void GetIntTest()
{
// Run 10000 test cases
for (var i = 0; i < 10000; i++)
{
var result = _randomUtil.GetInt(0, 10);
if (result < 0 || result > 10)
{
Assert.Fail($"GetInt(0, 10) out of range. Expected range [0, 10] but was {result}.");
}
}
}
[Test]
public void GetIntExTest()
{
// Run 10000 test cases
for (var i = 0; i < 10000; i++)
{
var result = _randomUtil.GetInt(1, 10, true);
if (result < 1 || result > 9)
{
Assert.Fail($"GetInt(10) out of range. Expected range [1, 9] but was {result}.");
}
}
}
[Test]
public void GetDoubleTest()
{
// Run 10000 test cases
for (var i = 0; i < 10000; i++)
{
var result = _randomUtil.GetDouble(0D, 10D);
if (result is < 0d or >= 10d)
{
Assert.Fail($"GetDouble(0d, 10d) out of range. Expected range [0.0d, 9.999d] but was {result}.");
}
}
}
[Test]
public void GetPercentOfValueTest()
{
const float expected = 45.5f;
var result = _randomUtil.GetPercentOfValue(45.5f, 100f);
Assert.AreEqual(expected, result, 0.0001f, $"GetPercentOfValue(45.5f, 100f) out of range. Expected: {expected}. Actual: {result}.");
}
[Test]
public void ReduceValueByPercentTest()
{
const float expected = 54.5f;
var result = _randomUtil.ReduceValueByPercent(100f, 45.5f);
Assert.AreEqual(
expected,
result,
0.0001f,
$"ReduceValueByPercent(100f, 45.5f) out of range. Expected: {expected}. Actual: {result}."
);
}
[Test]
public void GetChance100Test()
{
for (var i = 0; i < 100; i++)
{
const bool expectedTrue = true;
var resultTrue = _randomUtil.GetChance100(100f);
Assert.AreEqual(expectedTrue, resultTrue, $"GetChance100(100f) out of range. Expected: {expectedTrue}. Actual: {resultTrue}.");
}
for (var i = 0; i < 100; i++)
{
const bool expectedFalse = false;
var resultFalse = _randomUtil.GetChance100(0f);
Assert.AreEqual(
expectedFalse,
resultFalse,
$"GetChance100(0f) out of range. Expected: {expectedFalse}. Actual: {resultFalse}."
);
}
}
// TODO: Missing methods between these two
[Test]
public void RandIntTest()
{
for (var i = 0; i < 100; i++)
{
var result = _randomUtil.RandInt(0, 10);
if (result < 0 || result > 9)
{
Assert.Fail($"RandInt(0, 10) out of range. Expected range [0, 9] but was {result}.");
}
}
for (var i = 0; i < 100; i++)
{
var result = _randomUtil.RandInt(10);
if (result < 0 || result > 9)
{
Assert.Fail($"RandInt(10, null) out of range. Expected range [0, 9] but was {result}.");
}
}
}
[Test]
public void RandNumTest()
{
for (var i = 0; i < 10000; i++)
{
var result = _randomUtil.RandNum(0, 10, 15);
if (result < 0 || result >= 10)
{
Assert.Fail($"RandNum(0, 10) out of range. Expected range [0, 9.999d] but was {result}.");
}
if (_randomUtil.GetNumberPrecision(result) > RandomUtil.MaxSignificantDigits)
{
Assert.Fail(
$"RandNum(0, 10) precision of {result} exceeds the allowable precision ({RandomUtil.MaxSignificantDigits}) for the given values."
);
}
}
for (var i = 0; i < 10000; i++)
{
var result = _randomUtil.RandNum(10);
if (result < 0 || result >= 10)
{
Assert.Fail($"RandNum(10) out of range. Expected range [0, 9.999d] but was {result}.");
}
if (_randomUtil.GetNumberPrecision(result) > RandomUtil.MaxSignificantDigits)
{
Assert.Fail(
$"RandNum(10) precision of {result} exceeds the allowable precision ({RandomUtil.MaxSignificantDigits}) for the given values."
);
}
}
}
[Test]
public void ShuffleTest()
{
var testList = new List<int> { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var orig = new List<int>(testList);
var result = _randomUtil.Shuffle(testList);
Assert.IsFalse(
result.SequenceEqual(orig),
$"Shuffle test failed. Expected: {string.Join(", ", orig)}, but got {string.Join(", ", result)}"
);
}
[TestCase(0.1, 1)]
[TestCase(0.0001, 4)]
[TestCase(0, 0)]
[TestCase(10000000, 0)]
[TestCase(0.000_000_000_000_000_000_000_000_1D, 25)]
public void GetNumberPrecision_WithDoubles_ReturnsDecimalPoints(double value, int decimalPoints)
{
Assert.AreEqual(decimalPoints, _randomUtil.GetNumberPrecision(value));
}
[TestCase(new[] { "test" }, "test", "Expected first array value")]
public void GetArrayValueTest(string[] input, string expectedOutput, string failMessage)
{
var result = _randomUtil.GetArrayValue(input);
Assert.AreEqual(input.First(), result, failMessage);
}
}
+25
View File
@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\Build.props" />
<PropertyGroup>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.3.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Libraries\SPTarkov.Server.Assets\SPTarkov.Server.Assets.csproj" />
<ProjectReference Include="..\..\Libraries\SPTarkov.Server.Core\SPTarkov.Server.Core.csproj" />
<ProjectReference Include="..\..\Libraries\SPTarkov.DI\SPTarkov.DI.csproj" />
<ProjectReference Include="..\..\Libraries\SPTarkov.Common\SPTarkov.Common.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="TestAssets\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<Compile Remove="Tests\Mock\MockSptLogger.cs" />
</ItemGroup>
</Project>