286 lines
9.0 KiB
C#
286 lines
9.0 KiB
C#
using SlaySharp.Core.Catalogs;
|
|
using SlaySharp.Core.Combat;
|
|
using SlaySharp.Core.Enums;
|
|
using SlaySharp.Core.Models;
|
|
using Xunit;
|
|
|
|
namespace SlaySharp.Tests;
|
|
|
|
public class CombatEngineTests
|
|
{
|
|
[Fact]
|
|
public void CombatEngine_InitializesDeckAndDraws5Cards()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3, seed: 42);
|
|
player.MasterDeck.AddRange(StarterDecks.CreateCsharpArchitectDeck());
|
|
|
|
var engine = new CombatEngine();
|
|
var enemy = new NullReferenceExceptionEnemy();
|
|
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
Assert.Equal(CombatPhase.PlayerTurn, engine.Phase);
|
|
Assert.Equal(3, player.CurrentEnergy);
|
|
Assert.Equal(5, player.Hand.Count);
|
|
Assert.Equal(player.MasterDeck.Count - 5, player.DrawPile.Count);
|
|
Assert.Empty(player.DiscardPile);
|
|
}
|
|
|
|
[Fact]
|
|
public void PlayCard_DealsDamage_AndConsumesEnergy()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var card = new ConsoleWriteLineCard(); // Cost 1, dmg 6
|
|
var enemy = new NullReferenceExceptionEnemy(); // 45 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 3;
|
|
|
|
var success = engine.PlayCard(card, enemy);
|
|
|
|
Assert.True(success);
|
|
Assert.Equal(2, player.CurrentEnergy);
|
|
Assert.True(enemy.CurrentHp <= 39);
|
|
Assert.Contains(card, player.DiscardPile);
|
|
}
|
|
|
|
[Fact]
|
|
public void CalculateDamagePreview_AccuratelyCalculatesHpLossAndShieldAbsorbed()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var card = new ThrowExceptionCard(); // 16 dmg
|
|
var enemy = new OffByOneBugEnemy(); // 22 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
enemy.GainShield(6); // Set shield AFTER StartCombat
|
|
|
|
var preview = engine.CalculateDamagePreview(card, enemy);
|
|
|
|
Assert.Equal(16, preview.MinRawDamage);
|
|
Assert.Equal(6, preview.ShieldAbsorbed);
|
|
Assert.Equal(10, preview.MinHpLoss); // 16 - 6 shield = 10 HP loss
|
|
Assert.Equal(12, preview.ProjectedRemainingHp); // 22 - 10 = 12
|
|
Assert.False(preview.IsLethal);
|
|
}
|
|
|
|
[Fact]
|
|
public void CalculateDamagePreview_IdentifiesLethalDamage()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var card = new ThrowExceptionCard(); // 16 dmg
|
|
var enemy = new OffByOneBugEnemy(); // 22 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
enemy.CurrentHp = 14;
|
|
|
|
var preview = engine.CalculateDamagePreview(card, enemy);
|
|
|
|
Assert.Equal(16, preview.MinHpLoss);
|
|
Assert.True(preview.IsLethal);
|
|
Assert.Equal(0, preview.ProjectedRemainingHp);
|
|
}
|
|
|
|
[Fact]
|
|
public void SpanSliceCard_PiercesThroughShield()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var card = new SpanSliceCard(); // 8 piercing dmg
|
|
var enemy = new NullReferenceExceptionEnemy(); // 45 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
enemy.GainShield(10);
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 3;
|
|
|
|
var success = engine.PlayCard(card, enemy);
|
|
|
|
Assert.True(success);
|
|
Assert.Equal(10, enemy.Shield);
|
|
Assert.True(enemy.CurrentHp <= 37);
|
|
}
|
|
|
|
[Fact]
|
|
public void StatusEffects_StrengthAndVulnerable_MultiplyDamage()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var enemy = new NullReferenceExceptionEnemy(); // 45 HP
|
|
var card = new ConsoleWriteLineCard(); // 6 dmg
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
player.Statuses.Add(StatusType.Strength, 2);
|
|
enemy.Statuses.Add(StatusType.Vulnerable, 1);
|
|
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 3;
|
|
|
|
engine.PlayCard(card, enemy);
|
|
|
|
Assert.True(enemy.CurrentHp <= 33);
|
|
}
|
|
|
|
[Fact]
|
|
public void DiscardPile_Reshuffles_WhenDrawPileEmpty()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3, seed: 123);
|
|
player.MasterDeck.Add(new ConsoleWriteLineCard());
|
|
player.MasterDeck.Add(new TryCatchCard());
|
|
player.MasterDeck.Add(new LinqWhereCard());
|
|
|
|
var engine = new CombatEngine();
|
|
var enemy = new OffByOneBugEnemy();
|
|
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
Assert.Equal(3, player.Hand.Count);
|
|
Assert.Empty(player.DrawPile);
|
|
|
|
player.DiscardPile.Add(player.Hand[0]);
|
|
player.DiscardPile.Add(player.Hand[1]);
|
|
player.Hand.RemoveRange(0, 2);
|
|
|
|
player.DrawCards(2, engine.Log);
|
|
|
|
Assert.Equal(3, player.Hand.Count);
|
|
Assert.Empty(player.DiscardPile);
|
|
}
|
|
|
|
[Fact]
|
|
public void KillingAllEnemies_TriggersVictory()
|
|
{
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var card = new ThrowExceptionCard(); // 16 dmg
|
|
var enemy = new OffByOneBugEnemy(); // 22 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
enemy.CurrentHp = 10;
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 2;
|
|
|
|
engine.PlayCard(card, enemy);
|
|
|
|
Assert.Equal(CombatPhase.Victory, engine.Phase);
|
|
Assert.Empty(engine.Enemies);
|
|
Assert.NotEmpty(engine.CardRewards);
|
|
}
|
|
|
|
[Fact]
|
|
public void GameRun_StartNewRun_InitializesCorrectArchetypes()
|
|
{
|
|
var run = new SlaySharp.Core.Run.GameRun();
|
|
|
|
// Principal Architect
|
|
run.StartNewRun(DeveloperArchetype.PrincipalArchitect);
|
|
Assert.Equal("Principal Architect", run.Player.Name);
|
|
Assert.Equal(70, run.Player.MaxHp);
|
|
Assert.Equal(3, run.Player.MaxEnergy);
|
|
Assert.Equal(3, run.Player.Relics.Count);
|
|
|
|
// Async Concurrency Master
|
|
run.StartNewRun(DeveloperArchetype.AsyncConcurrencyMaster);
|
|
Assert.Equal("Async Master", run.Player.Name);
|
|
Assert.Equal(65, run.Player.MaxHp);
|
|
Assert.Equal(4, run.Player.MaxEnergy);
|
|
Assert.Equal(2, run.Player.Relics.Count);
|
|
|
|
// Unsafe Memory Hacker
|
|
run.StartNewRun(DeveloperArchetype.UnsafeMemoryHacker);
|
|
Assert.Equal("Unsafe Hacker", run.Player.Name);
|
|
Assert.Equal(60, run.Player.MaxHp);
|
|
Assert.Equal(3, run.Player.MaxEnergy);
|
|
Assert.Equal(2, run.Player.Relics.Count);
|
|
}
|
|
|
|
[Fact]
|
|
public void TaskWhenAllCard_DealsDamageToTargetEnemy()
|
|
{
|
|
var player = new Player("Async Master", 65, maxEnergy: 4);
|
|
var card = new TaskWhenAllCard(); // 14 dmg single target
|
|
var enemy = new NullReferenceExceptionEnemy(); // 45 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 2;
|
|
|
|
var success = engine.PlayCard(card, enemy);
|
|
|
|
Assert.True(success);
|
|
Assert.Equal(0, player.CurrentEnergy);
|
|
Assert.True(enemy.CurrentHp <= 31);
|
|
}
|
|
|
|
[Fact]
|
|
public void FixedPointerCard_DealsPiercingDamageTwoTimes()
|
|
{
|
|
var player = new Player("Unsafe Hacker", 60, maxEnergy: 3);
|
|
var card = new FixedPointerCard(); // 2 hits of 9 piercing dmg
|
|
var enemy = new MemoryLeakSpectreEnemy(); // 35 HP
|
|
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { enemy });
|
|
|
|
enemy.GainShield(12);
|
|
player.Hand.Clear();
|
|
player.Hand.Add(card);
|
|
player.CurrentEnergy = 2;
|
|
|
|
var success = engine.PlayCard(card, enemy);
|
|
|
|
Assert.True(success);
|
|
Assert.Equal(12, enemy.Shield); // Pierced
|
|
Assert.True(enemy.CurrentHp <= 17);
|
|
}
|
|
|
|
[Fact]
|
|
public void KubernetesBoss_ExecutesRoutines()
|
|
{
|
|
var boss = new KubernetesClusterBossEnemy();
|
|
Assert.Equal(110, boss.MaxHp);
|
|
Assert.Equal(110, boss.CurrentHp);
|
|
Assert.Equal("☸️", boss.Icon);
|
|
|
|
var player = new Player("Senior Dev", 70, maxEnergy: 3);
|
|
var engine = new CombatEngine();
|
|
engine.StartCombat(player, new List<Enemy> { boss });
|
|
|
|
// Verify intent is planned
|
|
Assert.NotNull(boss.CurrentIntent);
|
|
}
|
|
|
|
[Fact]
|
|
public void RunRecord_Create_GeneratesValidGitCommitRecord()
|
|
{
|
|
var record = RunRecord.Create(
|
|
DeveloperArchetype.AsyncConcurrencyMaster,
|
|
true,
|
|
55,
|
|
65,
|
|
120,
|
|
14,
|
|
7
|
|
);
|
|
|
|
Assert.Equal(7, record.CommitHash.Length);
|
|
Assert.True(record.IsVictory);
|
|
Assert.Equal(55, record.FinalHp);
|
|
Assert.Equal(120, record.DevCredits);
|
|
Assert.Contains("feat(prod)", record.Message);
|
|
}
|
|
} |