using System.Net; using System.Text.Json; using FluentAssertions; using PaperlessMCP.Tests.Fixtures; using RichardSzalay.MockHttp; using PaperlessMCP.Tools; using Xunit; namespace PaperlessMCP.Tests.Tools; public class TagToolsTests : IDisposable { private readonly MockHttpClientFactory _factory; public TagToolsTests() { _factory = new MockHttpClientFactory(); } public void Dispose() { _factory.Dispose(); } [Fact] public async Task List_ReturnsTagList() { // Arrange _factory.MockHandler .When(HttpMethod.Get, "https://paperless.example.com/api/tags/*") .Respond("application/json", TestFixtures.Tags.CreateTagListJson(5)); // Act var result = await TagTools.List(_factory.Client); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetArrayLength().Should().Be(5); json.RootElement.GetProperty("meta").GetProperty("total").GetInt32().Should().Be(5); } [Fact] public async Task List_WithPagination_IncludesMetadata() { // Arrange _factory.MockHandler .When(HttpMethod.Get, "https://paperless.example.com/api/tags/*") .Respond("application/json", TestFixtures.Tags.CreateTagListJson(10)); // Act var result = await TagTools.List(_factory.Client, page: 2, pageSize: 5); // Assert var json = JsonDocument.Parse(result); var meta = json.RootElement.GetProperty("meta"); meta.GetProperty("page").GetInt32().Should().Be(2); meta.GetProperty("page_size").GetInt32().Should().Be(5); } [Fact] public async Task Get_WhenTagExists_ReturnsTag() { // Arrange _factory.SetupGet("api/tags/1/", TestFixtures.Tags.CreateTagJson(1, "Important")); // Act var result = await TagTools.Get(_factory.Client, 1); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("id").GetInt32().Should().Be(1); json.RootElement.GetProperty("result").GetProperty("name").GetString().Should().Be("Important"); } [Fact] public async Task Get_WhenTagNotFound_ReturnsError() { // Arrange _factory.SetupGetWithStatus("api/tags/999/", HttpStatusCode.NotFound); // Act var result = await TagTools.Get(_factory.Client, 999); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeFalse(); json.RootElement.GetProperty("error").GetProperty("code").GetString().Should().Be("NOT_FOUND"); } [Fact] public async Task Create_WhenSuccessful_ReturnsCreatedTag() { // Arrange _factory.SetupPost("api/tags/", TestFixtures.Tags.CreateTagJson(5, "New Tag")); // Act var result = await TagTools.Create(_factory.Client, "New Tag", color: "#00ff00"); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("name").GetString().Should().Be("New Tag"); } [Fact] public async Task Create_WhenFails_ReturnsError() { // Arrange _factory.SetupPostWithStatus("api/tags/", HttpStatusCode.BadRequest); // Act var result = await TagTools.Create(_factory.Client, "Bad Tag"); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeFalse(); json.RootElement.GetProperty("error").GetProperty("code").GetString().Should().Be("UPSTREAM_ERROR"); } [Fact] public async Task Update_WhenSuccessful_ReturnsUpdatedTag() { // Arrange _factory.SetupPatch("api/tags/1/", TestFixtures.Tags.CreateTagJson(1, "Updated Name")); // Act var result = await TagTools.Update(_factory.Client, 1, name: "Updated Name"); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("name").GetString().Should().Be("Updated Name"); } [Fact] public async Task Delete_WithoutConfirmation_ReturnsDryRun() { // Arrange _factory.SetupGet("api/tags/1/", TestFixtures.Tags.CreateTagJson(1, "Tag to Delete")); // Act var result = await TagTools.Delete(_factory.Client, 1, confirm: false); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeFalse(); json.RootElement.GetProperty("error").GetProperty("code").GetString().Should().Be("CONFIRMATION_REQUIRED"); } [Fact] public async Task Delete_WithConfirmation_DeletesTag() { // Arrange _factory.SetupDelete("api/tags/1/", HttpStatusCode.NoContent); // Act var result = await TagTools.Delete(_factory.Client, 1, confirm: true); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("deleted").GetBoolean().Should().BeTrue(); } [Fact] public async Task BulkDelete_WithDryRun_ReturnsPreview() { // Act var result = await TagTools.BulkDelete(_factory.Client, "1,2,3", dryRun: true); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("executed").GetBoolean().Should().BeFalse(); json.RootElement.GetProperty("result").GetProperty("affected_ids").GetArrayLength().Should().Be(3); } [Fact] public async Task BulkDelete_WithConfirmation_ExecutesDeletion() { // Arrange _factory.SetupPost("api/bulk_edit_objects/", "{}"); // Act var result = await TagTools.BulkDelete(_factory.Client, "1,2,3", dryRun: false, confirm: true); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeTrue(); json.RootElement.GetProperty("result").GetProperty("executed").GetBoolean().Should().BeTrue(); } [Fact] public async Task BulkDelete_WithEmptyIds_ReturnsValidationError() { // Act var result = await TagTools.BulkDelete(_factory.Client, "", dryRun: false, confirm: true); // Assert var json = JsonDocument.Parse(result); json.RootElement.GetProperty("ok").GetBoolean().Should().BeFalse(); json.RootElement.GetProperty("error").GetProperty("code").GetString().Should().Be("VALIDATION"); } }