Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ private async Task<RestApiOperationResponse> SendAsync(

throw;
}
catch (OperationCanceledException ex)
{
ex.Data.Add(HttpRequestMethod, requestMessage.Method.Method);
Comment thread
markwallace-microsoft marked this conversation as resolved.
ex.Data.Add(UrlFull, requestMessage.RequestUri?.ToString());
ex.Data.Add(HttpRequestBody, payload);

throw;
}
catch (KernelException ex)
{
ex.Data.Add(HttpRequestMethod, requestMessage.Method.Method);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1206,6 +1206,38 @@ public async Task ItShouldSetHttpRequestMessageOptionsAsync()
Assert.Equal(options.KernelArguments, kernelFunctionContext.Arguments);
}

[Fact]
public async Task ItShouldIncludeRequestDataWhenOperationCanceledExceptionIsThrownAsync()
{
// Arrange
this._httpMessageHandlerStub.ExceptionToThrow = new OperationCanceledException();

var operation = new RestApiOperation(
"fake-id",
new Uri("https://fake-random-test-host"),
"fake-path",
HttpMethod.Post,
"fake-description",
[],
payload: null
);

var arguments = new KernelArguments
{
{ "payload", JsonSerializer.Serialize(new { value = "fake-value" }) },
{ "content-type", "application/json" }
};

var sut = new RestApiOperationRunner(this._httpClient, this._authenticationHandlerMock.Object);

// Act & Assert
var canceledException = await Assert.ThrowsAsync<OperationCanceledException>(() => sut.RunAsync(operation, arguments));
Assert.Equal("The operation was canceled.", canceledException.Message);
Assert.Equal("POST", canceledException.Data["http.request.method"]);
Assert.Equal("https://fake-random-test-host/fake-path", canceledException.Data["url.full"]);
Assert.Equal("{\"value\":\"fake-value\"}", canceledException.Data["http.request.body"]);
}

public class SchemaTestData : IEnumerable<object[]>
{
public IEnumerator<object[]> GetEnumerator()
Expand Down Expand Up @@ -1302,6 +1334,8 @@ private sealed class HttpMessageHandlerStub : DelegatingHandler

public HttpResponseMessage ResponseToReturn { get; set; }

public Exception? ExceptionToThrow { get; set; }

public HttpMessageHandlerStub()
{
this.ResponseToReturn = new HttpResponseMessage(System.Net.HttpStatusCode.OK)
Expand All @@ -1312,6 +1346,11 @@ public HttpMessageHandlerStub()

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
if (this.ExceptionToThrow is not null)
{
throw this.ExceptionToThrow;
}

this.RequestMessage = request;
this.RequestContent = request.Content is null ? null : await request.Content.ReadAsByteArrayAsync(cancellationToken);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// Copyright (c) Microsoft. All rights reserved.
using System;
using System.Net.Http;
using System.Text.Json;
using System.Text.Json.Serialization;
Expand All @@ -17,7 +18,7 @@ public async Task ValidateInvokingRepairServicePluginAsync()
{
// Arrange
var kernel = new Kernel();
using var stream = System.IO.File.OpenRead("Plugins/repair-service.json");
using var stream = System.IO.File.OpenRead("Plugins/OpenApi/repair-service.json");
using HttpClient httpClient = new();

var plugin = await kernel.ImportPluginFromOpenApiAsync(
Expand Down Expand Up @@ -73,7 +74,7 @@ public async Task HttpOperationExceptionIncludeRequestInfoAsync()
{
// Arrange
var kernel = new Kernel();
using var stream = System.IO.File.OpenRead("Plugins/repair-service.json");
using var stream = System.IO.File.OpenRead("Plugins/OpenApi/repair-service.json");
using HttpClient httpClient = new();

var plugin = await kernel.ImportPluginFromOpenApiAsync(
Expand Down Expand Up @@ -107,12 +108,54 @@ public async Task HttpOperationExceptionIncludeRequestInfoAsync()
}
}

[Fact(Skip = "This test is for manual verification.")]
public async Task KernelFunctionCanceledExceptionIncludeRequestInfoAsync()
{
// Arrange
var kernel = new Kernel();
using var stream = System.IO.File.OpenRead("Plugins/OpenApi/repair-service.json");
using HttpClient httpClient = new();

var plugin = await kernel.ImportPluginFromOpenApiAsync(
"RepairService",
stream,
new OpenApiFunctionExecutionParameters(httpClient) { IgnoreNonCompliantErrors = true, EnableDynamicPayload = false });

var arguments = new KernelArguments
{
["payload"] = """{ "title": "Engine oil change", "description": "Need to drain the old engine oil and replace it with fresh oil.", "assignedTo": "", "date": "", "image": "" }"""
};

var id = 99999;

// Update Repair
arguments = new KernelArguments
{
["payload"] = $"{{ \"id\": {id}, \"assignedTo\": \"Karin Blair\", \"date\": \"2024-04-16\", \"image\": \"https://www.howmuchisit.org/wp-content/uploads/2011/01/oil-change.jpg\" }}"
};

try
{
httpClient.Timeout = TimeSpan.FromMilliseconds(10); // Force a timeout

await plugin["updateRepair"].InvokeAsync(kernel, arguments);
Assert.Fail("Expected KernelFunctionCanceledException");
}
catch (KernelFunctionCanceledException ex)
{
Assert.Equal("The invocation of function 'updateRepair' was canceled.", ex.Message);
Assert.NotNull(ex.InnerException);
Assert.Equal("Patch", ex.InnerException.Data["http.request.method"]);
Assert.Equal("https://piercerepairsapi.azurewebsites.net/repairs", ex.InnerException.Data["url.full"]);
}
}

[Fact(Skip = "This test is for manual verification.")]
public async Task UseDelegatingHandlerAsync()
{
// Arrange
var kernel = new Kernel();
using var stream = System.IO.File.OpenRead("Plugins/repair-service.json");
using var stream = System.IO.File.OpenRead("Plugins/OpenApi/repair-service.json");

using var httpHandler = new HttpClientHandler();
using var customHandler = new CustomHandler(httpHandler);
Expand Down