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

406 lines
12 KiB
C#
Executable File

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<ContextSearchPlugin> _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<ContextSearchPlugin>();
_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<string, object>
{
["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<string, object>
{
["query"] = "authentication",
["projectPath"] = _testDirectory,
["maxResults"] = 10
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "api performance optimization",
["projectPath"] = _testDirectory,
["includeContent"] = true
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "system",
["contextType"] = "decision",
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "auth",
["priority"] = "high",
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "system",
["tags"] = "frontend,api",
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["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<JsonElement>(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<string, object>
{
["query"] = "authentication",
["includeContent"] = false,
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "anything",
["projectPath"] = nonExistentDir
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["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<string, object>
{
["query"] = "REACT FRONTEND", // Uppercase query
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "authentication JWT",
["projectPath"] = _testDirectory,
["includeContent"] = true
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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<string, object>
{
["query"] = "React frontend framework",
["projectPath"] = _testDirectory
};
// Act
var result = await _searchPlugin.ExecuteAsync(parameters);
// Assert
Assert.IsTrue(result.Success);
var resultData = JsonSerializer.Deserialize<JsonElement>(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");
}
}
}
}