MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Analysis/Tests/Infrastructure/ErrorHandlingTests.cs

322 lines
10 KiB
C#
Executable File

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<OperationCanceledException>(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<string>(
() => 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);
}
}
}