using Xunit; using Moq; using Moq.Protected; using System.Net; using System.Net.Http; using System.Text.Json; using System.Threading; using System.Threading.Tasks; using OpenHarbor.MCP.Core; using CodexMcpServer.Tools; namespace CodexMcpServer.Tests; /// /// Unit tests for SearchByTagTool following TDD approach. /// Tests integration with CODEX API for tag-based search. /// public class SearchByTagToolTests { [Fact] public void SearchByTagTool_ShouldHaveCorrectName() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); // Act var name = tool.Name; // Assert Assert.Equal("search_by_tag", name); } [Fact] public void SearchByTagTool_ShouldHaveDescription() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); // Act var description = tool.Description; // Assert Assert.NotNull(description); Assert.NotEmpty(description); Assert.Contains("tag", description.ToLower()); } [Fact] public void SearchByTagTool_ShouldHaveValidSchema() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); // Act var schema = tool.Schema; // Assert Assert.NotNull(schema); var root = schema.RootElement; Assert.Equal(JsonValueKind.Object, root.ValueKind); // Schema should define required "tag" parameter Assert.True(root.TryGetProperty("properties", out var properties)); Assert.True(properties.TryGetProperty("tag", out var tagProp)); } [Fact] public async Task SearchByTagTool_ExecuteAsync_WithValidTag_ShouldCallCodexApi() { // Arrange var mockResponse = new { documents = new[] { new { id = "doc1", title = "Document 1", tags = new[] { "test" } } }, total = 1 }; var mockHttpMessageHandler = new Mock(); mockHttpMessageHandler .Protected() .Setup>( "SendAsync", ItExpr.Is(req => req.Method == HttpMethod.Get && req.RequestUri.ToString().Contains("/api/tags/") ), ItExpr.IsAny() ) .ReturnsAsync(new HttpResponseMessage { StatusCode = HttpStatusCode.OK, Content = new StringContent(JsonSerializer.Serialize(mockResponse)) }); var httpClient = new HttpClient(mockHttpMessageHandler.Object) { BaseAddress = new System.Uri("http://localhost:5050") }; var tool = new SearchByTagTool(httpClient); var arguments = JsonDocument.Parse("""{"tag": "test"}"""); // Act var result = await tool.ExecuteAsync(arguments); // Assert Assert.NotNull(result); var root = result.RootElement; Assert.True(root.TryGetProperty("documents", out _)); } [Fact] public async Task SearchByTagTool_ExecuteAsync_WithEmptyTag_ShouldReturnError() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); var arguments = JsonDocument.Parse("""{"tag": ""}"""); // Act var result = await tool.ExecuteAsync(arguments); // Assert Assert.NotNull(result); var root = result.RootElement; Assert.True(root.TryGetProperty("error", out _)); } [Fact] public async Task SearchByTagTool_ExecuteAsync_WithNullArguments_ShouldReturnError() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); // Act var result = await tool.ExecuteAsync(null); // Assert Assert.NotNull(result); var root = result.RootElement; Assert.True(root.TryGetProperty("error", out _)); } [Fact] public async Task SearchByTagTool_ExecuteAsync_WithMissingTagProperty_ShouldReturnError() { // Arrange var httpClient = new HttpClient(); var tool = new SearchByTagTool(httpClient); var arguments = JsonDocument.Parse("""{"other": "value"}"""); // Act var result = await tool.ExecuteAsync(arguments); // Assert Assert.NotNull(result); var root = result.RootElement; Assert.True(root.TryGetProperty("error", out _)); } [Fact] public void SearchByTagTool_ShouldImplementIMcpTool() { // Arrange var httpClient = new HttpClient(); // Act var tool = new SearchByTagTool(httpClient); // Assert Assert.IsAssignableFrom(tool); } }