svrnty-mcp-server/tests/CodexMcpServer.Tests/ListDocumentsToolTests.cs
Svrnty 0c27de4162 refactor: rename OpenHarbor.MCP to Svrnty.MCP across all libraries
- Renamed all directories: OpenHarbor.MCP.* → Svrnty.MCP.*
- Updated all namespaces in 179 C# files
- Renamed 20 .csproj files and 3 .sln files
- Updated 193 documentation references
- Updated 33 references in main CODEX codebase
- Updated Codex.sln with new paths
- Build verified: 0 errors

Preparing for extraction to standalone repositories.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 21:04:17 -04:00

353 lines
11 KiB
C#

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 Svrnty.MCP.Core;
using CodexMcpServer.Tools;
namespace CodexMcpServer.Tests;
/// <summary>
/// Unit tests for ListDocumentsTool following TDD approach.
/// Tests integration with CODEX API /api/documents endpoint with pagination.
/// </summary>
public class ListDocumentsToolTests
{
[Fact]
public void ListDocumentsTool_ShouldHaveCorrectName()
{
// Arrange
var httpClient = new HttpClient();
var tool = new ListDocumentsTool(httpClient);
// Act
var name = tool.Name;
// Assert
Assert.Equal("list_documents", name);
}
[Fact]
public void ListDocumentsTool_ShouldHaveDescription()
{
// Arrange
var httpClient = new HttpClient();
var tool = new ListDocumentsTool(httpClient);
// Act
var description = tool.Description;
// Assert
Assert.NotNull(description);
Assert.NotEmpty(description);
Assert.Contains("list", description.ToLower());
}
[Fact]
public void ListDocumentsTool_ShouldHaveValidSchema()
{
// Arrange
var httpClient = new HttpClient();
var tool = new ListDocumentsTool(httpClient);
// Act
var schema = tool.Schema;
// Assert
Assert.NotNull(schema);
var root = schema.RootElement;
Assert.Equal(JsonValueKind.Object, root.ValueKind);
// Schema should have optional pagination properties
Assert.True(root.TryGetProperty("properties", out var properties));
// All properties should be optional (no required array or empty required)
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithNoArguments_ShouldCallCodexApi()
{
// Arrange
var mockResponse = new
{
documents = new[]
{
new { id = "doc1", title = "Document 1" },
new { id = "doc2", title = "Document 2" }
},
total = 2,
page = 1,
pageSize = 20
};
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.Is<HttpRequestMessage>(req =>
req.Method == HttpMethod.Get &&
req.RequestUri.ToString().Contains("/api/documents")
),
ItExpr.IsAny<CancellationToken>()
)
.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 ListDocumentsTool(httpClient);
// Act
var result = await tool.ExecuteAsync(null);
// Assert
Assert.NotNull(result);
var root = result.RootElement;
Assert.True(root.TryGetProperty("documents", out _));
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithPaginationParams_ShouldIncludeInRequest()
{
// Arrange
string capturedUri = null;
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync((HttpRequestMessage req, CancellationToken ct) =>
{
capturedUri = req.RequestUri?.ToString();
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("""{"documents": [], "total": 0}""")
};
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(httpClient);
var arguments = JsonDocument.Parse("""{"page": 2, "pageSize": 10}""");
// Act
await tool.ExecuteAsync(arguments);
// Assert
Assert.NotNull(capturedUri);
Assert.Contains("/api/documents", capturedUri);
Assert.Contains("page=2", capturedUri);
Assert.Contains("pageSize=10", capturedUri);
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithHttpError_ShouldReturnErrorResponse()
{
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.InternalServerError,
Content = new StringContent("Internal server error")
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(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 ListDocumentsTool_ExecuteAsync_WithInvalidJson_ShouldReturnError()
{
// Arrange
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync(new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("invalid json{{{")
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(httpClient);
// Act
var result = await tool.ExecuteAsync(null);
// Assert
Assert.NotNull(result);
var root = result.RootElement;
Assert.True(root.TryGetProperty("error", out _));
}
[Fact]
public void ListDocumentsTool_ShouldImplementIMcpTool()
{
// Arrange
var httpClient = new HttpClient();
// Act
var tool = new ListDocumentsTool(httpClient);
// Assert
Assert.IsAssignableFrom<IMcpTool>(tool);
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithLimit_ShouldIncludeInRequest()
{
// Arrange
string capturedUri = null;
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync((HttpRequestMessage req, CancellationToken ct) =>
{
capturedUri = req.RequestUri?.ToString();
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("""{"documents": [], "total": 0}""")
};
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(httpClient);
var arguments = JsonDocument.Parse("""{"limit": 5}""");
// Act
await tool.ExecuteAsync(arguments);
// Assert
Assert.NotNull(capturedUri);
Assert.Contains("limit=5", capturedUri);
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithOffset_ShouldIncludeInRequest()
{
// Arrange
string capturedUri = null;
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync((HttpRequestMessage req, CancellationToken ct) =>
{
capturedUri = req.RequestUri?.ToString();
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("""{"documents": [], "total": 0}""")
};
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(httpClient);
var arguments = JsonDocument.Parse("""{"offset": 10}""");
// Act
await tool.ExecuteAsync(arguments);
// Assert
Assert.NotNull(capturedUri);
Assert.Contains("offset=10", capturedUri);
}
[Fact]
public async Task ListDocumentsTool_ExecuteAsync_WithMultipleParams_ShouldIncludeAllInRequest()
{
// Arrange
string capturedUri = null;
var mockHttpMessageHandler = new Mock<HttpMessageHandler>();
mockHttpMessageHandler
.Protected()
.Setup<Task<HttpResponseMessage>>(
"SendAsync",
ItExpr.IsAny<HttpRequestMessage>(),
ItExpr.IsAny<CancellationToken>()
)
.ReturnsAsync((HttpRequestMessage req, CancellationToken ct) =>
{
capturedUri = req.RequestUri?.ToString();
return new HttpResponseMessage
{
StatusCode = HttpStatusCode.OK,
Content = new StringContent("""{"documents": [], "total": 0}""")
};
});
var httpClient = new HttpClient(mockHttpMessageHandler.Object)
{
BaseAddress = new System.Uri("http://localhost:5050")
};
var tool = new ListDocumentsTool(httpClient);
var arguments = JsonDocument.Parse("""{"page": 3, "pageSize": 15, "limit": 100, "offset": 30}""");
// Act
await tool.ExecuteAsync(arguments);
// Assert
Assert.NotNull(capturedUri);
Assert.Contains("page=3", capturedUri);
Assert.Contains("pageSize=15", capturedUri);
Assert.Contains("limit=100", capturedUri);
Assert.Contains("offset=30", capturedUri);
}
}