Files
YachtDice/Assets/Scripts/Tests/Editor/RunLoopServiceTests.cs
T
2026-03-18 09:13:48 +07:00

221 lines
7.6 KiB
C#

using System.Collections.Generic;
using NUnit.Framework;
using UnityEngine;
using YachtDice.Categories;
using YachtDice.Dice;
using YachtDice.Economy;
using YachtDice.Run;
using YachtDice.Scoring;
namespace YachtDice.Tests
{
public sealed class RunLoopServiceTests
{
private readonly List<Object> _createdAssets = new();
private readonly IReadOnlyList<IDice> _emptyDice = new IDice[0];
private CurrencyBank _currencyBank;
private ScoringSystem _scoringSystem;
private StoredRollBank _storedRollBank;
private RunBalanceConfigSO _config;
[SetUp]
public void SetUp()
{
var bankGo = new GameObject("CurrencyBank");
_currencyBank = bankGo.AddComponent<CurrencyBank>();
_currencyBank.SetBalance(0);
var scoringGo = new GameObject("ScoringSystem");
_scoringSystem = scoringGo.AddComponent<ScoringSystem>();
_storedRollBank = new StoredRollBank();
_config = RunBalanceConfigSO.CreateDefault();
_createdAssets.Add(_config);
}
[TearDown]
public void TearDown()
{
foreach (var scoring in Object.FindObjectsByType<ScoringSystem>(FindObjectsSortMode.None))
Object.DestroyImmediate(scoring.gameObject);
foreach (var bank in Object.FindObjectsByType<CurrencyBank>(FindObjectsSortMode.None))
Object.DestroyImmediate(bank.gameObject);
for (var i = 0; i < _createdAssets.Count; i++)
{
if (_createdAssets[i] != null)
Object.DestroyImmediate(_createdAssets[i]);
}
_createdAssets.Clear();
}
[Test]
public void StartNewRun_EntersShopWithConfigQuota()
{
var categories = CreateFixedCatalog(100, 100, 100);
var service = CreateService(categories);
service.StartNewRun();
Assert.AreEqual(30, service.State.BaseQuota);
Assert.AreEqual(1, service.State.BetIndex);
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
Assert.AreEqual(0, service.State.StoredRolls);
}
[Test]
public void ClearedStage_AwardsCurrencyAndBanksUnusedRolls()
{
var categories = CreateFixedCatalog(100, 100, 100);
var service = CreateService(categories);
service.StartNewRun();
service.CompleteShop();
service.TryBeginRoll();
service.NotifyRollResolved(_emptyDice);
var cleared = service.TryScoreCategoryAsync(_emptyDice, categories.All[0]).GetAwaiter().GetResult();
Assert.IsTrue(cleared);
Assert.AreEqual(25, _currencyBank.Balance);
Assert.AreEqual(2, service.State.StoredRolls);
Assert.AreEqual(2, service.State.StageNumber);
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
}
[Test]
public void StoredRolls_CanBeConsumedOnLaterStage()
{
var categories = CreateFixedCatalog(100, 100, 100);
var service = CreateService(categories);
service.StartNewRun();
service.CompleteShop();
service.TryBeginRoll();
service.NotifyRollResolved(_emptyDice);
service.TryScoreCategoryAsync(_emptyDice, categories.All[0]).GetAwaiter().GetResult();
for (var i = 0; i < 4; i++)
{
Assert.IsTrue(service.TryBeginRoll());
service.NotifyRollResolved(_emptyDice);
}
service.TryScoreCategoryAsync(_emptyDice, categories.All[1]).GetAwaiter().GetResult();
Assert.AreEqual(1, service.State.StoredRolls);
Assert.AreEqual(50, _currencyBank.Balance);
}
[Test]
public void CompletedCycle_GrantsBonusRaisesQuotaAndStartsNextBet()
{
var categories = CreateFixedCatalog(100, 100, 100);
var service = CreateService(categories);
service.StartNewRun();
service.CompleteShop();
ClearStageInOneRoll(service, categories.All[0]);
ClearStageInOneRoll(service, categories.All[1]);
ClearStageInOneRoll(service, categories.All[2]);
Assert.AreEqual(135, _currencyBank.Balance);
Assert.AreEqual(60, service.State.BaseQuota);
Assert.AreEqual(2, service.State.BetIndex);
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
Assert.AreEqual(6, service.State.StoredRolls);
Assert.AreEqual(0, _scoringSystem.CategoriesFilledCount);
}
[Test]
public void FailedStage_EndsRunWhenNoTargetCanBeMet()
{
var categories = CreateFixedCatalog(10);
var service = CreateService(categories);
service.StartNewRun();
service.CompleteShop();
for (var i = 0; i < 3; i++)
{
Assert.IsTrue(service.TryBeginRoll());
service.NotifyRollResolved(_emptyDice);
}
Assert.IsTrue(service.State.IsFailed);
Assert.AreEqual(RunPhase.RunFailed, service.State.Phase);
}
[Test]
public void Shop_ReopensOnlyAtStartOfNextBet()
{
var categories = CreateFixedCatalog(100, 100, 100, 100);
var service = CreateService(categories);
service.StartNewRun();
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
service.CompleteShop();
ClearStageInOneRoll(service, categories.All[0]);
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
ClearStageInOneRoll(service, categories.All[1]);
Assert.AreEqual(RunPhase.StageStart, service.State.Phase);
ClearStageInOneRoll(service, categories.All[2]);
Assert.AreEqual(RunPhase.Shop, service.State.Phase);
Assert.AreEqual(2, service.State.BetIndex);
}
private RunLoopService CreateService(CategoryCatalog catalog)
{
_scoringSystem.Construct(null, null, catalog, _currencyBank);
return new RunLoopService(_config, _scoringSystem, _currencyBank, _storedRollBank);
}
private CategoryCatalog CreateFixedCatalog(params int[] scores)
{
var categories = new List<CategoryDefinition>();
for (var i = 0; i < scores.Length; i++)
{
var category = FixedScoreCategory.Create($"cat_{i}", scores[i]);
_createdAssets.Add(category);
categories.Add(category);
}
var catalog = CategoryCatalog.CreateForTest(categories);
_createdAssets.Add(catalog);
return catalog;
}
private void ClearStageInOneRoll(RunLoopService service, CategoryDefinition category)
{
Assert.IsTrue(service.TryBeginRoll());
service.NotifyRollResolved(_emptyDice);
Assert.IsTrue(service.TryScoreCategoryAsync(_emptyDice, category).GetAwaiter().GetResult());
}
private sealed class FixedScoreCategory : CategoryDefinition
{
[SerializeField] private int fixedScore;
public override int Calculate(IReadOnlyList<IDice> dice)
{
return fixedScore;
}
public static FixedScoreCategory Create(string id, int score)
{
var category = CreateInstance<FixedScoreCategory>();
category.SetTestData(id, id);
category.fixedScore = score;
return category;
}
}
}
}