mirror of
https://github.com/Earth-Genesis-Games/Ajedrez_Purgatorio.git
synced 2026-09-11 09:47:35 +00:00
- 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
463 lines
16 KiB
C#
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
|
|
}
|
|
}
|