/*
┌──────────────────────────────────────────────────────────────────┐
│ Author: Ivan Murzak (https://github.com/IvanMurzak) │
│ Repository: GitHub (https://github.com/IvanMurzak/Unity-MCP) │
│ Copyright (c) 2025 Ivan Murzak │
│ Licensed under the Apache License, Version 2.0. │
│ See the LICENSE file in the project root for more information. │
└──────────────────────────────────────────────────────────────────┘
*/
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using com.IvanMurzak.ReflectorNet;
using com.IvanMurzak.ReflectorNet.Model;
using NUnit.Framework;
using UnityEngine;
using UnityEngine.TestTools;
namespace com.IvanMurzak.Unity.MCP.Editor.Tests.AtomicApi
{
///
/// EditMode coverage for ReflectorNet.Reflector.TryReadAt — the read-side
/// counterpart of TryModifyAt. Each path here mirrors a path used by
/// to validate read↔write symmetry: the
/// value written by TryModifyAt is observed back through TryReadAt.
///
public class TryReadAtTests : BaseTest
{
[UnitySetUp]
public override IEnumerator SetUp() => base.SetUp();
[UnityTearDown]
public override IEnumerator TearDown() => base.TearDown();
// ─── TryReadAt — root field ──────────────────────────────────────────
[UnityTest]
public IEnumerator TryReadAt_RootField_ReadWriteSymmetry()
{
var reflector = UnityMcpPluginEditor.Instance.Reflector
?? throw new Exception("Reflector is not available.");
var system = new StarSystemPoco { globalOrbitSpeedMultiplier = 1f };
object? obj = system;
// Write
var wrote = reflector.TryModifyAt(ref obj, "globalOrbitSpeedMultiplier", 5f);
Assert.IsTrue(wrote);
// Read back the same path
var read = reflector.TryReadAt(obj, "globalOrbitSpeedMultiplier", out var member);
Assert.IsTrue(read);
Assert.IsNotNull(member);
Assert.AreEqual(5f, member!.GetValue(reflector));
yield return null;
}
// ─── TryReadAt — array element ───────────────────────────────────────
[UnityTest]
public IEnumerator TryReadAt_ArrayElement_ReadWriteSymmetry()
{
var reflector = UnityMcpPluginEditor.Instance.Reflector
?? throw new Exception("Reflector is not available.");
var system = new StarSystemPoco
{
celestialBodies = new[]
{
new CelestialBody { orbitRadius = 10f },
new CelestialBody { orbitRadius = 20f },
}
};
object? obj = system;
// Write
var wrote = reflector.TryModifyAt(ref obj, "celestialBodies/[1]/orbitRadius", 999f);
Assert.IsTrue(wrote);
// Read leaf back
var read = reflector.TryReadAt(obj, "celestialBodies/[1]/orbitRadius", out var member);
Assert.IsTrue(read);
Assert.AreEqual(999f, member!.GetValue(reflector));
// And read the parent element to confirm both fields are observable
var readElem = reflector.TryReadAt(obj, "celestialBodies/[1]", out var elem);
Assert.IsTrue(readElem);
Assert.IsNotNull(elem);
StringAssert.Contains("CelestialBody", elem!.typeName);
var radius = elem.fields?.FirstOrDefault(f => f.name == "orbitRadius");
Assert.IsNotNull(radius);
Assert.AreEqual(999f, radius!.GetValue(reflector));
yield return null;
}
// ─── TryReadAt — Dictionary string key ───────────────────────────────
[UnityTest]
public IEnumerator TryReadAt_DictionaryStringKey_ReadWriteSymmetry()
{
var reflector = UnityMcpPluginEditor.Instance.Reflector
?? throw new Exception("Reflector is not available.");
var system = new StarSystemPoco
{
config = new Dictionary { ["timeout"] = 10, ["retries"] = 3 }
};
object? obj = system;
var wrote = reflector.TryModifyAt(ref obj, "config/[timeout]", 60);
Assert.IsTrue(wrote);
var read = reflector.TryReadAt(obj, "config/[timeout]", out var member);
Assert.IsTrue(read);
Assert.AreEqual(60, member!.GetValue(reflector));
yield return null;
}
// ─── TryReadAt — Dictionary integer key ──────────────────────────────
[UnityTest]
public IEnumerator TryReadAt_DictionaryIntKey_ReadWriteSymmetry()
{
var reflector = UnityMcpPluginEditor.Instance.Reflector
?? throw new Exception("Reflector is not available.");
var system = new StarSystemPoco
{
lookup = new Dictionary { [1] = "one", [2] = "two" }
};
object? obj = system;
var wrote = reflector.TryModifyAt(ref obj, "lookup/[2]", "TWO");
Assert.IsTrue(wrote);
var read = reflector.TryReadAt(obj, "lookup/[2]", out var member);
Assert.IsTrue(read);
Assert.AreEqual("TWO", member!.GetValue(reflector));
yield return null;
}
// ─── TryReadAt — invalid path returns false + detailed log ───────────
[UnityTest]
public IEnumerator TryReadAt_InvalidPath_ReturnsFalse_WithLogs()
{
var reflector = UnityMcpPluginEditor.Instance.Reflector
?? throw new Exception("Reflector is not available.");
var system = new StarSystemPoco { globalOrbitSpeedMultiplier = 1f };
object? obj = system;
var logs = new Logs();
var read = reflector.TryReadAt(obj, "doesNotExist", out var member, logs: logs);
Assert.IsFalse(read);
Assert.IsNull(member);
var logsText = logs.ToString();
Debug.Log($"[TryReadAt_InvalidPath] logs:\n{logsText}");
StringAssert.Contains("doesNotExist", logsText);
StringAssert.Contains("not found", logsText);
yield return null;
}
}
}