Files
Ajedrez_Purgatorio/Assets/Tests/EditMode/Campaign/CampaignStateTests.cs
T
jimmyabv f7d56066c0 feat: Purgatory Final duel vs La Muerte + SFX wiring + runtime options menu
- Duel flow: mating piece vs boss while board collapses (PurgatoryDuelManager/Rules/UI/MuerteQuotes)
- GameState.PurgatoryDuel + CampaignManager rescue/restart hooks + SquareClick routing
- Dice-based rescue of most valuable dead identity; final defeat restarts Chapter 1
- 6 SFX clips wired in Ch1/2/3 via AudioClipAssigner
- OptionsMenuController runtime audio/graphics panel, wired to MainMenu
- Tests migrated to Assets/Tests/EditMode subfolders + PurgatoryDuelRulesTests (200 total)
- Editor tools: DiagAssign, SpeakerDatabaseSetup, PurgatoryCanvasMaintenance
2026-08-24 19:39:52 -03:00

463 lines
16 KiB
C#

using NUnit.Framework;
using UnityEngine;
using System.Collections.Generic;
namespace AjedrezPurgatorio.Tests.Unit.Campaign
{
/// <summary>
/// Unit tests para CampaignState - Sistema de persistencia de campaña.
/// Cubre: persistencia de piezas, capítulos completados, contadores de pérdidas, reset.
/// </summary>
[TestFixture]
[Category("Unit")]
[Category("CampaignState")]
[Category("FastTests")]
public class CampaignStateTests
{
private CampaignState _campaignState;
private GameObject _mockRoot;
[SetUp]
public void Setup()
{
// Crear instancia fresca de CampaignState para cada test
_campaignState = ScriptableObject.CreateInstance<CampaignState>();
_mockRoot = new GameObject("CampaignStateMockRoot");
}
[TearDown]
public void Teardown()
{
// Cleanup
if (_campaignState != null)
{
Object.DestroyImmediate(_campaignState);
}
if (_mockRoot != null)
{
Object.DestroyImmediate(_mockRoot);
}
}
#region IsPieceAlive Tests
[Test]
public void IsPieceAlive_WhenPieceRegistered_ReturnsTrue()
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
// Act
bool isAlive = _campaignState.IsPieceAlive("elena");
// Assert
Assert.IsTrue(isAlive, "Elena debería estar viva después de registrarla");
}
[Test]
public void IsPieceAlive_WhenPieceNotRegistered_ReturnsFalse()
{
// Act
bool isAlive = _campaignState.IsPieceAlive("elena");
// Assert
Assert.IsFalse(isAlive, "Elena no debería estar viva si nunca fue registrada");
}
[Test]
[TestCase("Elena")]
[TestCase("ELENA")]
[TestCase("elena")]
[TestCase("ElEnA")]
public void IsPieceAlive_CaseInsensitive(string pieceName)
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
// Act
bool isAlive = _campaignState.IsPieceAlive(pieceName);
// Assert
Assert.IsTrue(isAlive, $"IsPieceAlive debería ser case-insensitive para '{pieceName}'");
}
[Test]
public void RegisterPieceLost_RemovesFromAlivePieces()
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
Assert.IsTrue(_campaignState.IsPieceAlive("elena"), "Precondición: Elena debe estar viva");
// Act
_campaignState.RegisterPieceLost("elena");
// Assert
Assert.IsFalse(_campaignState.IsPieceAlive("elena"), "Elena no debería estar viva después de RegisterPieceLost");
}
[Test]
public void RegisterPieceLost_IncrementsCounters()
{
// Arrange
_campaignState.StartChapter("ch1_factory");
int initialTotal = _campaignState.TotalPiecesLost;
int initialCurrent = _campaignState.CurrentChapterPiecesLost;
// Act
_campaignState.RegisterPieceLost("elena");
// Assert
Assert.AreEqual(initialTotal + 1, _campaignState.TotalPiecesLost, "TotalPiecesLost debería incrementar");
Assert.AreEqual(initialCurrent + 1, _campaignState.CurrentChapterPiecesLost, "CurrentChapterPiecesLost debería incrementar");
}
[Test]
public void RegisterPieceAlive_AddsToAlivePieces()
{
// Act
_campaignState.RegisterPieceAlive("ricardo");
// Assert
Assert.IsTrue(_campaignState.IsPieceAlive("ricardo"), "Ricardo debería estar vivo después de RegisterPieceAlive");
}
[Test]
public void RegisterPieceAlive_NoDuplicates()
{
// Act
_campaignState.RegisterPieceAlive("elena");
_campaignState.RegisterPieceAlive("elena"); // Doble registro
// Assert
var availableNames = _campaignState.GetAvailablePieceNames();
int elenaCount = 0;
foreach (var name in availableNames)
{
if (name.ToLower() == "elena")
elenaCount++;
}
Assert.AreEqual(1, elenaCount, "Elena no debería duplicarse en _alivePieces");
}
#endregion
#region IsChapterComplete Tests
[Test]
public void IsChapterComplete_InitiallyFalse()
{
// Act
bool isComplete = _campaignState.IsChapterComplete("ch1_factory");
// Assert
Assert.IsFalse(isComplete, "Los capítulos deberían empezar sin completar");
}
[Test]
public void CompleteChapter_MarksAsComplete()
{
// Act
_campaignState.CompleteChapter("ch1_factory", wasVictory: true);
// Assert
Assert.IsTrue(_campaignState.IsChapterComplete("ch1_factory"), "ch1_factory debería estar completado");
}
[Test]
public void CompleteChapter_StoresVictoryStatus()
{
// Act
_campaignState.CompleteChapter("ch1_factory", wasVictory: true);
// Assert
Assert.IsTrue(_campaignState.LastChapterWasVictory, "LastChapterWasVictory debería ser true");
}
[Test]
public void CompleteChapter_StoresDefeatStatus()
{
// Act
_campaignState.CompleteChapter("ch3_court", wasVictory: false);
// Assert
Assert.IsFalse(_campaignState.LastChapterWasVictory, "LastChapterWasVictory debería ser false después de derrota");
}
[Test]
public void LastChapterWasVictory_UpdatesCorrectly()
{
// Arrange
_campaignState.CompleteChapter("ch1_factory", wasVictory: true);
Assert.IsTrue(_campaignState.LastChapterWasVictory, "Precondición: primera victoria");
// Act
_campaignState.CompleteChapter("ch2_hospital", wasVictory: false);
// Assert
Assert.IsFalse(_campaignState.LastChapterWasVictory, "LastChapterWasVictory debería actualizarse con el último resultado");
}
#endregion
#region Counter Tests
[Test]
public void StartChapter_ResetsPiecesLostThisChapter()
{
// Arrange
_campaignState.StartChapter("ch1_factory");
_campaignState.RegisterPieceLost("elena");
Assert.AreEqual(1, _campaignState.CurrentChapterPiecesLost, "Precondición: 1 pieza perdida en ch1");
// Act
_campaignState.StartChapter("ch2_hospital");
// Assert
Assert.AreEqual(0, _campaignState.CurrentChapterPiecesLost, "CurrentChapterPiecesLost debería resetearse al iniciar nuevo capítulo");
}
[Test]
public void RegisterPieceLost_IncrementsTotal()
{
// Arrange
_campaignState.StartChapter("ch1_factory");
int initialTotal = _campaignState.TotalPiecesLost;
// Act
_campaignState.RegisterPieceLost("elena");
_campaignState.RegisterPieceLost("ricardo");
// Assert
Assert.AreEqual(initialTotal + 2, _campaignState.TotalPiecesLost, "TotalPiecesLost debería incrementar con cada pérdida");
}
[Test]
public void RegisterPieceLost_IncrementsCurrentChapter()
{
// Arrange
_campaignState.StartChapter("ch1_factory");
// Act
_campaignState.RegisterPieceLost("elena");
_campaignState.RegisterPieceLost("ricardo");
// Assert
Assert.AreEqual(2, _campaignState.CurrentChapterPiecesLost, "CurrentChapterPiecesLost debería ser 2");
}
[Test]
public void TotalPiecesLost_AccumulatesAcrossChapters()
{
// Arrange & Act
_campaignState.StartChapter("ch1_factory");
_campaignState.RegisterPieceLost("elena");
_campaignState.RegisterPieceLost("ricardo");
_campaignState.StartChapter("ch2_hospital");
_campaignState.RegisterPieceLost("carlos");
// Assert
Assert.AreEqual(3, _campaignState.TotalPiecesLost, "TotalPiecesLost debería acumular pérdidas de todos los capítulos");
Assert.AreEqual(1, _campaignState.CurrentChapterPiecesLost, "CurrentChapterPiecesLost debería ser solo del capítulo actual");
}
#endregion
#region Reset Tests
[Test]
public void Reset_ClearsAllData()
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
_campaignState.RegisterPieceAlive("ricardo");
_campaignState.CompleteChapter("ch1_factory", wasVictory: true);
_campaignState.StartChapter("ch1_factory");
_campaignState.RegisterPieceLost("elena");
// Act
_campaignState.Reset();
// Assert
Assert.IsFalse(_campaignState.IsPieceAlive("elena"), "Elena no debería estar en _alivePieces después de Reset");
Assert.IsFalse(_campaignState.IsPieceAlive("ricardo"), "Ricardo no debería estar en _alivePieces después de Reset");
Assert.IsFalse(_campaignState.IsChapterComplete("ch1_factory"), "Capítulos completados deberían limpiarse");
}
[Test]
public void Reset_ResetsCounters()
{
// Arrange
_campaignState.StartChapter("ch1_factory");
_campaignState.RegisterPieceLost("elena");
_campaignState.CompleteChapter("ch1_factory", wasVictory: true);
// Act
_campaignState.Reset();
// Assert
Assert.AreEqual(0, _campaignState.TotalPiecesLost, "TotalPiecesLost debería resetearse a 0");
Assert.AreEqual(0, _campaignState.CurrentChapterPiecesLost, "CurrentChapterPiecesLost debería resetearse a 0");
Assert.IsFalse(_campaignState.LastChapterWasVictory, "LastChapterWasVictory debería resetearse a false");
}
#endregion
#region SyncFromBoard Tests
[Test]
public void SyncFromBoard_ReadsAllWhitePieces()
{
// Arrange
var board = CreateMockBoard();
AddMockPiece(board, 0, 0, isWhite: true, identity: CreateMockIdentity("elena"));
AddMockPiece(board, 1, 0, isWhite: true, identity: CreateMockIdentity("ricardo"));
// Act
_campaignState.SyncFromBoard(board);
// Assert
Assert.IsTrue(_campaignState.IsPieceAlive("elena"), "Elena debería sincronizarse del tablero");
Assert.IsTrue(_campaignState.IsPieceAlive("ricardo"), "Ricardo debería sincronizarse del tablero");
}
[Test]
public void SyncFromBoard_IgnoresBlackPieces()
{
// Arrange
var board = CreateMockBoard();
AddMockPiece(board, 0, 0, isWhite: false, identity: CreateMockIdentity("black_piece"));
// Act
_campaignState.SyncFromBoard(board);
// Assert
Assert.IsFalse(_campaignState.IsPieceAlive("black_piece"), "Piezas negras no deberían sincronizarse");
}
[Test]
public void SyncFromBoard_IgnoresPiecesWithoutIdentity()
{
// Arrange
var board = CreateMockBoard();
AddMockPiece(board, 0, 0, isWhite: true, identity: null);
// Act
_campaignState.SyncFromBoard(board);
// Assert
var availableNames = _campaignState.GetAvailablePieceNames();
Assert.AreEqual(0, availableNames.Count, "Piezas sin identidad no deberían sincronizarse");
}
[Test]
public void SyncFromBoard_ClearsOldDataBeforeSync()
{
// Arrange
_campaignState.RegisterPieceAlive("old_piece");
Assert.IsTrue(_campaignState.IsPieceAlive("old_piece"), "Precondición: old_piece existe");
var board = CreateMockBoard();
AddMockPiece(board, 0, 0, isWhite: true, identity: CreateMockIdentity("new_piece"));
// Act
_campaignState.SyncFromBoard(board);
// Assert
Assert.IsFalse(_campaignState.IsPieceAlive("old_piece"), "Datos antiguos deberían limpiarse antes de sync");
Assert.IsTrue(_campaignState.IsPieceAlive("new_piece"), "Nuevos datos deberían estar presentes");
}
#endregion
#region Compatibility Methods Tests
[Test]
public void MarkPieceDead_WithPieceIdentity_CallsRegisterPieceLost()
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
var identity = CreateMockIdentity("elena");
// Act
_campaignState.MarkPieceDead(identity);
// Assert
Assert.IsFalse(_campaignState.IsPieceAlive("elena"), "Elena debería estar muerta después de MarkPieceDead");
}
[Test]
public void MarkPieceRecovered_WithPieceIdentity_CallsRegisterPieceAlive()
{
// Arrange
var identity = CreateMockIdentity("elena");
// Act
_campaignState.MarkPieceRecovered(identity);
// Assert
Assert.IsTrue(_campaignState.IsPieceAlive("elena"), "Elena debería estar viva después de MarkPieceRecovered");
}
[Test]
public void GetAvailablePieceNames_ReturnsCorrectList()
{
// Arrange
_campaignState.RegisterPieceAlive("elena");
_campaignState.RegisterPieceAlive("ricardo");
_campaignState.RegisterPieceAlive("carlos");
// Act
var names = _campaignState.GetAvailablePieceNames();
// Assert
Assert.AreEqual(3, names.Count, "Deberían haber 3 piezas vivas");
Assert.Contains("elena", names, "Elena debería estar en la lista");
Assert.Contains("ricardo", names, "Ricardo debería estar en la lista");
Assert.Contains("carlos", names, "Carlos debería estar en la lista");
}
#endregion
#region Helper Methods
private Piece[,] CreateMockBoard()
{
return new Piece[8, 8];
}
private void AddMockPiece(Piece[,] board, int x, int y, bool isWhite, PieceIdentity identity)
{
// Piece es MonoBehaviour: hay que crearlo con AddComponent (un
// 'new MockPiece()' produce un objeto Unity inválido cuyos campos
// no persisten y SyncFromBoard lo vería vacío).
var go = new GameObject($"MockPiece_{x}_{y}");
go.transform.SetParent(_mockRoot.transform);
var piece = go.AddComponent<MockPiece>();
piece.isWhite = isWhite;
piece.identity = identity;
board[x, y] = piece;
}
private PieceIdentity CreateMockIdentity(string characterName)
{
var identity = ScriptableObject.CreateInstance<PieceIdentity>();
identity.characterName = characterName;
return identity;
}
/// <summary>
/// Mock simple de Piece para tests.
/// </summary>
private class MockPiece : Piece
{
public override List<Vector2Int> GetAvailableMoves(Piece[,] board)
{
return new List<Vector2Int>();
}
}
#endregion
}
}