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 @@ -105,6 +105,10 @@ private static IServiceCollection AddAzureFunctionsGraphQLRequestExecutor(
.UseWhen(
context => context.MayReachNitroApp(path),
branch => branch.UseNitroApp(path, serverOptions.Tool))
.Use(MiddlewareFactory.CreateHttpUnsupportedRequestMiddleware(
executor,
serverOptions,
path))
.Compile(sp);

return new DefaultGraphQLRequestExecutor(pipeline);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
using System.Net;
using System.Text;
using HotChocolate.AspNetCore;
using HotChocolate.AspNetCore.Formatters;
using HotChocolate.AzureFunctions.IsolatedProcess.Tests.Helpers;
using HotChocolate.AzureFunctions.Tests.Helpers;
using HotChocolate.Types;
using Microsoft.AspNetCore.Http;
using Microsoft.Azure.Functions.Extensions.DependencyInjection;
using Microsoft.Azure.Functions.Worker.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Newtonsoft.Json.Linq;

namespace HotChocolate.AzureFunctions.IsolatedProcess.Tests;
Expand Down Expand Up @@ -232,6 +235,221 @@ public async Task AzFuncIsolatedProcess_NitroTestAsync()
Assert.True(resultContent.Contains("<html") && resultContent.Contains("</html>"));
}

[Theory]
[InlineData(HttpTransportVersion.Legacy, HttpStatusCode.NotFound, null)]
[InlineData(HttpTransportVersion.Draft20250508, HttpStatusCode.NotFound, null)]
[InlineData(
HttpTransportVersion.Draft20260903,
HttpStatusCode.MethodNotAllowed,
"GET, HEAD, OPTIONS, POST")]
public async Task Put_Should_ReturnMethodNotAllowed_When_MethodIsUnsupported(
HttpTransportVersion transportVersion,
HttpStatusCode expectedStatusCode,
string? expectedAllow)
{
// arrange
var host = CreateHost(transportVersion);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
HttpMethods.Put,
TestHttpContextHelper.DefaultAzFuncGraphQLUri,
"""{"query":"{ person }"}""");

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(expectedStatusCode, response.StatusCode);
Assert.Equal(expectedAllow, GetHeader(response, "Allow"));
Assert.Equal(0, response.Body.Length);
}

[Theory]
[InlineData(HttpTransportVersion.Legacy, HttpStatusCode.NotFound, null)]
[InlineData(HttpTransportVersion.Draft20250508, HttpStatusCode.NotFound, null)]
[InlineData(
HttpTransportVersion.Draft20260903,
HttpStatusCode.NoContent,
"GET, HEAD, OPTIONS, POST")]
public async Task Options_Should_ReturnAllowedMethods_When_EndpointIsRequested(
HttpTransportVersion transportVersion,
HttpStatusCode expectedStatusCode,
string? expectedAllow)
{
// arrange
var host = CreateHost(transportVersion);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
HttpMethods.Options,
TestHttpContextHelper.DefaultAzFuncGraphQLUri);

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(expectedStatusCode, response.StatusCode);
Assert.Equal(expectedAllow, GetHeader(response, "Allow"));
}

[Theory]
[InlineData(HttpTransportVersion.Legacy, HttpStatusCode.NotFound)]
[InlineData(HttpTransportVersion.Draft20250508, HttpStatusCode.NotFound)]
[InlineData(HttpTransportVersion.Draft20260903, HttpStatusCode.UnsupportedMediaType)]
public async Task Post_Should_ReturnUnsupportedMediaType_When_ContentTypeIsUnsupported(
HttpTransportVersion transportVersion,
HttpStatusCode expectedStatusCode)
{
// arrange
var host = CreateHost(transportVersion);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
HttpMethods.Post,
TestHttpContextHelper.DefaultAzFuncGraphQLUri,
"{ person }",
"text/plain");

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(expectedStatusCode, response.StatusCode);
Assert.Equal(0, response.Body.Length);
}

