c67781bac5
- Add ApiResult<T> type to carry success/failure with error details - Add UpdateDocumentWithResultAsync and CreateTagWithResultAsync methods - Update DocumentTools.Update and TagTools.Create to return actual HTTP status codes and response bodies in error responses - Add comprehensive tests for error handling (18 new tests) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
126 lines
3.2 KiB
C#
126 lines
3.2 KiB
C#
using System.Net;
|
|
using FluentAssertions;
|
|
using PaperlessMCP.Client;
|
|
using Xunit;
|
|
|
|
namespace PaperlessMCP.Tests.Client;
|
|
|
|
public class ApiResultTests
|
|
{
|
|
#region Success Tests
|
|
|
|
[Fact]
|
|
public void Success_WithValue_ReturnsSuccessResult()
|
|
{
|
|
// Act
|
|
var result = ApiResult<string>.Success("test value");
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeTrue();
|
|
result.Value.Should().Be("test value");
|
|
result.Error.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Success_ImplicitBoolConversion_ReturnsTrue()
|
|
{
|
|
// Arrange
|
|
var result = ApiResult<int>.Success(42);
|
|
|
|
// Act & Assert
|
|
bool isSuccess = result;
|
|
isSuccess.Should().BeTrue();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Failure Tests
|
|
|
|
[Fact]
|
|
public void Failure_WithStatusCodeAndMessage_ReturnsFailureResult()
|
|
{
|
|
// Act
|
|
var result = ApiResult<string>.Failure(HttpStatusCode.NotFound, "Resource not found");
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeFalse();
|
|
result.Value.Should().BeNull();
|
|
result.Error.Should().NotBeNull();
|
|
result.Error!.StatusCode.Should().Be(HttpStatusCode.NotFound);
|
|
result.Error.Message.Should().Be("Resource not found");
|
|
result.Error.ResponseBody.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void Failure_WithResponseBody_IncludesBody()
|
|
{
|
|
// Arrange
|
|
var responseBody = """{"error": "Document not found"}""";
|
|
|
|
// Act
|
|
var result = ApiResult<string>.Failure(HttpStatusCode.NotFound, "Not found", responseBody);
|
|
|
|
// Assert
|
|
result.Error.Should().NotBeNull();
|
|
result.Error!.ResponseBody.Should().Be(responseBody);
|
|
}
|
|
|
|
[Fact]
|
|
public void Failure_WithApiError_ReturnsFailureResult()
|
|
{
|
|
// Arrange
|
|
var error = new ApiError(HttpStatusCode.BadRequest, "Invalid input", """{"field": "name is required"}""");
|
|
|
|
// Act
|
|
var result = ApiResult<object>.Failure(error);
|
|
|
|
// Assert
|
|
result.IsSuccess.Should().BeFalse();
|
|
result.Error.Should().Be(error);
|
|
}
|
|
|
|
[Fact]
|
|
public void Failure_ImplicitBoolConversion_ReturnsFalse()
|
|
{
|
|
// Arrange
|
|
var result = ApiResult<int>.Failure(HttpStatusCode.InternalServerError, "Server error");
|
|
|
|
// Act & Assert
|
|
bool isSuccess = result;
|
|
isSuccess.Should().BeFalse();
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region ApiError Tests
|
|
|
|
[Fact]
|
|
public void ApiError_ToString_FormatsCorrectly()
|
|
{
|
|
// Arrange
|
|
var error = new ApiError(HttpStatusCode.Forbidden, "Access denied", null);
|
|
|
|
// Act
|
|
var result = error.ToString();
|
|
|
|
// Assert
|
|
result.Should().Be("HTTP 403 Forbidden: Access denied");
|
|
}
|
|
|
|
[Fact]
|
|
public void ApiError_ToString_IncludesResponseBody()
|
|
{
|
|
// Arrange
|
|
var error = new ApiError(HttpStatusCode.BadRequest, "Validation failed", """{"name": ["This field is required."]}""");
|
|
|
|
// Act
|
|
var result = error.ToString();
|
|
|
|
// Assert
|
|
result.Should().Contain("HTTP 400 BadRequest: Validation failed");
|
|
result.Should().Contain("""{"name": ["This field is required."]}""");
|
|
}
|
|
|
|
#endregion
|
|
}
|