flat snapshot record per aggregate - separate files mean saving a run doesn't rewrite the Profile, deleting a slot is deleting a file, and a corrupt slot doesn't cost achievements or best times. snapshots hold primitives so Restore re-runs the value-object factories; a serialiser building LevelId directly would bypass every guard. LoadResult has three outcomes: Absent != Corrupt. Collapsing them is the bug where a version bump made ContinueGame overwrite the save. Profile.Restore writes collections directly - replaying UnlockAchievement would re-fire popups and Steam pushes on every load. 44 tests.
35 lines
1.2 KiB
C#
35 lines
1.2 KiB
C#
namespace Mr.BrickAdventures.Progression.Tests;
|
|
|
|
/// <summary>
|
|
/// The test-side adapter for <see cref="IPlaythroughStore"/>. Its existence alongside the JSON store
|
|
/// is what makes the port a real seam rather than a hypothetical one.
|
|
/// </summary>
|
|
internal sealed class InMemoryPlaythroughStore : IPlaythroughStore
|
|
{
|
|
private readonly Dictionary<Guid, PlaythroughSnapshot> _saved = new();
|
|
private readonly HashSet<Guid> _corrupt = new();
|
|
|
|
public void Save(Playthrough playthrough) => _saved[playthrough.Id.Value] = playthrough.Export();
|
|
|
|
/// <summary>Simulates a file that exists but cannot be read back into a valid snapshot.</summary>
|
|
public void Corrupt(PlaythroughId id) => _corrupt.Add(id.Value);
|
|
|
|
public LoadResult<Playthrough> Load(PlaythroughId id)
|
|
{
|
|
if (_corrupt.Contains(id.Value))
|
|
return LoadResult<Playthrough>.Corrupt("snapshot could not be read");
|
|
|
|
if (!_saved.TryGetValue(id.Value, out var snapshot))
|
|
return LoadResult<Playthrough>.Absent();
|
|
|
|
try
|
|
{
|
|
return LoadResult<Playthrough>.Loaded(Playthrough.Restore(snapshot));
|
|
}
|
|
catch (Exception e)
|
|
{
|
|
return LoadResult<Playthrough>.Corrupt(e.Message);
|
|
}
|
|
}
|
|
}
|