Puzzles
This commit is contained in:
53
SVSim.UnitTests/Services/PuzzleMissionEvaluatorTests.cs
Normal file
53
SVSim.UnitTests/Services/PuzzleMissionEvaluatorTests.cs
Normal file
@@ -0,0 +1,53 @@
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.EmulatedEntrypoint.Services;
|
||||
|
||||
namespace SVSim.UnitTests.Services;
|
||||
|
||||
public class PuzzleMissionEvaluatorTests
|
||||
{
|
||||
private static readonly PuzzleMissionEntry Round1 = new()
|
||||
{ Id = 1, MissionName = "Clear all Round 1 puzzles", RequireNumber = 3, TargetPuzzleGroupId = 301 };
|
||||
private static readonly PuzzleMissionEntry SpecialAll = new()
|
||||
{ Id = 2, MissionName = "Clear all Special Round puzzles", RequireNumber = 8, TargetPuzzleGroupId = null };
|
||||
|
||||
private readonly PuzzleMissionEvaluator _e = new();
|
||||
|
||||
[Test]
|
||||
public void Evaluate_unmapped_mission_always_zero()
|
||||
{
|
||||
var cleared = new Dictionary<int, HashSet<int>> { [316] = new() { 106, 107, 108 } };
|
||||
var result = _e.Evaluate(new[] { SpecialAll }, cleared);
|
||||
|
||||
Assert.That(result.Single().TotalCount, Is.EqualTo(0));
|
||||
Assert.That(result.Single().IsAchieved, Is.False);
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void Evaluate_mapped_mission_counts_clears_in_target_group_capped()
|
||||
{
|
||||
var partial = new Dictionary<int, HashSet<int>> { [301] = new() { 37, 38 } };
|
||||
Assert.That(_e.Evaluate(new[] { Round1 }, partial).Single().TotalCount, Is.EqualTo(2));
|
||||
Assert.That(_e.Evaluate(new[] { Round1 }, partial).Single().IsAchieved, Is.False);
|
||||
|
||||
var complete = new Dictionary<int, HashSet<int>> { [301] = new() { 37, 38, 39 } };
|
||||
Assert.That(_e.Evaluate(new[] { Round1 }, complete).Single().IsAchieved, Is.True);
|
||||
|
||||
// Imagine a future where the group has more puzzles than RequireNumber — cap at RequireNumber.
|
||||
var over = new Dictionary<int, HashSet<int>> { [301] = new() { 37, 38, 39, 999 } };
|
||||
Assert.That(_e.Evaluate(new[] { Round1 }, over).Single().TotalCount, Is.EqualTo(3));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void FreshlyCompleted_returns_only_missions_flipping_true()
|
||||
{
|
||||
var before = new Dictionary<int, HashSet<int>> { [301] = new() { 37, 38 } };
|
||||
var after = new Dictionary<int, HashSet<int>> { [301] = new() { 37, 38, 39 } };
|
||||
|
||||
var fresh = _e.FreshlyCompleted(new[] { Round1, SpecialAll }, before, after);
|
||||
Assert.That(fresh, Has.Count.EqualTo(1));
|
||||
Assert.That(fresh[0].Mission.Id, Is.EqualTo(Round1.Id));
|
||||
|
||||
// Re-evaluating with same before==after returns no fresh completions.
|
||||
Assert.That(_e.FreshlyCompleted(new[] { Round1, SpecialAll }, after, after), Is.Empty);
|
||||
}
|
||||
}
|
||||
99
SVSim.UnitTests/Services/RewardGrantServiceTests.cs
Normal file
99
SVSim.UnitTests/Services/RewardGrantServiceTests.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Enums;
|
||||
using SVSim.Database.Models;
|
||||
using SVSim.Database.Services;
|
||||
using SVSim.UnitTests.Infrastructure;
|
||||
|
||||
namespace SVSim.UnitTests.Services;
|
||||
|
||||
public class RewardGrantServiceTests
|
||||
{
|
||||
[Test]
|
||||
public async Task Sleeve_added_to_viewer_collection()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
// Pick an Id above the seeded sleeves.csv range so this test doesn't collide with the
|
||||
// reference-CSV importer SVSimTestFactory runs at host construction.
|
||||
const int testSleeveId = 2_000_000_000;
|
||||
var sleeve = new SleeveEntry { Id = testSleeveId }; // SleeveEntry has no Name field; Id only
|
||||
ctx.Sleeves.Add(sleeve);
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var viewer = await ctx.Viewers.Include(v => v.Sleeves).FirstAsync(v => v.Id == viewerId);
|
||||
var svc = scope.ServiceProvider.GetRequiredService<RewardGrantService>();
|
||||
|
||||
var entry = svc.Apply(viewer, UserGoodsType.Sleeve, detailId: testSleeveId, num: 1);
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
Assert.That(viewer.Sleeves.Any(s => s.Id == testSleeveId), Is.True);
|
||||
Assert.That(entry.RewardType, Is.EqualTo((int)UserGoodsType.Sleeve));
|
||||
Assert.That(entry.RewardId, Is.EqualTo((long)testSleeveId));
|
||||
Assert.That(entry.RewardNum, Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Rupy_sets_currency_post_state_total()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
var viewer = await ctx.Viewers.FirstAsync(v => v.Id == viewerId);
|
||||
viewer.Currency.Rupees = 100UL;
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var svc = scope.ServiceProvider.GetRequiredService<RewardGrantService>();
|
||||
|
||||
// Reward grants 50; final balance becomes 150 and reward_num on the wire is the new total.
|
||||
var entry = svc.Apply(viewer, UserGoodsType.Rupy, detailId: 0, num: 50);
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
Assert.That(viewer.Currency.Rupees, Is.EqualTo(150UL));
|
||||
Assert.That(entry.RewardNum, Is.EqualTo(150));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task LeaderSkin_added_idempotently()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
// Pick an Id above the seeded leaderskins.csv range so this test doesn't collide with
|
||||
// the reference-CSV importer SVSimTestFactory runs at host construction.
|
||||
const int testSkinId = 9_999_999;
|
||||
ctx.LeaderSkins.Add(new LeaderSkinEntry { Id = testSkinId, Name = "Round1Reward" });
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
var viewer = await ctx.Viewers.Include(v => v.LeaderSkins).FirstAsync(v => v.Id == viewerId);
|
||||
var svc = scope.ServiceProvider.GetRequiredService<RewardGrantService>();
|
||||
|
||||
svc.Apply(viewer, UserGoodsType.Skin, testSkinId, 1);
|
||||
svc.Apply(viewer, UserGoodsType.Skin, testSkinId, 1); // second grant is a no-op on collection size
|
||||
await ctx.SaveChangesAsync();
|
||||
|
||||
Assert.That(viewer.LeaderSkins.Count(s => s.Id == testSkinId), Is.EqualTo(1));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public async Task Card_reward_throws_NotSupported()
|
||||
{
|
||||
using var factory = new SVSimTestFactory();
|
||||
long viewerId = await factory.SeedViewerAsync();
|
||||
using var scope = factory.Services.CreateScope();
|
||||
var ctx = scope.ServiceProvider.GetRequiredService<SVSimDbContext>();
|
||||
|
||||
var viewer = await ctx.Viewers.FirstAsync(v => v.Id == viewerId);
|
||||
var svc = scope.ServiceProvider.GetRequiredService<RewardGrantService>();
|
||||
|
||||
Assert.Throws<NotSupportedException>(() =>
|
||||
svc.Apply(viewer, UserGoodsType.Card, 10001001L, 1));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user