feat(progression): remaining slice 1 value objects
Lives (floors at zero, exhaustion observable), LevelId, SkillId, PlaythroughId (never self-generates, keeps tests deterministic), StatName enum, LevelTime rejects NaN/infinity. 14 tests. Arithmetic/comparison members deferred to the slices whose behaviour needs them rather than added speculatively.
This commit is contained in:
@@ -14,4 +14,10 @@ public class CoinsTests
|
||||
// which is the safe outcome; clamping would silently consume what wasn't there.
|
||||
Assert.Equal(Coins.Of(10), remaining);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_negative_quantity_of_coins_cannot_exist()
|
||||
{
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => Coins.Of(-1));
|
||||
}
|
||||
}
|
||||
|
||||
14
progression.tests/LevelIdTests.cs
Normal file
14
progression.tests/LevelIdTests.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace Mr.BrickAdventures.Progression.Tests;
|
||||
|
||||
public class LevelIdTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void A_blank_id_is_not_a_level(string value)
|
||||
{
|
||||
// Levels are addressed by a stable authored id, never by position in a list. An unset
|
||||
// field in a .tres would otherwise become a valid id that silently collides with itself.
|
||||
Assert.Throws<ArgumentException>(() => LevelId.Of(value));
|
||||
}
|
||||
}
|
||||
@@ -10,4 +10,14 @@ public class LevelTimeTests
|
||||
// files. Rejecting zero makes that unrepresentable rather than merely unlikely.
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => LevelTime.Of(0));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(double.NaN)]
|
||||
[InlineData(double.PositiveInfinity)]
|
||||
public void A_time_that_is_not_a_finite_number_is_rejected(double seconds)
|
||||
{
|
||||
// Times are derived from subtracting tick counts, so a stale or unset start time can
|
||||
// produce NaN or infinity rather than a plausible-but-wrong number.
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => LevelTime.Of(seconds));
|
||||
}
|
||||
}
|
||||
|
||||
21
progression.tests/LivesTests.cs
Normal file
21
progression.tests/LivesTests.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Mr.BrickAdventures.Progression.Tests;
|
||||
|
||||
public class LivesTests
|
||||
{
|
||||
[Fact]
|
||||
public void Losing_the_last_life_exhausts_them()
|
||||
{
|
||||
var remaining = Lives.Of(1).Decrement();
|
||||
|
||||
Assert.Equal(0, remaining.Value);
|
||||
Assert.True(remaining.IsExhausted);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Decrementing_when_already_exhausted_stays_at_zero()
|
||||
{
|
||||
var remaining = Lives.Of(0).Decrement();
|
||||
|
||||
Assert.Equal(0, remaining.Value);
|
||||
}
|
||||
}
|
||||
13
progression.tests/PlaythroughIdTests.cs
Normal file
13
progression.tests/PlaythroughIdTests.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace Mr.BrickAdventures.Progression.Tests;
|
||||
|
||||
public class PlaythroughIdTests
|
||||
{
|
||||
[Fact]
|
||||
public void The_empty_guid_is_not_a_playthrough()
|
||||
{
|
||||
// Ids arrive from an adapter, never from Guid.NewGuid() inside the domain — otherwise every
|
||||
// test touching identity would be non-deterministic. That makes a default-valued guid a
|
||||
// realistic mistake worth refusing.
|
||||
Assert.Throws<ArgumentException>(() => PlaythroughId.Of(Guid.Empty));
|
||||
}
|
||||
}
|
||||
20
progression.tests/SkillIdTests.cs
Normal file
20
progression.tests/SkillIdTests.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace Mr.BrickAdventures.Progression.Tests;
|
||||
|
||||
public class SkillIdTests
|
||||
{
|
||||
[Theory]
|
||||
[InlineData("")]
|
||||
[InlineData(" ")]
|
||||
public void A_blank_id_is_not_a_skill(string value)
|
||||
{
|
||||
Assert.Throws<ArgumentException>(() => SkillId.Of(value));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Ids_with_the_same_text_are_the_same_skill()
|
||||
{
|
||||
// Owned skills are held in a dictionary keyed by id, so value equality is what makes the
|
||||
// type usable at all rather than a convenience.
|
||||
Assert.Equal(SkillId.Of("brick_throw"), SkillId.Of("brick_throw"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user