117 lines
3.6 KiB
C#
117 lines
3.6 KiB
C#
using System.Text;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using NeonSprawl.Server.Game.Skills;
|
|
using Xunit;
|
|
|
|
namespace NeonSprawl.Server.Tests.Game.Skills;
|
|
|
|
public sealed class ContentBackedSkillLevelCurveTests
|
|
{
|
|
private const string LevelCurveSchemaJson =
|
|
"""
|
|
{
|
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["schemaVersion", "levels"],
|
|
"properties": {
|
|
"schemaVersion": { "type": "integer", "const": 1 },
|
|
"levels": {
|
|
"type": "array",
|
|
"minItems": 1,
|
|
"items": {
|
|
"type": "object",
|
|
"additionalProperties": false,
|
|
"required": ["level", "requiredXp"],
|
|
"properties": {
|
|
"level": { "type": "integer", "minimum": 1 },
|
|
"requiredXp": { "type": "integer", "minimum": 0 }
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
""";
|
|
|
|
private static ContentBackedSkillLevelCurve BuildCurve() =>
|
|
new(
|
|
[
|
|
new ContentBackedSkillLevelCurve.LevelThresholdRow(1, 0),
|
|
new ContentBackedSkillLevelCurve.LevelThresholdRow(2, 100),
|
|
new ContentBackedSkillLevelCurve.LevelThresholdRow(3, 250),
|
|
new ContentBackedSkillLevelCurve.LevelThresholdRow(4, 450),
|
|
]);
|
|
|
|
[Theory]
|
|
[InlineData(-5, 1)]
|
|
[InlineData(0, 1)]
|
|
[InlineData(99, 1)]
|
|
[InlineData(100, 2)]
|
|
[InlineData(249, 2)]
|
|
[InlineData(250, 3)]
|
|
[InlineData(449, 3)]
|
|
[InlineData(450, 4)]
|
|
[InlineData(9999, 4)]
|
|
public void LevelFromTotalXp_ShouldResolveExpectedLevelAcrossBoundaries(int xp, int expectedLevel)
|
|
{
|
|
// Arrange
|
|
var curve = BuildCurve();
|
|
|
|
// Act
|
|
var level = curve.LevelFromTotalXp(xp);
|
|
|
|
// Assert
|
|
Assert.Equal(expectedLevel, level);
|
|
}
|
|
|
|
[Fact]
|
|
public void Load_ShouldThrow_WhenCurveHasUnexpectedTopLevelProperty()
|
|
{
|
|
// Arrange
|
|
var root = Directory.CreateTempSubdirectory("neon-sprawl-level-curve-");
|
|
try
|
|
{
|
|
var skillsDir = Path.Combine(root.FullName, "content", "skills");
|
|
var schemaDir = Path.Combine(root.FullName, "content", "schemas");
|
|
Directory.CreateDirectory(skillsDir);
|
|
Directory.CreateDirectory(schemaDir);
|
|
|
|
var curvePath = Path.Combine(skillsDir, "prototype_level_curve.json");
|
|
var schemaPath = Path.Combine(schemaDir, "level-curve.schema.json");
|
|
File.WriteAllText(schemaPath, LevelCurveSchemaJson, Encoding.UTF8);
|
|
File.WriteAllText(
|
|
curvePath,
|
|
"""
|
|
{
|
|
"schemaVersion": 1,
|
|
"levels": [
|
|
{ "level": 1, "requiredXp": 0 },
|
|
{ "level": 2, "requiredXp": 100 }
|
|
],
|
|
"extraField": "should-fail"
|
|
}
|
|
""",
|
|
Encoding.UTF8);
|
|
|
|
// Act
|
|
var ex = Record.Exception(() =>
|
|
ContentBackedSkillLevelCurve.Load(skillsDir, curvePath, schemaPath, NullLogger.Instance));
|
|
|
|
// Assert
|
|
var ioe = Assert.IsType<InvalidOperationException>(ex);
|
|
Assert.Contains("extraField", ioe.Message, StringComparison.Ordinal);
|
|
}
|
|
finally
|
|
{
|
|
try
|
|
{
|
|
Directory.Delete(root.FullName, recursive: true);
|
|
}
|
|
catch (IOException)
|
|
{
|
|
// Best-effort cleanup only.
|
|
}
|
|
}
|
|
}
|
|
}
|