using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; using MarketAlly.AIPlugin.Context.Configuration; using MarketAlly.AIPlugin.Context.Security; using System.Text.Json; namespace MarketAlly.AIPlugin.Context.Tests { [TestClass] public class ContextStoragePluginTests { private ContextStoragePlugin _plugin = null!; private string _testDirectory = null!; private ILogger _logger = null!; [TestInitialize] public void Setup() { _testDirectory = Path.Combine(Path.GetTempPath(), $"context-test-{Guid.NewGuid():N}"); Directory.CreateDirectory(_testDirectory); var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); _logger = loggerFactory.CreateLogger(); _plugin = new ContextStoragePlugin(); } [TestCleanup] public void Cleanup() { if (Directory.Exists(_testDirectory)) { Directory.Delete(_testDirectory, recursive: true); } } [TestMethod] public async Task StoreContextEntry_ValidData_ReturnsSuccess() { // Arrange var parameters = new Dictionary { ["contextType"] = "decision", ["content"] = "Test decision content", ["summary"] = "Test summary", ["projectPath"] = _testDirectory, ["priority"] = "high", ["tags"] = "test,unit-test" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success, $"Expected success but got: {result.Message}"); Assert.IsNotNull(result.Data); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Success", out var successProp)); Assert.IsTrue(successProp.GetBoolean()); } [TestMethod] public async Task StoreContextEntry_MissingContent_ReturnsError() { // Arrange var parameters = new Dictionary { ["contextType"] = "decision", ["summary"] = "Test summary", ["projectPath"] = _testDirectory }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsFalse(result.Success); Assert.IsTrue(result.Message.Contains("content") || result.Message.Contains("required")); } [TestMethod] public async Task StoreContextEntry_MultipleEntries_StoresInCorrectOrder() { // Arrange var entries = new[] { new { content = "First entry", summary = "First", timestamp = DateTime.UtcNow.AddMinutes(-2) }, new { content = "Second entry", summary = "Second", timestamp = DateTime.UtcNow.AddMinutes(-1) }, new { content = "Third entry", summary = "Third", timestamp = DateTime.UtcNow } }; // Act foreach (var entry in entries) { var parameters = new Dictionary { ["contextType"] = "test", ["content"] = entry.content, ["summary"] = entry.summary, ["projectPath"] = _testDirectory }; var result = await _plugin.ExecuteAsync(parameters); Assert.IsTrue(result.Success); } // Assert - Check if entries are stored in chronological order (newest first) var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); Assert.IsTrue(contextFiles.Length >= 1); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(3, storedEntries.Count); // Verify order (newest first) Assert.AreEqual("Third", storedEntries[0].Summary); Assert.AreEqual("Second", storedEntries[1].Summary); Assert.AreEqual("First", storedEntries[2].Summary); } [TestMethod] public async Task StoreContextEntry_CaseInsensitiveParameters_Works() { // Arrange var parameters = new Dictionary { ["contexttype"] = "decision", // lowercase ["content"] = "Test content", // lowercase to match SupportedParameters ["summary"] = "Test summary", // lowercase to match SupportedParameters ["projectpath"] = _testDirectory, ["priority"] = "medium" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); } [TestMethod] public async Task StoreContextEntry_CreatesContextDirectory_WhenNotExists() { // Arrange var nonExistentDir = Path.Combine(_testDirectory, "new-project"); var parameters = new Dictionary { ["contextType"] = "test", ["content"] = "Test content", ["summary"] = "Test summary", ["projectPath"] = nonExistentDir }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); Assert.IsTrue(Directory.Exists(Path.Combine(nonExistentDir, ".context"))); } [TestMethod] public async Task StoreContextEntry_UpdatesIndex_Correctly() { // Arrange var parameters = new Dictionary { ["contextType"] = "decision", ["content"] = "Test content for indexing", ["summary"] = "Test summary for index", ["tags"] = "index,test", ["projectPath"] = _testDirectory, ["priority"] = "high" }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var indexPath = Path.Combine(_testDirectory, ".context", "context-index.json"); Assert.IsTrue(File.Exists(indexPath)); var indexContent = await File.ReadAllTextAsync(indexPath); var indexEntries = JsonSerializer.Deserialize>(indexContent); Assert.IsNotNull(indexEntries); Assert.AreEqual(1, indexEntries.Count); Assert.AreEqual("Test summary for index", indexEntries[0].Summary); Assert.AreEqual("decision", indexEntries[0].Type); Assert.AreEqual("high", indexEntries[0].Priority); } [TestMethod] public async Task StoreContextEntry_WithMetadata_StoresCorrectly() { // Arrange var metadata = new { source = "unit-test", version = 1.0, automated = true }; var parameters = new Dictionary { ["contextType"] = "insight", ["content"] = "Content with metadata", ["summary"] = "Summary with metadata", ["projectPath"] = _testDirectory, ["metadata"] = JsonSerializer.Serialize(metadata) }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(1, storedEntries.Count); var entry = storedEntries[0]; Assert.IsTrue(entry.Metadata.ContainsKey("source")); Assert.AreEqual("unit-test", entry.Metadata["source"].ToString()); } [TestMethod] public async Task StoreContextEntry_EmptyTags_HandledCorrectly() { // Arrange var parameters = new Dictionary { ["contextType"] = "test", ["content"] = "Content without tags", ["summary"] = "Summary without tags", ["projectPath"] = _testDirectory, ["tags"] = "" // Empty tags }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(1, storedEntries.Count); // Empty tags string should result in empty list after filtering Assert.AreEqual(0, storedEntries[0].Tags.Count); } [TestMethod] public async Task StoreContextEntry_DefaultValues_AppliedCorrectly() { // Arrange var parameters = new Dictionary { ["content"] = "Minimal content", ["summary"] = "Minimal summary", ["projectPath"] = _testDirectory }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(1, storedEntries.Count); var entry = storedEntries[0]; Assert.AreEqual("conversation", entry.Type); // Default contextType Assert.AreEqual("medium", entry.Priority); // Default priority Assert.AreEqual(_testDirectory, entry.ProjectPath); // Should use provided path } [TestMethod] public async Task StoreContextEntry_LargeContent_HandledCorrectly() { // Arrange var largeContent = new string('x', 50000); // 50KB content var parameters = new Dictionary { ["contextType"] = "large-test", ["content"] = largeContent, ["summary"] = "Large content test", ["projectPath"] = _testDirectory }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(1, storedEntries.Count); Assert.AreEqual(largeContent, storedEntries[0].Content); } [TestMethod] public async Task StoreContextEntry_SpecialCharacters_EncodedCorrectly() { // Arrange var specialContent = "Content with special chars: <>&\"'🚀\n\r\t"; var parameters = new Dictionary { ["contextType"] = "encoding-test", ["content"] = specialContent, ["summary"] = "Special characters test", ["projectPath"] = _testDirectory }; // Act var result = await _plugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var contextFiles = Directory.GetFiles(Path.Combine(_testDirectory, ".context"), "context-*.json"); var fileContent = await File.ReadAllTextAsync(contextFiles[0]); var storedEntries = JsonSerializer.Deserialize>(fileContent); Assert.IsNotNull(storedEntries); Assert.AreEqual(1, storedEntries.Count); Assert.AreEqual(specialContent, storedEntries[0].Content); } } }