using Microsoft.VisualStudio.TestTools.UnitTesting; using Microsoft.Extensions.Logging; using System.Text.Json; namespace MarketAlly.AIPlugin.Context.Tests { [TestClass] public class ContextSearchPluginTests { private ContextSearchPlugin _searchPlugin = null!; private ContextStoragePlugin _storagePlugin = null!; private string _testDirectory = null!; private ILogger _logger = null!; [TestInitialize] public async Task Setup() { _testDirectory = Path.Combine(Path.GetTempPath(), $"context-search-test-{Guid.NewGuid():N}"); Directory.CreateDirectory(_testDirectory); var loggerFactory = LoggerFactory.Create(builder => builder.AddConsole()); _logger = loggerFactory.CreateLogger(); _searchPlugin = new ContextSearchPlugin(); _storagePlugin = new ContextStoragePlugin(); // Create test data await CreateTestData(); } [TestCleanup] public void Cleanup() { if (Directory.Exists(_testDirectory)) { Directory.Delete(_testDirectory, recursive: true); } } private async Task CreateTestData() { var testEntries = new[] { new { type = "decision", content = "We decided to use React for the frontend", summary = "Frontend framework decision", tags = "react,frontend,decision", priority = "high" }, new { type = "insight", content = "The API performance improved by 50% after optimization", summary = "API performance insight", tags = "api,performance,optimization", priority = "medium" }, new { type = "codechange", content = "Refactored authentication module using JWT tokens", summary = "Authentication refactoring", tags = "auth,jwt,refactor", priority = "high" }, new { type = "conversation", content = "Discussed database migration strategy with the team", summary = "Database migration discussion", tags = "database,migration,team", priority = "low" }, new { type = "milestone", content = "Completed user authentication system implementation", summary = "Auth system milestone", tags = "auth,milestone,complete", priority = "critical" } }; foreach (var entry in testEntries) { var parameters = new Dictionary { ["contextType"] = entry.type, ["content"] = entry.content, ["summary"] = entry.summary, ["tags"] = entry.tags, ["priority"] = entry.priority, ["projectPath"] = _testDirectory }; await _storagePlugin.ExecuteAsync(parameters); await Task.Delay(10); // Small delay to ensure different timestamps } } [TestMethod] public async Task Search_BasicKeyword_ReturnsRelevantResults() { // Arrange var parameters = new Dictionary { ["query"] = "authentication", ["projectPath"] = _testDirectory, ["maxResults"] = 10 }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); Assert.IsTrue(results.Count >= 2); // Should find auth-related entries // Verify that results contain auth-related content var foundAuthEntries = results.Any(r => r.TryGetProperty("Summary", out var summary) && summary.GetString()!.Contains("Auth", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(foundAuthEntries); } [TestMethod] public async Task Search_MultipleKeywords_CombinesResults() { // Arrange var parameters = new Dictionary { ["query"] = "api performance optimization", ["projectPath"] = _testDirectory, ["includeContent"] = true }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); Assert.IsTrue(results.Count >= 1); // Should find the API performance entry var foundApiEntry = results.Any(r => r.TryGetProperty("Summary", out var summary) && summary.GetString()!.Contains("API performance", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(foundApiEntry); } [TestMethod] public async Task Search_ByContextType_FiltersCorrectly() { // Arrange var parameters = new Dictionary { ["query"] = "system", ["contextType"] = "decision", ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); // All results should be of type "decision" foreach (var resultItem in results) { Assert.IsTrue(resultItem.TryGetProperty("Type", out var type)); Assert.AreEqual("decision", type.GetString()); } } [TestMethod] public async Task Search_ByPriority_FiltersCorrectly() { // Arrange var parameters = new Dictionary { ["query"] = "auth", ["priority"] = "high", ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); // All results should be high priority foreach (var resultItem in results) { Assert.IsTrue(resultItem.TryGetProperty("Priority", out var priority)); Assert.AreEqual("high", priority.GetString()); } } [TestMethod] public async Task Search_ByTags_FiltersCorrectly() { // Arrange var parameters = new Dictionary { ["query"] = "system", ["tags"] = "frontend,api", ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); // Results should contain entries with frontend or api tags // Note: May return 0 results if no entries match the tag filter Assert.IsTrue(results.Count >= 0); } [TestMethod] public async Task Search_MaxResults_LimitsOutput() { // Arrange var parameters = new Dictionary { ["query"] = "system", // Should match multiple entries ["maxResults"] = 2, ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); Assert.IsTrue(results.Count <= 2); } [TestMethod] public async Task Search_IncludeContentFalse_ExcludesContent() { // Arrange var parameters = new Dictionary { ["query"] = "authentication", ["includeContent"] = false, ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); // Content should be null or empty for all results foreach (var resultItem in results) { if (resultItem.TryGetProperty("Content", out var content)) { var contentValue = content.GetString(); Assert.IsTrue(string.IsNullOrEmpty(contentValue)); } } } [TestMethod] public async Task Search_NoStorageDirectory_ReturnsEmptyResults() { // Arrange var nonExistentDir = Path.Combine(_testDirectory, "non-existent"); var parameters = new Dictionary { ["query"] = "anything", ["projectPath"] = nonExistentDir }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); Assert.IsTrue(resultData.TryGetProperty("TotalFound", out var totalFound)); var results = resultsArray.EnumerateArray().ToList(); Assert.AreEqual(0, results.Count); Assert.AreEqual(0, totalFound.GetInt32()); } [TestMethod] public async Task Search_EmptyQuery_ReturnsError() { // Arrange var parameters = new Dictionary { ["query"] = "", ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsFalse(result.Success); } [TestMethod] public async Task Search_CaseInsensitive_FindsResults() { // Arrange var parameters = new Dictionary { ["query"] = "REACT FRONTEND", // Uppercase query ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); Assert.IsTrue(results.Count >= 1); // Should find React entry despite case difference } [TestMethod] public async Task Search_RelevanceScoring_OrdersResultsCorrectly() { // Arrange var parameters = new Dictionary { ["query"] = "authentication JWT", ["projectPath"] = _testDirectory, ["includeContent"] = true }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); if (results.Count > 1) { // Verify that results with "JWT" come before those without var firstResult = results[0]; Assert.IsTrue(firstResult.TryGetProperty("Summary", out var summary) || firstResult.TryGetProperty("Content", out var content)); var hasJWT = (firstResult.TryGetProperty("Summary", out var sum) && sum.GetString()!.Contains("JWT", StringComparison.OrdinalIgnoreCase)) || (firstResult.TryGetProperty("Content", out var cont) && cont.GetString()!.Contains("JWT", StringComparison.OrdinalIgnoreCase)); Assert.IsTrue(hasJWT, "Most relevant result should contain JWT"); } } [TestMethod] public async Task Search_WithMatchedTerms_ReturnsMatchInfo() { // Arrange var parameters = new Dictionary { ["query"] = "React frontend framework", ["projectPath"] = _testDirectory }; // Act var result = await _searchPlugin.ExecuteAsync(parameters); // Assert Assert.IsTrue(result.Success); var resultData = JsonSerializer.Deserialize(JsonSerializer.Serialize(result.Data)); Assert.IsTrue(resultData.TryGetProperty("Results", out var resultsArray)); var results = resultsArray.EnumerateArray().ToList(); if (results.Count > 0) { var firstResult = results[0]; Assert.IsTrue(firstResult.TryGetProperty("MatchedTerms", out var matchedTerms)); var terms = matchedTerms.EnumerateArray().Select(t => t.GetString()).ToList(); Assert.IsTrue(terms.Count > 0, "Should have matched terms"); } } } }