feat: implement GetMessageBatchResultsAsync method
This commit is contained in:
@@ -42,6 +42,13 @@ public interface IAnthropicApiClient
|
|||||||
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="MessageBatchResponse"/>.</returns>
|
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="MessageBatchResponse"/>.</returns>
|
||||||
Task<AnthropicResult<MessageBatchResponse>> GetMessageBatchAsync(string batchId);
|
Task<AnthropicResult<MessageBatchResponse>> GetMessageBatchAsync(string batchId);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the results of a message batch asynchronously.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="batchId">The ID of the message batch to get the results for.</param>
|
||||||
|
/// <returns>A task that represents the asynchronous operation. The task result contains the response as an <see cref="AnthropicResult{T}"/> where T is <see cref="IAsyncEnumerable{T}"/> where T is <see cref="MessageBatchResultItem"/>.</returns>
|
||||||
|
Task<AnthropicResult<IAsyncEnumerable<MessageBatchResultItem>>> GetMessageBatchResultsAsync(string batchId);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Counts the tokens in a message asynchronously.
|
/// Counts the tokens in a message asynchronously.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -336,6 +343,38 @@ public class AnthropicApiClient : IAnthropicApiClient
|
|||||||
return AnthropicResult<MessageBatchResponse>.Success(msgBatchResponse, anthropicHeaders);
|
return AnthropicResult<MessageBatchResponse>.Success(msgBatchResponse, anthropicHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc/>
|
||||||
|
public async Task<AnthropicResult<IAsyncEnumerable<MessageBatchResultItem>>> GetMessageBatchResultsAsync(string batchId)
|
||||||
|
{
|
||||||
|
var response = await SendRequestAsync($"{MessageBatchesEndpoint}/{batchId}/results");
|
||||||
|
var anthropicHeaders = new AnthropicHeaders(response.Headers);
|
||||||
|
|
||||||
|
if (response.IsSuccessStatusCode is false)
|
||||||
|
{
|
||||||
|
var content = await response.Content.ReadAsStringAsync();
|
||||||
|
var error = Deserialize<AnthropicError>(content) ?? new AnthropicError();
|
||||||
|
return AnthropicResult<IAsyncEnumerable<MessageBatchResultItem>>.Failure(error, anthropicHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
|
async IAsyncEnumerable<MessageBatchResultItem> ReadResults()
|
||||||
|
{
|
||||||
|
using var responseContent = await response.Content.ReadAsStreamAsync();
|
||||||
|
using var streamReader = new StreamReader(responseContent);
|
||||||
|
|
||||||
|
var line = await streamReader.ReadLineAsync();
|
||||||
|
|
||||||
|
while (line is not null)
|
||||||
|
{
|
||||||
|
var resultItem = Deserialize<MessageBatchResultItem>(line) ?? new MessageBatchResultItem();
|
||||||
|
yield return resultItem;
|
||||||
|
|
||||||
|
line = await streamReader.ReadLineAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return AnthropicResult<IAsyncEnumerable<MessageBatchResultItem>>.Success(ReadResults(), anthropicHeaders);
|
||||||
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
public async Task<AnthropicResult<TokenCountResponse>> CountMessageTokensAsync(CountMessageTokensRequest request)
|
public async Task<AnthropicResult<TokenCountResponse>> CountMessageTokensAsync(CountMessageTokensRequest request)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ static class JsonSerializationOptions
|
|||||||
new EventDataConverter(),
|
new EventDataConverter(),
|
||||||
new ContentDeltaConverter(),
|
new ContentDeltaConverter(),
|
||||||
new JsonStringEnumConverter(),
|
new JsonStringEnumConverter(),
|
||||||
|
new MessageBatchResultConverter(),
|
||||||
},
|
},
|
||||||
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
using AnthropicClient.Models;
|
||||||
|
|
||||||
|
namespace AnthropicClient.Json;
|
||||||
|
|
||||||
|
class MessageBatchResultConverter : JsonConverter<MessageBatchResult>
|
||||||
|
{
|
||||||
|
public override MessageBatchResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
using var jsonDocument = JsonDocument.ParseValue(ref reader);
|
||||||
|
var root = jsonDocument.RootElement;
|
||||||
|
var type = root.GetProperty("type").GetString();
|
||||||
|
return type switch
|
||||||
|
{
|
||||||
|
MessageBatchResultType.Succeeded => JsonSerializer.Deserialize<SucceededMessageBatchResult>(root.GetRawText(), options)!,
|
||||||
|
MessageBatchResultType.Errored => JsonSerializer.Deserialize<ErroredMessageBatchResult>(root.GetRawText(), options)!,
|
||||||
|
MessageBatchResultType.Canceled => JsonSerializer.Deserialize<CanceledMessageBatchResult>(root.GetRawText(), options)!,
|
||||||
|
MessageBatchResultType.Expired => JsonSerializer.Deserialize<ExpiredMessageBatchResult>(root.GetRawText(), options)!,
|
||||||
|
_ => throw new JsonException($"Unknown message batch result type: {type}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Write(Utf8JsonWriter writer, MessageBatchResult value, JsonSerializerOptions options)
|
||||||
|
{
|
||||||
|
JsonSerializer.Serialize(writer, value, value.GetType(), options);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result that was cancelled.
|
||||||
|
/// </summary>
|
||||||
|
public class CanceledMessageBatchResult : MessageBatchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="CanceledMessageBatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public CanceledMessageBatchResult() : base(MessageBatchResultType.Canceled)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result that contains an error response.
|
||||||
|
/// </summary>
|
||||||
|
public class ErroredMessageBatchResult : MessageBatchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the error of the message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public AnthropicError Error { get; init; } = new AnthropicError();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ErroredMessageBatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ErroredMessageBatchResult() : base(MessageBatchResultType.Errored)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result that has expired.
|
||||||
|
/// </summary>
|
||||||
|
public class ExpiredMessageBatchResult : MessageBatchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="ExpiredMessageBatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
public ExpiredMessageBatchResult() : base(MessageBatchResultType.Expired)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using AnthropicClient.Utils;
|
||||||
|
|
||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class MessageBatchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public string Type { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="MessageBatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type of the message batch result.</param>
|
||||||
|
/// <returns>An instance of the <see cref="MessageBatchResult"/> class.</returns>
|
||||||
|
public MessageBatchResult(string type)
|
||||||
|
{
|
||||||
|
ArgumentValidator.ThrowIfNullOrWhitespace(type, nameof(type));
|
||||||
|
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result item.
|
||||||
|
/// </summary>
|
||||||
|
public class MessageBatchResultItem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the custom ID of the message batch result item.
|
||||||
|
/// </summary>
|
||||||
|
[JsonPropertyName("custom_id")]
|
||||||
|
public string CustomId { get; init; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the result of the message batch result item.
|
||||||
|
/// </summary>
|
||||||
|
public MessageBatchResult Result { get; init; } = default!;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents the types of message batch results.
|
||||||
|
/// </summary>
|
||||||
|
public static class MessageBatchResultType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a succeeded message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public const string Succeeded = "succeeded";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an errored message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public const string Errored = "errored";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a canceled message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public const string Canceled = "canceled";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents an expired message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public const string Expired = "expired";
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace AnthropicClient.Models;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a message batch result that contains a message response.
|
||||||
|
/// </summary>
|
||||||
|
public class SucceededMessageBatchResult : MessageBatchResult
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the message of the message batch result.
|
||||||
|
/// </summary>
|
||||||
|
public MessageResponse Message { get; init; } = new MessageResponse();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SucceededMessageBatchResult"/> class.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>An instance of the <see cref="SucceededMessageBatchResult"/> class.</returns>
|
||||||
|
public SucceededMessageBatchResult() : base(MessageBatchResultType.Succeeded)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
|
using AnthropicClient.Tests.Files;
|
||||||
|
|
||||||
namespace AnthropicClient.Tests.EndToEnd;
|
namespace AnthropicClient.Tests.EndToEnd;
|
||||||
|
|
||||||
public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture)
|
public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(configFixture)
|
||||||
{
|
{
|
||||||
private string GetTestFilePath(string fileName) =>
|
|
||||||
Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName);
|
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateMessageAsync_WhenCalled_ItShouldReturnResponse()
|
public async Task CreateMessageAsync_WhenCalled_ItShouldReturnResponse()
|
||||||
{
|
{
|
||||||
@@ -63,7 +62,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateMessageAsync_WhenImageIsSent_ItShouldReturnResponse()
|
public async Task CreateMessageAsync_WhenImageIsSent_ItShouldReturnResponse()
|
||||||
{
|
{
|
||||||
var imagePath = GetTestFilePath("elephant.jpg");
|
var imagePath = TestFileHelper.GetTestFilePath("elephant.jpg");
|
||||||
var mediaType = "image/jpeg";
|
var mediaType = "image/jpeg";
|
||||||
var bytes = await File.ReadAllBytesAsync(imagePath);
|
var bytes = await File.ReadAllBytesAsync(imagePath);
|
||||||
var base64Data = Convert.ToBase64String(bytes);
|
var base64Data = Convert.ToBase64String(bytes);
|
||||||
@@ -102,7 +101,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
|||||||
{
|
{
|
||||||
var client = CreateClient(new HttpClient());
|
var client = CreateClient(new HttpClient());
|
||||||
|
|
||||||
var storyPath = GetTestFilePath("story.txt");
|
var storyPath = TestFileHelper.GetTestFilePath("story.txt");
|
||||||
var storyText = await File.ReadAllTextAsync(storyPath);
|
var storyText = await File.ReadAllTextAsync(storyPath);
|
||||||
|
|
||||||
var request = new MessageRequest(
|
var request = new MessageRequest(
|
||||||
@@ -141,7 +140,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
|||||||
{
|
{
|
||||||
var client = CreateClient(new HttpClient());
|
var client = CreateClient(new HttpClient());
|
||||||
|
|
||||||
var storyPath = GetTestFilePath("story.txt");
|
var storyPath = TestFileHelper.GetTestFilePath("story.txt");
|
||||||
var storyText = await File.ReadAllTextAsync(storyPath);
|
var storyText = await File.ReadAllTextAsync(storyPath);
|
||||||
|
|
||||||
var request = new MessageRequest(
|
var request = new MessageRequest(
|
||||||
@@ -217,7 +216,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateMessageAsync_WhenProvidedWithPDF_ItShouldReturnResponse()
|
public async Task CreateMessageAsync_WhenProvidedWithPDF_ItShouldReturnResponse()
|
||||||
{
|
{
|
||||||
var pdfPath = GetTestFilePath("addendum.pdf");
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
||||||
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
||||||
var base64Data = Convert.ToBase64String(bytes);
|
var base64Data = Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
@@ -253,7 +252,7 @@ public class ClientTests(ConfigurationFixture configFixture) : EndToEndTest(conf
|
|||||||
[Fact]
|
[Fact]
|
||||||
public async Task CreateMessageAsync_WhenProvidedWithPDFWithCacheControl_ItShouldUseCache()
|
public async Task CreateMessageAsync_WhenProvidedWithPDFWithCacheControl_ItShouldUseCache()
|
||||||
{
|
{
|
||||||
var pdfPath = GetTestFilePath("addendum.pdf");
|
var pdfPath = TestFileHelper.GetTestFilePath("addendum.pdf");
|
||||||
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
var bytes = await File.ReadAllBytesAsync(pdfPath);
|
||||||
var base64Data = Convert.ToBase64String(bytes);
|
var base64Data = Convert.ToBase64String(bytes);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
namespace AnthropicClient.Tests.Files;
|
||||||
|
|
||||||
|
static class TestFileHelper
|
||||||
|
{
|
||||||
|
public static string GetTestFilePath(string fileName) =>
|
||||||
|
Path.Combine(Directory.GetCurrentDirectory(), "Files", fileName);
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{"custom_id":"my-fifth-request","result":{"type":"errored","error":{"type":"error","error":{"type":"not_found_error","message":"The requested resource could not be found."}}}}
|
||||||
|
{"custom_id":"my-fourth-request","result":{"type":"expired"}}
|
||||||
|
{"custom_id":"my-third-request","result":{"type":"canceled"}}
|
||||||
|
{"custom_id":"my-second-request","result":{"type":"succeeded","message":{"id":"msg_014VwiXbi91y3JMjcpyGBHX5","type":"message","role":"assistant","model":"claude-3-5-sonnet-20240620","content":[{"type":"text","text":"Hello again! It's nice to see you. How can I assist you today? Is there anything specific you'd like to chat about or any questions you have?"}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":11,"output_tokens":36}}}}
|
||||||
|
{"custom_id":"my-first-request","result":{"type":"succeeded","message":{"id":"msg_01FqfsLoHwgeFbguDgpz48m7","type":"message","role":"assistant","model":"claude-3-5-sonnet-20240620","content":[{"type":"text","text":"Hello! How can I assist you today? Feel free to ask me any questions or let me know if there's anything you'd like to chat about."}],"stop_reason":"end_turn","stop_sequence":null,"usage":{"input_tokens":10,"output_tokens":34}}}}
|
||||||
@@ -1,3 +1,6 @@
|
|||||||
|
using AnthropicClient.Tests.Files;
|
||||||
|
using AnthropicClient.Tests.Unit;
|
||||||
|
|
||||||
namespace AnthropicClient.Tests.Integration;
|
namespace AnthropicClient.Tests.Integration;
|
||||||
|
|
||||||
public class AnthropicApiClientTests : IntegrationTest
|
public class AnthropicApiClientTests : IntegrationTest
|
||||||
@@ -1218,4 +1221,30 @@ public class AnthropicApiClientTests : IntegrationTest
|
|||||||
result.IsSuccess.Should().BeTrue();
|
result.IsSuccess.Should().BeTrue();
|
||||||
result.Value.Should().BeOfType<MessageBatchResponse>();
|
result.Value.Should().BeOfType<MessageBatchResponse>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task GetMessageBatchResultsAsync_WhenCalledAndSuccessful_ItShouldReturnAllBatchResults()
|
||||||
|
{
|
||||||
|
var batchId = "msgbatch_013Zva2CMHLNnXjNJJKqJ2EF";
|
||||||
|
var batchResultsPath = TestFileHelper.GetTestFilePath("batch_results.jsonl");
|
||||||
|
var batchResultsText = await File.ReadAllTextAsync(batchResultsPath);
|
||||||
|
var batchResults = batchResultsText.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
var expectedResults = batchResults.Select(Deserialize<MessageBatchResultItem>);
|
||||||
|
|
||||||
|
_mockHttpMessageHandler
|
||||||
|
.WhenGetMessageBatchResultsRequest(batchId)
|
||||||
|
.Respond(
|
||||||
|
HttpStatusCode.OK,
|
||||||
|
"application/x-jsonl",
|
||||||
|
batchResultsText
|
||||||
|
);
|
||||||
|
|
||||||
|
var result = await Client.GetMessageBatchResultsAsync(batchId);
|
||||||
|
|
||||||
|
result.IsSuccess.Should().BeTrue();
|
||||||
|
|
||||||
|
var actualResults = await result.Value.ToListAsync();
|
||||||
|
|
||||||
|
actualResults.Should().BeEquivalentTo(expectedResults);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
using AnthropicClient.Tests.Unit;
|
||||||
|
|
||||||
namespace AnthropicClient.Tests.Integration;
|
namespace AnthropicClient.Tests.Integration;
|
||||||
|
|
||||||
public class IntegrationTest
|
public class IntegrationTest : SerializationTest
|
||||||
{
|
{
|
||||||
protected readonly MockHttpMessageHandler _mockHttpMessageHandler = new();
|
protected readonly MockHttpMessageHandler _mockHttpMessageHandler = new();
|
||||||
protected AnthropicApiClient Client => CreateClient();
|
protected AnthropicApiClient Client => CreateClient();
|
||||||
@@ -77,4 +79,10 @@ public static class MockHttpMessageHandlerExtensions
|
|||||||
return mockHttpMessageHandler
|
return mockHttpMessageHandler
|
||||||
.SetupBaseRequest(HttpMethod.Get, $"{MessageBatchesEndpoint}/{batchId}");
|
.SetupBaseRequest(HttpMethod.Get, $"{MessageBatchesEndpoint}/{batchId}");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static MockedRequest WhenGetMessageBatchResultsRequest(this MockHttpMessageHandler mockHttpMessageHandler, string batchId)
|
||||||
|
{
|
||||||
|
return mockHttpMessageHandler
|
||||||
|
.SetupBaseRequest(HttpMethod.Get, $"{MessageBatchesEndpoint}/{batchId}/results");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
namespace AnthropicClient.Tests.Unit.Models;
|
||||||
|
|
||||||
|
public class MessageBatchResultItemTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Constructor_WhenCalled_ItShouldReturnInstanceWithPropertiesSet()
|
||||||
|
{
|
||||||
|
var result = new MessageBatchResultItem();
|
||||||
|
|
||||||
|
result.Should().BeOfType<MessageBatchResultItem>();
|
||||||
|
result.CustomId.Should().BeEmpty();
|
||||||
|
result.Result.Should().Be(default);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user