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

203 lines
6.7 KiB
C#
Executable File

using MarketAlly.AIPlugin.Analysis.Infrastructure;
using Microsoft.Extensions.Logging;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Threading.Tasks;
namespace MarketAlly.AIPlugin.Analysis.Tests.Infrastructure
{
[TestClass]
public class AnalysisContextTests
{
private AnalysisConfiguration _configuration = null!;
private ILogger? _logger;
[TestInitialize]
public void Setup()
{
_configuration = new AnalysisConfiguration
{
MaxConcurrentAnalyses = 2,
DefaultTimeout = TimeSpan.FromMinutes(5)
};
_logger = null; // In real tests, you might use a mock logger
}
[TestMethod]
public void Constructor_WithValidConfiguration_ShouldInitialize()
{
// Act
using var context = new AnalysisContext(_configuration, _logger);
// Assert
Assert.IsNotNull(context.Configuration);
Assert.AreEqual(_configuration, context.Configuration);
Assert.IsNotNull(context.CancellationToken);
Assert.IsFalse(context.CancellationToken.IsCancellationRequested);
Assert.IsNotNull(context.ConcurrencySemaphore);
}
[TestMethod]
[ExpectedException(typeof(ArgumentNullException))]
public void Constructor_WithNullConfiguration_ShouldThrowArgumentNullException()
{
// Act & Assert
using var context = new AnalysisContext(null!, _logger);
}
[TestMethod]
public void Cancel_ShouldSetCancellationTokenToRequested()
{
// Arrange
using var context = new AnalysisContext(_configuration, _logger);
// Act
context.Cancel();
// Assert
Assert.IsTrue(context.CancellationToken.IsCancellationRequested);
}
[TestMethod]
public async Task AcquireConcurrencySlot_ShouldSucceed()
{
// Arrange
using var context = new AnalysisContext(_configuration, _logger);
// Act & Assert - Should not throw
await context.AcquireConcurrencySlotAsync();
// Cleanup
context.ReleaseConcurrencySlot();
}
[TestMethod]
public async Task ReleaseConcurrencySlot_ShouldSucceed()
{
// Arrange
using var context = new AnalysisContext(_configuration, _logger);
// First acquire a slot
await context.AcquireConcurrencySlotAsync();
// Act & Assert - Should not throw
context.ReleaseConcurrencySlot();
}
[TestMethod]
public async Task ConcurrencySlot_ShouldLimitConcurrentAccess()
{
// Arrange - Use a configuration with max concurrency of 1 for clearer testing
var restrictiveConfig = new AnalysisConfiguration
{
MaxConcurrentAnalyses = 1,
DefaultTimeout = TimeSpan.FromMinutes(5)
};
using var context = new AnalysisContext(restrictiveConfig, _logger);
var task1Started = false;
var task2Started = false;
var task1CanContinue = new TaskCompletionSource<bool>();
// Act
var task1 = Task.Run(async () =>
{
await context.AcquireConcurrencySlotAsync();
task1Started = true;
await task1CanContinue.Task;
context.ReleaseConcurrencySlot();
});
var task2 = Task.Run(async () =>
{
// Small delay to ensure task1 starts first
await Task.Delay(100);
await context.AcquireConcurrencySlotAsync();
task2Started = true;
context.ReleaseConcurrencySlot();
});
// Wait for task1 to start and task2 to be blocked
await Task.Delay(300);
// Assert
Assert.IsTrue(task1Started);
Assert.IsFalse(task2Started); // Should be blocked by semaphore
// Release task1
task1CanContinue.SetResult(true);
await Task.WhenAll(task1, task2);
Assert.IsTrue(task2Started);
}
[TestMethod]
public void AnalysisContext_WithNullLogger_ShouldInitializeProperly()
{
// Arrange & Act
using var context = new AnalysisContext(_configuration, _logger);
// Assert - Logger can be null, that's valid
Assert.AreEqual(_logger, context.Logger); // _logger is null in setup
Assert.IsNotNull(context.Configuration);
Assert.IsNotNull(context.CancellationToken);
Assert.IsNotNull(context.ConcurrencySemaphore);
}
[TestMethod]
public void AnalysisContext_BasicFunctionality_ShouldWork()
{
// Arrange & Act
using var context = new AnalysisContext(_configuration, _logger);
// Assert - Test core functionality without child contexts
Assert.IsNotNull(context.Configuration);
Assert.IsNotNull(context.CancellationToken);
Assert.IsNotNull(context.ConcurrencySemaphore);
Assert.IsFalse(context.CancellationToken.IsCancellationRequested);
// Test cancellation works
context.Cancel();
Assert.IsTrue(context.CancellationToken.IsCancellationRequested);
}
[TestMethod]
public async Task AcquireConcurrencySlotAsync_AfterDispose_ShouldThrowObjectDisposedException()
{
// Arrange
var context = new AnalysisContext(_configuration, _logger);
context.Dispose();
// Act & Assert
await Assert.ThrowsExceptionAsync<ObjectDisposedException>(async () =>
{
await context.AcquireConcurrencySlotAsync();
});
}
[TestMethod]
[ExpectedException(typeof(ObjectDisposedException))]
public void Cancel_AfterDispose_ShouldThrowObjectDisposedException()
{
// Arrange
var context = new AnalysisContext(_configuration, _logger);
context.Dispose();
// Act & Assert
context.Cancel();
}
[TestMethod]
public void Dispose_ShouldNotThrow()
{
// Arrange
var context = new AnalysisContext(_configuration, _logger);
// Act & Assert - Should not throw
context.Dispose();
// Multiple dispose calls should not throw
context.Dispose();
}
}
}