feat: add proper error handling with full API error details

- 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>
This commit is contained in:
Barry Walker
2026-01-13 18:59:16 -05:00
parent 39768fdd45
commit c67781bac5
9 changed files with 462 additions and 33 deletions
+41
View File
@@ -119,6 +119,47 @@ public class TagToolsTests : IDisposable
json.RootElement.GetProperty("error").GetProperty("code").GetString().Should().Be("UPSTREAM_ERROR");
}
[Fact]
public async Task Create_WhenDuplicate_ReturnsErrorWithDetails()
{
// Arrange
var errorBody = """{"name": ["tag with this name already exists."]}""";
_factory.SetupPostWithError("api/tags/", HttpStatusCode.BadRequest, errorBody);
// Act
var result = await TagTools.Create(_factory.Client, "Existing 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");
// Verify error details include status code and response body
var details = json.RootElement.GetProperty("error").GetProperty("details");
details.GetProperty("status_code").GetInt32().Should().Be(400);
details.GetProperty("response_body").GetString().Should().Contain("already exists");
}
[Fact]
public async Task Create_WhenUnauthorized_ReturnsErrorWithDetails()
{
// Arrange
var errorBody = """{"detail": "Authentication credentials were not provided."}""";
_factory.SetupPostWithError("api/tags/", HttpStatusCode.Unauthorized, errorBody);
// Act
var result = await TagTools.Create(_factory.Client, "New 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");
var details = json.RootElement.GetProperty("error").GetProperty("details");
details.GetProperty("status_code").GetInt32().Should().Be(401);
details.GetProperty("response_body").GetString().Should().Contain("Authentication credentials");
}
[Fact]
public async Task Update_WhenSuccessful_ReturnsUpdatedTag()
{