Skip to content

Adding tests for GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando #11040

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Apr 30, 2025
Merged
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
170 changes: 170 additions & 0 deletions test/WebJobs.Script.Tests/GrpcMessageExtensionUtilitiesTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Google.Protobuf;
using Google.Protobuf.Collections;
using Microsoft.Azure.WebJobs.Script.Grpc;
using Microsoft.Azure.WebJobs.Script.Grpc.Messages;
using Xunit;

namespace Microsoft.Azure.WebJobs.Script.Tests
{
public sealed class GrpcMessageExtensionUtilitiesTests
{
[Theory]
[InlineData(HttpStatusCode.OK)]
[InlineData(HttpStatusCode.BadRequest)]
[InlineData(HttpStatusCode.Accepted)]
[InlineData(HttpStatusCode.NotFound)]
public async Task ConvertFromHttpMessageToExpando_ShouldReturnExpandoObject_WithStringResponseAndStatusCode(HttpStatusCode httpStatusCode)
{
var responseBodyString = "Hello world";
var rpcHttp = await CreateRpcHttpAsync(responseBodyString, httpStatusCode);
rpcHttp.EnableContentNegotiation = true;

dynamic result = GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando(rpcHttp);

AssertValidHttpResponseObject(result, "GET", httpStatusCode, responseBodyString, enableContentNegotiation: true);

var headers = result.headers as IDictionary<string, object>;
Assert.Empty(headers);

var cookies = (IEnumerable<object>)result.cookies;
Assert.Empty(cookies);
}

[Fact]
public async Task ConvertFromHttpMessageToExpando_ShouldReturnExpandoObject_WithPOCOResponse()
{
var bookPoco = new Book("Book1", "Author1", 49.99, new DateTime(2012, 1, 1), true, ["C#", "CLR"]);
var responseHeaders = new MapField<string, string>
{
{ "header1", "value1" },
{ "header2", "value2" }
};
var responseCookies = new RepeatedField<RpcHttpCookie>
{
new RpcHttpCookie
{
Name = "cookie1",
Value = "value1",
Domain = new NullableString { Value = "microsoft.com" }
}
};
var rpcHttp = await CreateRpcHttpAsync(bookPoco, headers: responseHeaders, cookies: responseCookies);

dynamic result = GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando(rpcHttp);

AssertValidHttpResponseObject(result, "GET", HttpStatusCode.OK);

var headers = result.headers as IDictionary<string, object>;
Assert.Equal(2, headers.Count);
Assert.Equal("value1", headers["header1"]);
Assert.Equal("value2", headers["header2"]);

var cookies = (IEnumerable<object>)result.cookies;
Assert.Single(cookies);

var body = result.body;
Assert.NotNull(body);

if (body is byte[] bodyAsByteArray)
{
var actualBook = JsonSerializer.Deserialize<Book>(Encoding.UTF8.GetString(bodyAsByteArray));

Assert.Equal(bookPoco.Title, actualBook.Title);
Assert.Equal(bookPoco.Author, actualBook.Author);
Assert.Equal(bookPoco.Price, actualBook.Price);
Assert.Equal(bookPoco.PublishedDate.ToString("yyyy-MM-dd"), actualBook.PublishedDate.ToString("yyyy-MM-dd"));
Assert.Equal(bookPoco.IsBestSeller, actualBook.IsBestSeller);
Assert.Equal(bookPoco.Tags, actualBook.Tags);
}
}

[Fact]
public void ConvertFromHttpMessageToExpando_ShouldReturnNull_WhenInputIsNull()
{
var result = GrpcMessageExtensionUtilities.ConvertFromHttpMessageToExpando(null);

Assert.Null(result);
}

private static async Task<RpcHttp> CreateRpcHttpAsync<T>(T body,
HttpStatusCode httpStatusCode = HttpStatusCode.OK, string method = "GET",
MapField<string, string> headers = null, RepeatedField<RpcHttpCookie> cookies = null)
{
var response = new RpcHttp
{
Method = method,
Query = { },
StatusCode = ((int)httpStatusCode).ToString(),
Headers = { },
Cookies = { },
Body = await CreateResponseBodyTypedDataAsync(body)
};

if (headers != null)
{
foreach (var header in headers)
{
response.Headers.Add(header.Key, header.Value);
}
}

if (cookies != null)
{
foreach (var cookie in cookies)
{
response.Cookies.Add(cookie);
}
}

return response;
}

private static async Task<TypedData> CreateResponseBodyTypedDataAsync<T>(T value)
{
TypedData typedData = new();

using var memoryStream = new MemoryStream();
if (value is string stringValue)
{
await memoryStream.WriteAsync(Encoding.UTF8.GetBytes(stringValue));
}
else
{
await JsonSerializer.SerializeAsync(memoryStream, value, typeof(T));
}

memoryStream.Position = 0;
typedData.Bytes = await ByteString.FromStreamAsync(memoryStream);

return typedData;
}

private static void AssertValidHttpResponseObject(dynamic expando, string expectedMethod, HttpStatusCode expectedStatusCode, string expectedResponseBodyContent = null, bool enableContentNegotiation = false)
{
Assert.NotNull(expando);
Assert.Equal(expectedMethod, expando.method);
Assert.Equal(((int)expectedStatusCode).ToString(), expando.statusCode);
Assert.Equal(enableContentNegotiation, expando.enableContentNegotiation);

if (expectedResponseBodyContent is null || expando.body is not byte[] bodyAsByteArray)
{
return;
}

var bodyAsString = Encoding.UTF8.GetString(bodyAsByteArray);
Assert.Equal(expectedResponseBodyContent, bodyAsString);
}
}

internal record Book(string Title, string Author, double Price, DateTime PublishedDate, bool IsBestSeller, List<string> Tags);
}