using NUnit.Framework;
using UnityEngine;
using EerieVillage.Skills;
using EerieVillage.Skills.Effectors;
namespace EerieVillage.Skills.Tests.Editor
{
///
/// A10 분신 스킬 EditMode 테스트 (BT12-Dev-Clone · 2026-05-15 δ 단계).
///
/// PD 명세 5항목 + PD 결정 4건 정합 검증.
/// PlayMode 의존 (Time.unscaledTime·Update) 영역은 본 EditMode에서 제외 — PD Play 검증 영역.
///
public class CloneSkillTests
{
// ─────────────────────────────────────────────────────────────
// 1. PD 결정 상수 검증 (BaseCooldown 25·MinionLifetime 12·딜레이 0.5·offset 1·alpha 0.5)
// ─────────────────────────────────────────────────────────────
[Test]
public void T01_CloneInstance_PdConstantsMatch()
{
Assert.AreEqual(12f, CloneInstance.LIFETIME_SECONDS,
"PD 결정 (2026-05-15) — 분신 lifetime 12초.");
Assert.AreEqual(0.5f, CloneInstance.FIRE_DELAY_SECONDS,
"PD 명세 — 공격 시작 딜레이 0.5초.");
Assert.AreEqual(1f, CloneInstance.SPAWN_OFFSET_X,
"PD 명세 — facing 반대 방향 1유닛.");
Assert.AreEqual(0.5f, CloneInstance.SPRITE_ALPHA,
"PD 명세 — 반투명 alpha 0.5.");
}
[Test]
public void T02_PlayerSkillInventory_CloneDamageMultiplierIsHalf()
{
// PD 명세 — 분신 공격력 50% 반감 (= 0.5f multiplier)
Assert.AreEqual(0.5f, PlayerSkillInventory.CLONE_DAMAGE_MULTIPLIER,
"PD 명세 4번 — 분신 damage 50% 반감.");
}
// ─────────────────────────────────────────────────────────────
// 2. helper 메서드 동작 검증 (γ 단계 anchor·facing 분기)
// ─────────────────────────────────────────────────────────────
[Test]
public void T03_GetSpawnAnchor_ReturnsPlayerPositionWhenNotCloneFire()
{
var go = new GameObject("PlayerStub");
go.AddComponent();
var inv = go.AddComponent();
go.transform.position = new Vector3(5f, 3f, 0f);
inv.IsCloneFireActive = false;
Vector2 anchor = inv.GetSpawnAnchor();
Assert.AreEqual(5f, anchor.x, "기본 영역 — Player x 위치 반환.");
Assert.AreEqual(3f, anchor.y, "기본 영역 — Player y 위치 반환.");
Object.DestroyImmediate(go);
}
[Test]
public void T04_GetSpawnAnchor_ReturnsCloneOriginWhenCloneFireActive()
{
var go = new GameObject("PlayerStub");
go.AddComponent();
var inv = go.AddComponent();
go.transform.position = new Vector3(5f, 3f, 0f);
inv.IsCloneFireActive = true;
inv.CloneFireOrigin = new Vector2(-2f, 7f);
Vector2 anchor = inv.GetSpawnAnchor();
Assert.AreEqual(-2f, anchor.x, "분신 발동 영역 — 분신 위치 반환.");
Assert.AreEqual(7f, anchor.y, "분신 발동 영역 — 분신 위치 반환.");
Object.DestroyImmediate(go);
}
[Test]
public void T05_GetSpawnFacing_ReturnsCloneFacingWhenCloneFireActive()
{
var go = new GameObject("PlayerStub");
go.AddComponent();
var inv = go.AddComponent();
inv.IsCloneFireActive = true;
inv.CloneFireFacingX = -1f; // 분신 왼쪽 facing 고정
Vector2 facing = inv.GetSpawnFacing(null);
Assert.AreEqual(-1f, facing.x, "분신 facing 고정 (x sign).");
Assert.AreEqual(0f, facing.y, "분신 facing y = 0.");
Object.DestroyImmediate(go);
}
// ─────────────────────────────────────────────────────────────
// 3. CloneInstance Singleton·재귀 skip 검증
// ─────────────────────────────────────────────────────────────
[Test]
public void T06_CloneInstance_CurrentIsNullInitially()
{
// 정적 Singleton 초기 영역 null (또는 이전 테스트 잔존 정리)
if (CloneInstance.Current != null)
Object.DestroyImmediate(CloneInstance.Current.gameObject);
Assert.IsNull(CloneInstance.Current, "분신 미spawn 영역 Current = null.");
}
[Test]
public void T07_EnqueuePlayerFire_IgnoresA10RecursionPrep()
{
// 분신이 A10 발동 hook 영역 무한 재귀 방지 영역 — null·CardId == "A10" 즉시 return
// 본 테스트는 hook 영역 진입 자체가 null safe 영역 확증.
var go = new GameObject("CloneStub");
var instance = go.AddComponent();
// null runtime 영역 즉시 return — Exception X
Assert.DoesNotThrow(() => instance.EnqueuePlayerFire(null),
"null runtime hook 영역 NullReferenceException X.");
Object.DestroyImmediate(go);
}
}
}