MarketAlly.AIPlugin.Extensions/MarketAlly.AIPlugin.Context/Tests/ContextStoragePluginTests.cs

344 lines
10 KiB
C#
Executable File

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<ContextStoragePlugin> _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<ContextStoragePlugin>();
_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<string, object>
{
["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<JsonElement>(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<string, object>
{
["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<string, object>
{
["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<List<StoredContextEntry>>(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<string, object>
{
["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<string, object>
{
["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<string, object>
{
["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<List<ContextIndexEntry>>(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<string, object>
{
["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<List<StoredContextEntry>>(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<string, object>
{
["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<List<StoredContextEntry>>(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<string, object>
{
["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<List<StoredContextEntry>>(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<string, object>
{
["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<List<StoredContextEntry>>(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<string, object>
{
["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<List<StoredContextEntry>>(fileContent);
Assert.IsNotNull(storedEntries);
Assert.AreEqual(1, storedEntries.Count);
Assert.AreEqual(specialContent, storedEntries[0].Content);
}
}
}