[Theory]
[InlineData(HttpTransportVersion.Legacy, HttpStatusCode.NotFound, null)]
[InlineData(HttpTransportVersion.Draft20250508, HttpStatusCode.NotFound, null)]
[InlineData(
HttpTransportVersion.Draft20260903,
HttpStatusCode.MethodNotAllowed,
"OPTIONS, POST")]
public async Task Get_Should_ReturnMethodNotAllowed_When_GetRequestsAreDisabled(
HttpTransportVersion transportVersion,
HttpStatusCode expectedStatusCode,
string? expectedAllow)
{
// arrange
var host = CreateHost(transportVersion, enableGetRequests: false);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
HttpMethods.Get,
new Uri(
TestHttpContextHelper.DefaultAzFuncGraphQLUri,
"?query=%7B%20person%20%7D"));

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(expectedStatusCode, response.StatusCode);
Assert.Equal(expectedAllow, GetHeader(response, "Allow"));
}

[Fact]
public async Task Put_Should_ReturnNotFound_When_PathIsBelowTheFunctionRoute()
{
// arrange
var host = CreateHost(HttpTransportVersion.Draft20260903);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
HttpMethods.Put,
new Uri(TestHttpContextHelper.DefaultAzFuncGraphQLUri, "/api/graphql/other"),
"""{"query":"{ person }"}""");

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(HttpStatusCode.NotFound, response.StatusCode);
}

[Theory]
[InlineData(HttpTransportVersion.Legacy, HttpStatusCode.NotFound, null)]
[InlineData(HttpTransportVersion.Draft20250508, HttpStatusCode.NotFound, null)]
[InlineData(
HttpTransportVersion.Draft20260903,
HttpStatusCode.UnsupportedMediaType,
"application/json")]
public async Task Query_Should_ReturnUnsupportedMediaType_When_ContentTypeIsUnsupported(
HttpTransportVersion transportVersion,
HttpStatusCode expectedStatusCode,
string? expectedAcceptQuery)
{
// arrange
var host = CreateHost(transportVersion, enableQueryRequests: true);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
"QUERY",
TestHttpContextHelper.DefaultAzFuncGraphQLUri,
"{ person }",
"text/plain");

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(expectedStatusCode, response.StatusCode);
Assert.Equal(expectedAcceptQuery, GetHeader(response, "Accept-Query"));
Assert.Equal(0, response.Body.Length);
}

[Fact]
public async Task Query_Should_ExecuteRequest_When_QueryRequestsAreEnabled()
{
// arrange
var host = CreateHost(HttpTransportVersion.Latest, enableQueryRequests: true);
var requestExecutor = host.Services.GetRequiredService<IGraphQLRequestExecutor>();
var request = new MockHttpRequestData(
new MockFunctionContext(host.Services),
"QUERY",
TestHttpContextHelper.DefaultAzFuncGraphQLUri,
"""{"query":"{ person }"}""");

// act
var response = await requestExecutor.ExecuteAsync(request);

// assert
Assert.Equal(HttpStatusCode.OK, response.StatusCode);
Assert.Equal(
"""{"data":{"person":"Luke Skywalker"}}""",
await ReadResponseAsStringAsync(response));
}

private static IHost CreateHost(
HttpTransportVersion transportVersion,
bool enableGetRequests = true,
bool enableQueryRequests = false)
{
var formatterOptions = new HttpResponseFormatterOptions
{
HttpTransportVersion = transportVersion
};

return new MockIsolatedProcessHostBuilder()
.AddGraphQLFunction(
b => b
.AddQueryType(d => d.Name("Query").Field("person").Resolve("Luke Skywalker"))
.AddHttpResponseFormatter(formatterOptions)
.ModifyFunctionOptions(
o =>
{
o.EnableGetRequests = enableGetRequests;
o.EnableQueryRequests = enableQueryRequests;
}))
.Build();
}

private static string? GetHeader(HttpResponseData response, string name)
=> response.Headers.TryGetValues(name, out var values)
? string.Join(", ", values)
: null;

private static async Task<string> ReadResponseAsStringAsync(HttpResponseData responseData)
{
responseData.Body.Seek(0, SeekOrigin.Begin);
Expand Down
Loading
Loading