unity-mcp/ava-worktrees/feature/ava-asset-store-compliance/Tests/EditMode/EdgeCasesTests.cs

367 lines
14 KiB
C#

using System;
using System.Collections.Generic;
using NUnit.Framework;
using UnityEditor;
using MCPForUnity.Editor.Dependencies;
using MCPForUnity.Editor.Setup;
using MCPForUnity.Editor.Installation;
using MCPForUnity.Editor.Dependencies.Models;
using MCPForUnity.Tests.Mocks;
namespace MCPForUnity.Tests
{
[TestFixture]
public class EdgeCasesTests
{
private string _originalSetupState;
private const string SETUP_STATE_KEY = "MCPForUnity.SetupState";
[SetUp]
public void SetUp()
{
_originalSetupState = EditorPrefs.GetString(SETUP_STATE_KEY, "");
EditorPrefs.DeleteKey(SETUP_STATE_KEY);
}
[TearDown]
public void TearDown()
{
if (!string.IsNullOrEmpty(_originalSetupState))
{
EditorPrefs.SetString(SETUP_STATE_KEY, _originalSetupState);
}
else
{
EditorPrefs.DeleteKey(SETUP_STATE_KEY);
}
}
[Test]
public void DependencyManager_NullPlatformDetector_HandlesGracefully()
{
// This test verifies behavior when no platform detector is available
// (though this shouldn't happen in practice)
// We can't easily mock this without changing the DependencyManager,
// but we can verify it handles the current platform correctly
Assert.DoesNotThrow(() => DependencyManager.GetCurrentPlatformDetector(),
"Should handle platform detection gracefully");
}
[Test]
public void DependencyManager_CorruptedDependencyData_HandlesGracefully()
{
// Test handling of corrupted or unexpected dependency data
var result = DependencyManager.CheckAllDependencies();
// Even with potential corruption, should return valid result structure
Assert.IsNotNull(result, "Should return valid result even with potential data issues");
Assert.IsNotNull(result.Dependencies, "Dependencies list should not be null");
Assert.IsNotNull(result.Summary, "Summary should not be null");
Assert.IsNotNull(result.RecommendedActions, "Recommended actions should not be null");
}
[Test]
public void SetupWizard_CorruptedEditorPrefs_CreatesDefaultState()
{
// Test handling of corrupted EditorPrefs data
// Set invalid JSON
EditorPrefs.SetString(SETUP_STATE_KEY, "{ invalid json data }");
// Should create default state without throwing
var state = SetupWizard.GetSetupState();
Assert.IsNotNull(state, "Should create default state for corrupted data");
Assert.IsFalse(state.HasCompletedSetup, "Default state should not be completed");
Assert.IsFalse(state.HasDismissedSetup, "Default state should not be dismissed");
}
[Test]
public void SetupWizard_EmptyEditorPrefs_CreatesDefaultState()
{
// Test handling of empty EditorPrefs
EditorPrefs.SetString(SETUP_STATE_KEY, "");
var state = SetupWizard.GetSetupState();
Assert.IsNotNull(state, "Should create default state for empty data");
Assert.IsFalse(state.HasCompletedSetup, "Default state should not be completed");
}
[Test]
public void SetupWizard_VeryLongVersionString_HandlesCorrectly()
{
// Test handling of unusually long version strings
var longVersion = new string('1', 1000) + ".0.0";
var state = SetupWizard.GetSetupState();
Assert.DoesNotThrow(() => state.ShouldShowSetup(longVersion),
"Should handle long version strings");
Assert.DoesNotThrow(() => state.MarkSetupCompleted(longVersion),
"Should handle long version strings in completion");
}
[Test]
public void SetupWizard_NullVersionString_HandlesCorrectly()
{
// Test handling of null version strings
var state = SetupWizard.GetSetupState();
Assert.DoesNotThrow(() => state.ShouldShowSetup(null),
"Should handle null version strings");
Assert.DoesNotThrow(() => state.MarkSetupCompleted(null),
"Should handle null version strings in completion");
}
[Test]
public void InstallationOrchestrator_NullDependenciesList_HandlesGracefully()
{
// Test handling of null dependencies list
var orchestrator = new InstallationOrchestrator();
Assert.DoesNotThrow(() => orchestrator.StartInstallation(null),
"Should handle null dependencies list gracefully");
}
[Test]
public void InstallationOrchestrator_EmptyDependenciesList_CompletesSuccessfully()
{
// Test handling of empty dependencies list
var orchestrator = new InstallationOrchestrator();
var emptyList = new List<DependencyStatus>();
bool completed = false;
bool success = false;
orchestrator.OnInstallationComplete += (s, m) => { completed = true; success = s; };
orchestrator.StartInstallation(emptyList);
// Wait briefly
System.Threading.Thread.Sleep(200);
Assert.IsTrue(completed, "Empty installation should complete");
Assert.IsTrue(success, "Empty installation should succeed");
}
[Test]
public void InstallationOrchestrator_DependencyWithNullName_HandlesGracefully()
{
// Test handling of dependency with null name
var orchestrator = new InstallationOrchestrator();
var dependencies = new List<DependencyStatus>
{
new DependencyStatus { Name = null, IsRequired = true, IsAvailable = false }
};
bool completed = false;
orchestrator.OnInstallationComplete += (s, m) => completed = true;
Assert.DoesNotThrow(() => orchestrator.StartInstallation(dependencies),
"Should handle dependency with null name");
// Wait briefly
System.Threading.Thread.Sleep(1000);
Assert.IsTrue(completed, "Installation should complete even with null dependency name");
}
[Test]
public void DependencyCheckResult_NullDependenciesList_HandlesGracefully()
{
// Test handling of null dependencies in result
var result = new DependencyCheckResult();
result.Dependencies = null;
Assert.DoesNotThrow(() => result.GenerateSummary(),
"Should handle null dependencies list in summary generation");
Assert.DoesNotThrow(() => result.GetMissingDependencies(),
"Should handle null dependencies list in missing dependencies");
Assert.DoesNotThrow(() => result.GetMissingRequired(),
"Should handle null dependencies list in missing required");
}
[Test]
public void DependencyStatus_ExtremeValues_HandlesCorrectly()
{
// Test handling of extreme values in dependency status
var status = new DependencyStatus();
// Test very long strings
var longString = new string('x', 10000);
Assert.DoesNotThrow(() => status.Name = longString,
"Should handle very long name");
Assert.DoesNotThrow(() => status.Version = longString,
"Should handle very long version");
Assert.DoesNotThrow(() => status.Path = longString,
"Should handle very long path");
Assert.DoesNotThrow(() => status.Details = longString,
"Should handle very long details");
Assert.DoesNotThrow(() => status.ErrorMessage = longString,
"Should handle very long error message");
}
[Test]
public void SetupState_ExtremeAttemptCounts_HandlesCorrectly()
{
// Test handling of extreme attempt counts
var state = new SetupState();
// Test very high attempt count
state.SetupAttempts = int.MaxValue;
Assert.DoesNotThrow(() => state.RecordSetupAttempt(),
"Should handle overflow in setup attempts gracefully");
}
[Test]
public void DependencyManager_ConcurrentAccess_HandlesCorrectly()
{
// Test concurrent access to dependency manager
var tasks = new List<System.Threading.Tasks.Task>();
var exceptions = new List<Exception>();
for (int i = 0; i < 10; i++)
{
tasks.Add(System.Threading.Tasks.Task.Run(() =>
{
try
{
DependencyManager.CheckAllDependencies();
DependencyManager.IsSystemReady();
DependencyManager.GetMissingDependenciesSummary();
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
}));
}
System.Threading.Tasks.Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(10));
Assert.AreEqual(0, exceptions.Count,
$"Concurrent access should not cause exceptions. Exceptions: {string.Join(", ", exceptions)}");
}
[Test]
public void SetupWizard_ConcurrentStateAccess_HandlesCorrectly()
{
// Test concurrent access to setup wizard state
var tasks = new List<System.Threading.Tasks.Task>();
var exceptions = new List<Exception>();
for (int i = 0; i < 10; i++)
{
tasks.Add(System.Threading.Tasks.Task.Run(() =>
{
try
{
var state = SetupWizard.GetSetupState();
state.RecordSetupAttempt();
SetupWizard.SaveSetupState();
}
catch (Exception ex)
{
lock (exceptions)
{
exceptions.Add(ex);
}
}
}));
}
System.Threading.Tasks.Task.WaitAll(tasks.ToArray(), TimeSpan.FromSeconds(10));
Assert.AreEqual(0, exceptions.Count,
$"Concurrent state access should not cause exceptions. Exceptions: {string.Join(", ", exceptions)}");
}
[Test]
public void MockPlatformDetector_EdgeCases_HandlesCorrectly()
{
// Test edge cases with mock platform detector
var mock = new MockPlatformDetector();
// Test with null/empty values
mock.SetPythonAvailable(true, null, "", null);
mock.SetUVAvailable(false, "", null, "");
mock.SetMCPServerAvailable(true, null, "");
Assert.DoesNotThrow(() => mock.DetectPython(),
"Mock should handle null/empty values");
Assert.DoesNotThrow(() => mock.DetectUV(),
"Mock should handle null/empty values");
Assert.DoesNotThrow(() => mock.DetectMCPServer(),
"Mock should handle null/empty values");
}
[Test]
public void InstallationOrchestrator_RapidCancellation_HandlesCorrectly()
{
// Test rapid cancellation of installation
var orchestrator = new InstallationOrchestrator();
var dependencies = new List<DependencyStatus>
{
new DependencyStatus { Name = "Python", IsRequired = true, IsAvailable = false }
};
// Start and immediately cancel
orchestrator.StartInstallation(dependencies);
orchestrator.CancelInstallation();
// Should handle rapid cancellation gracefully
Assert.IsFalse(orchestrator.IsInstalling, "Should not be installing after cancellation");
}
[Test]
public void DependencyManager_InvalidDependencyNames_HandlesCorrectly()
{
// Test handling of invalid dependency names
var invalidNames = new[] { null, "", " ", "invalid-name", "PYTHON", "python123" };
foreach (var name in invalidNames)
{
Assert.DoesNotThrow(() => DependencyManager.IsDependencyAvailable(name),
$"Should handle invalid dependency name: '{name}'");
var result = DependencyManager.IsDependencyAvailable(name);
if (name != "python" && name != "uv" && name != "mcpserver" && name != "mcp-server")
{
Assert.IsFalse(result, $"Invalid dependency name '{name}' should return false");
}
}
}
}
}