feat(progression): Profile aggregate (slice 4)
best times only improve; achievement unlock announces on the transition only, so popup/Steam subscribers cannot double-fire; statistics are increase-only (IncrementStat took any int). RelockAchievement is console-only and documented as not reaching Steam - a Steam achievement cannot be un-earned, so the disagreement is permanent and deliberate. refactor: extract EventLog, composed by both aggregates now that the duplication actually exists. 34 tests.
This commit is contained in:
74
progression.tests/ProfileTests.cs
Normal file
74
progression.tests/ProfileTests.cs
Normal file
@@ -0,0 +1,74 @@
|
||||
namespace Mr.BrickAdventures.Progression.Tests;
|
||||
|
||||
public class ProfileTests
|
||||
{
|
||||
private static readonly LevelId Level = LevelId.Of("village_1");
|
||||
|
||||
[Fact]
|
||||
public void A_slower_run_does_not_replace_a_better_time()
|
||||
{
|
||||
var profile = Profile.Empty();
|
||||
|
||||
Assert.True(profile.RecordTime(Level, LevelTime.Of(42.5)));
|
||||
Assert.False(profile.RecordTime(Level, LevelTime.Of(51.0)));
|
||||
|
||||
Assert.Equal(LevelTime.Of(42.5), profile.BestTimeFor(Level));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Unlocking_the_same_achievement_twice_announces_it_once()
|
||||
{
|
||||
// AchievementManager shows a popup and pushes to Steam on every unlock call, guarded only by
|
||||
// a Contains check on a list it reached into. Announcing only on the transition means
|
||||
// subscribers cannot double-fire regardless of how often they are told.
|
||||
var rescueTen = AchievementId.Of("rescue_ten_kids");
|
||||
var profile = Profile.Empty();
|
||||
|
||||
profile.UnlockAchievement(rescueTen);
|
||||
profile.UnlockAchievement(rescueTen);
|
||||
|
||||
Assert.True(profile.HasAchievement(rescueTen));
|
||||
Assert.Single(profile.DrainEvents().OfType<AchievementUnlocked>());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Statistics_accumulate_and_an_unrecorded_one_reads_as_zero()
|
||||
{
|
||||
var profile = Profile.Empty();
|
||||
|
||||
profile.Record(StatName.CoinsCollected, 5);
|
||||
profile.Record(StatName.CoinsCollected, 3);
|
||||
|
||||
Assert.Equal(8, profile.ReadStat(StatName.CoinsCollected));
|
||||
Assert.Equal(0, profile.ReadStat(StatName.Deaths));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void A_statistic_cannot_be_decremented()
|
||||
{
|
||||
// IncrementStat currently takes any int. A lifetime counter that can go down is not a
|
||||
// lifetime counter, and nothing in the game has a reason to reduce one.
|
||||
var profile = Profile.Empty();
|
||||
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => profile.Record(StatName.Deaths, -1));
|
||||
Assert.Throws<ArgumentOutOfRangeException>(() => profile.Record(StatName.Deaths, 0));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Relocking_an_achievement_removes_it_and_lets_it_announce_again()
|
||||
{
|
||||
// Console-only. Note the asymmetry: unlocking is mirrored to Steam by a subscriber, but
|
||||
// relocking cannot be, because a Steam achievement cannot be un-earned.
|
||||
var rescueTen = AchievementId.Of("rescue_ten_kids");
|
||||
var profile = Profile.Empty();
|
||||
profile.UnlockAchievement(rescueTen);
|
||||
profile.DrainEvents();
|
||||
|
||||
profile.RelockAchievement(rescueTen);
|
||||
|
||||
Assert.False(profile.HasAchievement(rescueTen));
|
||||
|
||||
profile.UnlockAchievement(rescueTen);
|
||||
Assert.Single(profile.DrainEvents().OfType<AchievementUnlocked>());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user