using MarketAlly.AIPlugin.Analysis.Infrastructure; using Microsoft.Extensions.Logging; using Microsoft.VisualStudio.TestTools.UnitTesting; using System; using System.IO; using System.Threading; using System.Threading.Tasks; namespace MarketAlly.AIPlugin.Analysis.Tests.Infrastructure { [TestClass] public class ErrorHandlingTests { private ILogger? _logger; [TestInitialize] public void Setup() { _logger = null; // In real tests, you might use a mock logger } [TestMethod] public async Task ExecuteWithRetryAsync_SuccessfulOperation_ShouldReturnResult() { // Arrange var expectedResult = "success"; var callCount = 0; // Act var result = await ErrorHandling.ExecuteWithRetryAsync( () => { callCount++; return Task.FromResult(expectedResult); }, maxRetries: 3, logger: _logger ); // Assert Assert.AreEqual(expectedResult, result); Assert.AreEqual(1, callCount); } [TestMethod] public async Task ExecuteWithRetryAsync_TransientFailureThenSuccess_ShouldRetryAndSucceed() { // Arrange var expectedResult = "success"; var callCount = 0; // Act var result = await ErrorHandling.ExecuteWithRetryAsync( () => { callCount++; if (callCount < 3) throw new IOException("Transient failure"); return Task.FromResult(expectedResult); }, maxRetries: 3, delay: TimeSpan.FromMilliseconds(10), logger: _logger ); // Assert Assert.AreEqual(expectedResult, result); Assert.AreEqual(3, callCount); } [TestMethod] [ExpectedException(typeof(AggregateException))] public async Task ExecuteWithRetryAsync_PersistentFailure_ShouldThrowAggregateException() { // Arrange var callCount = 0; // Act & Assert await ErrorHandling.ExecuteWithRetryAsync( () => { callCount++; throw new IOException("Persistent failure"); }, maxRetries: 2, delay: TimeSpan.FromMilliseconds(10), logger: _logger ); } [TestMethod] [ExpectedException(typeof(ArgumentException))] public async Task ExecuteWithRetryAsync_NonRetryableException_ShouldNotRetry() { // Arrange var callCount = 0; // Act & Assert await ErrorHandling.ExecuteWithRetryAsync( () => { callCount++; throw new ArgumentException("Non-retryable failure"); }, maxRetries: 3, delay: TimeSpan.FromMilliseconds(10), logger: _logger ); } [TestMethod] public async Task ExecuteWithRetryAsync_CancellationRequested_ShouldThrowOperationCancelledException() { // Arrange using var cts = new CancellationTokenSource(); cts.Cancel(); // Act & Assert await Assert.ThrowsExceptionAsync(async () => { await ErrorHandling.ExecuteWithRetryAsync( () => Task.FromResult("result"), maxRetries: 3, logger: _logger, cancellationToken: cts.Token ); }); } [TestMethod] public async Task SafeExecuteAsync_SuccessfulOperation_ShouldReturnSuccessResult() { // Arrange var expectedValue = "success"; // Act var result = await ErrorHandling.SafeExecuteAsync( () => Task.FromResult(expectedValue), logger: _logger ); // Assert Assert.IsTrue(result.IsSuccess); Assert.AreEqual(expectedValue, result.Value); Assert.IsNull(result.Exception); Assert.IsNull(result.ErrorMessage); Assert.IsTrue(result.Duration > TimeSpan.Zero); } [TestMethod] public async Task SafeExecuteAsync_FailedOperation_ShouldReturnFailureResult() { // Arrange var expectedException = new InvalidOperationException("Test error"); // Act var result = await ErrorHandling.SafeExecuteAsync( () => throw expectedException, logger: _logger ); // Assert Assert.IsFalse(result.IsSuccess); Assert.IsNull(result.Value); Assert.IsNotNull(result.Exception); Assert.AreEqual(expectedException, result.Exception); Assert.AreEqual("Test error", result.ErrorMessage); Assert.IsTrue(result.Duration > TimeSpan.Zero); } [TestMethod] public async Task WithTimeoutAsync_OperationCompletesInTime_ShouldReturnResult() { // Arrange var expectedResult = "success"; // Act var result = await ErrorHandling.WithTimeoutAsync( async token => { await Task.Delay(50, token); return expectedResult; }, timeout: TimeSpan.FromSeconds(1), logger: _logger ); // Assert Assert.AreEqual(expectedResult, result); } [TestMethod] [ExpectedException(typeof(TimeoutException))] public async Task WithTimeoutAsync_OperationTimesOut_ShouldThrowTimeoutException() { // Act & Assert await ErrorHandling.WithTimeoutAsync( async token => { await Task.Delay(1000, token); return "result"; }, timeout: TimeSpan.FromMilliseconds(100), logger: _logger ); } [TestMethod] public void HandlePluginException_ShouldReturnPluginErrorInfo() { // Arrange var exception = new InvalidOperationException("Plugin error"); var pluginName = "TestPlugin"; var operationName = "ExecuteAsync"; // Act var errorInfo = ErrorHandling.HandlePluginException( exception, pluginName, operationName, _logger ); // Assert Assert.IsNotNull(errorInfo); Assert.AreEqual(pluginName, errorInfo.PluginName); Assert.AreEqual(operationName, errorInfo.OperationName); Assert.AreEqual(exception, errorInfo.Exception); Assert.AreEqual("General", errorInfo.ErrorType); Assert.AreEqual(ErrorSeverity.Medium, errorInfo.Severity); Assert.IsTrue(errorInfo.Recoverable); Assert.IsTrue(errorInfo.Timestamp <= DateTime.UtcNow); } [TestMethod] public void HandlePluginException_IOError_ShouldClassifyCorrectly() { // Arrange var exception = new IOException("File not accessible"); var pluginName = "TestPlugin"; var operationName = "ReadFile"; // Act var errorInfo = ErrorHandling.HandlePluginException( exception, pluginName, operationName, _logger ); // Assert Assert.AreEqual("IO", errorInfo.ErrorType); Assert.AreEqual(ErrorSeverity.Medium, errorInfo.Severity); Assert.IsTrue(errorInfo.Recoverable); } [TestMethod] public void HandlePluginException_OutOfMemoryError_ShouldClassifyAsCritical() { // Arrange var exception = new OutOfMemoryException("Out of memory"); var pluginName = "TestPlugin"; var operationName = "ProcessLargeFile"; // Act var errorInfo = ErrorHandling.HandlePluginException( exception, pluginName, operationName, _logger ); // Assert Assert.AreEqual("Memory", errorInfo.ErrorType); Assert.AreEqual(ErrorSeverity.Critical, errorInfo.Severity); Assert.IsFalse(errorInfo.Recoverable); } [TestMethod] public void HandlePluginException_UnauthorizedAccessError_ShouldClassifyAsHighSeverity() { // Arrange var exception = new UnauthorizedAccessException("Access denied"); var pluginName = "TestPlugin"; var operationName = "AccessSecureResource"; // Act var errorInfo = ErrorHandling.HandlePluginException( exception, pluginName, operationName, _logger ); // Assert Assert.AreEqual("Security", errorInfo.ErrorType); Assert.AreEqual(ErrorSeverity.High, errorInfo.Severity); Assert.IsFalse(errorInfo.Recoverable); } [TestMethod] public async Task ExecuteWithRetryAsync_NonGeneric_ShouldWork() { // Arrange var callCount = 0; // Act await ErrorHandling.ExecuteWithRetryAsync( () => { callCount++; return Task.CompletedTask; }, maxRetries: 3, logger: _logger ); // Assert Assert.AreEqual(1, callCount); } } }