using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; using RobotNet10.ScriptEngine; using RobotNet10.ScriptEngine.Enums; using RobotNet10.ScriptEngine.Helpers; using RobotNet10.ScriptEngine.Hubs; using RobotNet10.ScriptEngine.Models; using RobotNet10.ScriptEngine.Shared; using RobotNet10.ScriptEngine.Test.Helpers; using RobotNet10.Shared; namespace RobotNet10.ScriptEngine.Test; [TestFixture] public class ScriptEngineTests { private ScriptEngine _scriptEngine = null!; private Mock _mockResource = null!; private FileManager _fileManager = null!; private VariableManager _variableManager = null!; private TaskManager _taskManager = null!; private MissionManager _missionManager = null!; private Mock> _mockLogger = null!; private Mock _mockConfiguration = null!; private Mock> _mockHubContext = null!; private string _testScriptPath = null!; [SetUp] public void SetUp() { _testScriptPath = Path.Combine(Path.GetTempPath(), "ScriptEngineTest", Guid.NewGuid().ToString()); Directory.CreateDirectory(_testScriptPath); _mockResource = TestHelpers.CreateMockScriptResource(); _mockLogger = new Mock>(); _mockConfiguration = new Mock(); _mockHubContext = TestHelpers.CreateMockHubContext(); var mockConsoleHubContext = TestHelpers.CreateMockConsoleHubContext(); var mockLoggerGlobals = TestHelpers.CreateMockLogger(); var configuration = TestHelpers.CreateMockConfiguration(); var mockScopeFactory = Mock.Of(); _variableManager = new VariableManager(); _fileManager = new FileManager(); _fileManager.RootPath = _testScriptPath; _taskManager = new TaskManager( _variableManager, _mockResource.Object, mockLoggerGlobals.Object, mockScopeFactory, mockConsoleHubContext.Object, configuration); _missionManager = new MissionManager( mockScopeFactory, _variableManager, _mockResource.Object, mockLoggerGlobals.Object, mockConsoleHubContext.Object, configuration); _scriptEngine = new ScriptEngine( _mockResource.Object, _mockConfiguration.Object, _fileManager, _variableManager, _taskManager, _missionManager, _mockLogger.Object, _mockHubContext.Object, mockConsoleHubContext.Object); } [TearDown] public void TearDown() { _scriptEngine?.Dispose(); _taskManager?.Dispose(); _missionManager?.Dispose(); if (Directory.Exists(_testScriptPath)) { Directory.Delete(_testScriptPath, true); } } [Test] public void Constructor_ShouldInitializeToIdleState() { // Assert Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.Idle)); } [Test] public void Build_WithValidScript_ShouldTransitionToReady() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, @" using RobotNet10.Script; public class DummyClass { [Variable] public int TestVar = 42; } "); // Act var result = _scriptEngine.Build(); // Assert Assert.That(result.IsSuccess, Is.True); // Wait a bit for async build to complete Thread.Sleep(500); Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.Ready).Or.EqualTo(ScriptEngineState.Building)); } [Test] public void Build_WithInvalidScript_ShouldTransitionToBuildError() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, "invalid syntax here"); // Act var result = _scriptEngine.Build(); // Assert Assert.That(result.IsSuccess, Is.True); // Build initiation succeeds // Wait a bit for async build to complete Thread.Sleep(500); Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.BuildError).Or.EqualTo(ScriptEngineState.Building)); } [Test] public void Build_WhenNotIdle_ShouldFail() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, "public class DummyClass { }"); _scriptEngine.Build(); Thread.Sleep(500); // Wait for build to complete _scriptEngine.Start(); Thread.Sleep(500); // Wait for start to complete // Act var result = _scriptEngine.Build(); // Assert Assert.That(result.IsSuccess, Is.False); } [Test] public void Start_WhenReady_ShouldTransitionToRunning() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, "public class DummyClass { }"); _scriptEngine.Build(); Thread.Sleep(1000); // Wait for build to complete // Act var result = _scriptEngine.Start(); // Assert Assert.That(result.IsSuccess, Is.True); Thread.Sleep(500); Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.Running).Or.EqualTo(ScriptEngineState.Starting)); } [Test] public void Start_WhenNotReady_ShouldFail() { // Act var result = _scriptEngine.Start(); // Assert Assert.That(result.IsSuccess, Is.False); } [Test] public void Stop_WhenRunning_ShouldTransitionToReady() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, "public class DummyClass { }"); _scriptEngine.Build(); Thread.Sleep(1000); _scriptEngine.Start(); Thread.Sleep(1000); // Act var result = _scriptEngine.Stop(); // Assert Assert.That(result.IsSuccess, Is.True); Thread.Sleep(2000); // Wait for stop to complete Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.Ready).Or.EqualTo(ScriptEngineState.Stopping)); } [Test] public void Reset_ShouldTransitionToIdle() { // Arrange var scriptFile = Path.Combine(_testScriptPath, "test.cs"); File.WriteAllText(scriptFile, "public class DummyClass { }"); _scriptEngine.Build(); Thread.Sleep(1000); // Act var result = _scriptEngine.Reset(); // Assert Assert.That(result.IsSuccess, Is.True); Thread.Sleep(500); Assert.That(_scriptEngine.State, Is.EqualTo(ScriptEngineState.Idle).Or.EqualTo(ScriptEngineState.Resetting)); } [Test] public void Dispose_ShouldCleanupResources() { // Act & Assert Assert.DoesNotThrow(() => _scriptEngine.Dispose()); } }