From c538a3eb8c5de6406534ce3d457a0ef20e20cddc Mon Sep 17 00:00:00 2001 From: Stevan Freeborn <65925598+StevanFreeborn@users.noreply.github.com> Date: Sat, 6 Jul 2024 11:55:47 -0500 Subject: [PATCH] fix: throw exception when accessing result props incorrectly --- src/AnthropicClient/Models/AnthropicResult.cs | 37 ++++++++++++++++++- src/AnthropicClient/Models/ToolCallResult.cs | 37 ++++++++++++++++++- .../Examples/Examples.cs | 2 +- .../Integration/AnthropicApiClientTests.cs | 5 --- .../Unit/Models/AnthropicResultTests.cs | 10 +++++ .../Unit/Models/ToolCallResultTests.cs | 34 +++++++++++++++++ .../Unit/Models/ToolCallTests.cs | 15 -------- .../Unit/Models/ToolTests.cs | 2 +- 8 files changed, 116 insertions(+), 26 deletions(-) create mode 100644 tests/AnthropicClient.Tests/Unit/Models/ToolCallResultTests.cs diff --git a/src/AnthropicClient/Models/AnthropicResult.cs b/src/AnthropicClient/Models/AnthropicResult.cs index ddadaeb..dbde966 100644 --- a/src/AnthropicClient/Models/AnthropicResult.cs +++ b/src/AnthropicClient/Models/AnthropicResult.cs @@ -6,21 +6,54 @@ using AnthropicClient.Models; /// The type of the result value. public class AnthropicResult { + private T _value = default!; + /// /// The value of the result. /// - public T Value { get; } + /// Thrown when the result is not successful. + public T Value + { + get + { + return IsSuccess ? _value : throw new InvalidOperationException("The result is not successful. Check the error property for more information."); + } + + private set + { + _value = value; + } + } + + private AnthropicError _error = default!; /// /// The error of the result. /// - public AnthropicError Error { get; } + /// Thrown when the result is successful. + public AnthropicError Error + { + get + { + return IsSuccess ? throw new InvalidOperationException("The result is successful. Check the value property for more information.") : _error; + } + + private set + { + _error = value; + } + } /// /// Indicates whether the operation was successful. /// public bool IsSuccess { get; } + /// + /// Indicates whether the operation failed. + /// + public bool IsFailure => !IsSuccess; + /// /// The request ID of the operation. /// diff --git a/src/AnthropicClient/Models/ToolCallResult.cs b/src/AnthropicClient/Models/ToolCallResult.cs index d0ea5a5..38df223 100644 --- a/src/AnthropicClient/Models/ToolCallResult.cs +++ b/src/AnthropicClient/Models/ToolCallResult.cs @@ -5,21 +5,54 @@ namespace AnthropicClient.Models; /// public class ToolCallResult { + private T? _value = default!; + /// /// The value of the tool call result. Can be null if the call failed, the call was successful but the return type is void or Task, or the call was successful but the return value is null /// - public T? Value { get; } + /// Thrown when the result is not successful. + public T? Value + { + get + { + return IsSuccess ? _value : throw new InvalidOperationException("The result is not successful. Check the error property for more information."); + } + + private set + { + _value = value; + } + } + + private Exception _error = default!; /// /// The error of the tool call result. /// - public Exception Error { get; } + /// Thrown when the result is successful. + public Exception Error + { + get + { + return IsSuccess ? throw new InvalidOperationException("The result is successful. Check the value property for more information.") : _error; + } + + private set + { + _error = value; + } + } /// /// Indicates whether the tool call was successful. /// public bool IsSuccess { get; } + /// + /// Indicates whether the tool call failed. + /// + public bool IsFailure => !IsSuccess; + /// /// Initializes a new instance of the class. /// diff --git a/tests/AnthropicClient.Tests/Examples/Examples.cs b/tests/AnthropicClient.Tests/Examples/Examples.cs index ae2a82a..104f0d7 100644 --- a/tests/AnthropicClient.Tests/Examples/Examples.cs +++ b/tests/AnthropicClient.Tests/Examples/Examples.cs @@ -154,7 +154,7 @@ public class Examples(ConfigurationFixture config, ITestOutputHelper console) : "Get Weather", "Get the weather for a location in the specified units", typeof(GetWeatherTool), - nameof(GetWeatherTool.GetWeather) + nameof(GetWeatherTool.GetWeatherStatically) ); var response = await _client.CreateMessageAsync(new MessageRequest( diff --git a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs index 93bf560..644e923 100644 --- a/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs +++ b/tests/AnthropicClient.Tests/Integration/AnthropicApiClientTests.cs @@ -69,7 +69,6 @@ public class AnthropicApiClientTests : IntegrationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().BeOfType(); - result.Error.Should().BeNull(); var message = result.Value; message.Id.Should().Be("msg_013Zva2CMHLNnXjNJJKqJ2EF"); @@ -135,7 +134,6 @@ public class AnthropicApiClientTests : IntegrationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().BeOfType(); - result.Error.Should().BeNull(); var message = result.Value; message.Id.Should().Be("msg_01D7FLrfh4GYq7yT1ULFeyMV"); @@ -163,7 +161,6 @@ public class AnthropicApiClientTests : IntegrationTest var toolCallResult = await message.ToolCall!.InvokeAsync(); toolCallResult.IsSuccess.Should().BeTrue(); toolCallResult.Value!.ToString().Should().Be("^GSPC"); - toolCallResult.Error.Should().BeNull(); } @@ -208,7 +205,6 @@ public class AnthropicApiClientTests : IntegrationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().BeOfType(); - result.Error.Should().BeNull(); var message = result.Value; message.Id.Should().Be("msg_01D7FLrfh4GYq7yT1ULFeyMV"); @@ -323,7 +319,6 @@ public class AnthropicApiClientTests : IntegrationTest var toolCallResult = await toolCall!.InvokeAsync(); toolCallResult.IsSuccess.Should().BeTrue(); - toolCallResult.Error.Should().BeNull(); toolCallResult.Value.Should().Be(getWeather("San Francisco, CA","fahrenheit")); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/AnthropicResultTests.cs b/tests/AnthropicClient.Tests/Unit/Models/AnthropicResultTests.cs index 7d4264b..3e24801 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/AnthropicResultTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/AnthropicResultTests.cs @@ -11,7 +11,12 @@ public class AnthropicResultTests var actual = AnthropicResult.Success(value, headers); actual.IsSuccess.Should().BeTrue(); + actual.IsFailure.Should().BeFalse(); actual.Value.Should().Be(value); + + var action = () => { var _ = actual.Error; }; + action.Should().Throw(); + actual.Headers.Should().Be(headers); } @@ -24,7 +29,12 @@ public class AnthropicResultTests var actual = AnthropicResult.Failure(error, headers); actual.IsSuccess.Should().BeFalse(); + actual.IsFailure.Should().BeTrue(); actual.Error.Should().Be(error); + + var action = () => { var _ = actual.Value; }; + action.Should().Throw(); + actual.Headers.Should().Be(headers); } } \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/ToolCallResultTests.cs b/tests/AnthropicClient.Tests/Unit/Models/ToolCallResultTests.cs new file mode 100644 index 0000000..6a97018 --- /dev/null +++ b/tests/AnthropicClient.Tests/Unit/Models/ToolCallResultTests.cs @@ -0,0 +1,34 @@ +namespace AnthropicClient.Tests.Unit.Models; + +public class ToolCallResultTests +{ + [Fact] + public void Success_WhenCalled_ItShouldReturnSuccessResult() + { + var value = "success"; + + var actual = ToolCallResult.Success(value); + + actual.IsSuccess.Should().BeTrue(); + actual.IsFailure.Should().BeFalse(); + actual.Value.Should().Be(value); + + var action = () => { var _ = actual.Error; }; + action.Should().Throw(); + } + + [Fact] + public void Failure_WhenCalled_ItShouldReturnFailureResult() + { + var error = new Exception(); + + var actual = ToolCallResult.Failure(error); + + actual.IsSuccess.Should().BeFalse(); + actual.IsFailure.Should().BeTrue(); + actual.Error.Should().Be(error); + + var action = () => { var _ = actual.Value; }; + action.Should().Throw(); + } +} \ No newline at end of file diff --git a/tests/AnthropicClient.Tests/Unit/Models/ToolCallTests.cs b/tests/AnthropicClient.Tests/Unit/Models/ToolCallTests.cs index 4c8400c..97240a4 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ToolCallTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/ToolCallTests.cs @@ -16,7 +16,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("Hello, World!"); - result.Error.Should().BeNull(); } [Fact] @@ -30,7 +29,6 @@ public class ToolCallTests : SerializationTest var result = await toolCall.InvokeAsync(); result.IsSuccess.Should().BeFalse(); - result.Value.Should().BeNull(); result.Error.Should().NotBeNull(); } @@ -46,7 +44,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("Hello, World!"); - result.Error.Should().BeNull(); } [Fact] @@ -60,7 +57,6 @@ public class ToolCallTests : SerializationTest var result = await toolCall.InvokeAsync(); result.IsSuccess.Should().BeFalse(); - result.Value.Should().BeNull(); result.Error.Should().NotBeNull(); } @@ -78,7 +74,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("42"); - result.Error.Should().BeNull(); } [Fact] @@ -94,7 +89,6 @@ public class ToolCallTests : SerializationTest var result = await toolCall.InvokeAsync(); result.IsSuccess.Should().BeFalse(); - result.Value.Should().BeNull(); result.Error.Should().NotBeNull(); } @@ -112,7 +106,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("42"); - result.Error.Should().BeNull(); } [Fact] @@ -128,7 +121,6 @@ public class ToolCallTests : SerializationTest var result = await toolCall.InvokeAsync(); result.IsSuccess.Should().BeFalse(); - result.Value.Should().BeNull(); result.Error.Should().NotBeNull(); } @@ -146,7 +138,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("42"); - result.Error.Should().BeNull(); } [Fact] @@ -162,7 +153,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("42"); - result.Error.Should().BeNull(); } [Fact] @@ -177,7 +167,6 @@ public class ToolCallTests : SerializationTest var result = await toolCall.InvokeAsync(); result.IsSuccess.Should().BeFalse(); - result.Value.Should().BeNull(); result.Error.Should().NotBeNull(); } @@ -195,7 +184,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("Monday"); - result.Error.Should().BeNull(); } [Fact] @@ -219,7 +207,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().Be("John"); - result.Error.Should().BeNull(); } [Fact] @@ -236,7 +223,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().BeNull(); - result.Error.Should().BeNull(); } [Fact] @@ -253,7 +239,6 @@ public class ToolCallTests : SerializationTest result.IsSuccess.Should().BeTrue(); result.Value.Should().BeNull(); - result.Error.Should().BeNull(); } } diff --git a/tests/AnthropicClient.Tests/Unit/Models/ToolTests.cs b/tests/AnthropicClient.Tests/Unit/Models/ToolTests.cs index 6f7240d..a95bdc5 100644 --- a/tests/AnthropicClient.Tests/Unit/Models/ToolTests.cs +++ b/tests/AnthropicClient.Tests/Unit/Models/ToolTests.cs @@ -301,7 +301,7 @@ public class ToolTests : SerializationTest tool.DisplayName.Should().Be("Name"); tool.Description.Should().Be("Description"); tool.Function.Method.Name.Should().Be(nameof(ProperTool.GetWeather)); - tool.Function.Instance.Should().BeNull(); + tool.Function.Instance.Should().BeOfType(); tool.InputSchema.Should().BeEquivalentTo( expectedSchema, t => t.IgnoringCyclicReferences()