Add E2E test for external routing (#215)

This commit is contained in:
yzt 2021-04-28 12:05:38 +08:00 коммит произвёл GitHub
Родитель 8c805ad858
Коммит 95efb6a6b5
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
13 изменённых файлов: 264 добавлений и 1 удалений

Просмотреть файл

@ -4,4 +4,5 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("SignalRServiceExtension.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c547cac37abd99c8db225ef2f6c8a3602f3b3606cc9891605d02baa56104f4cfc0734aa39b93bf7852f7d9266654753cc297e7d2edfe0bac1cdcf9f717241550e0a7b191195b7667bb4f64bcb8e2121380fd1d9d46ad2d92d2d15605093924cceaf74c4861eff62abf69b9291ed0a340e113be11e6a7d3113e92484cf7045cc7")]

Просмотреть файл

@ -0,0 +1,28 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System.Net.Http;
using System.Net.Http.Json;
using System.Threading.Tasks;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
{
internal class ExternalRoutingClient
{
private static readonly HttpClient HttpClient = new();
public static async Task<SignalRConnectionInfo> Negotiate(string userId, string url, int endpointId)
{
const string path = "negotiate";
var connectionInfo = await HttpClient.GetFromJsonAsync<SignalRConnectionInfo>($"{url}/api/{path}?userId={userId}&endpointId={endpointId}");
return connectionInfo;
}
public static async Task Send(string url, int endpointId, string target)
{
const string SendPath = "send";
await HttpClient.GetAsync($"{url}/api/{SendPath}?endpointId={endpointId}&target={target}");
}
}
}

Просмотреть файл

@ -0,0 +1,83 @@
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using Xunit;
using static Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests.Utils;
using Client = Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests.ExternalRoutingClient;
namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
{
public class ExternalRoutingTests
{
private const string Section = "ExternalRouting";
public static readonly IEnumerable<object[]> FunctionUrls = GetFunctionUrls(Section);
/// <summary>
/// Set up two connections, each of which connects to a different endpoint.
/// Trigger the function to send a message to endpoint[0]. The parameter is the endpoint itself. Validate the endpoint status in the connection[0].
/// Repeat last step to the endpoint[1].
/// </summary>
[MemberData(nameof(FunctionUrls))]
[SkipIfFunctionAbsent(Section)]
public async Task RoutingTest(string key, string url)
{
var target = nameof(RoutingTest) + key;
const int count = 2;
var users = GenerateRandomUsers(count).ToArray();
var completionSources = new ConcurrentDictionary<string, TaskCompletionSource>();
var tasks = Enumerable.Range(0, count).Zip(users).Select(async (pair) =>
{
var (endpointId, user) = pair;
var connectionInfo = await Client.Negotiate(user, url, endpointId);
var connection = CreateHubConnection(connectionInfo.Url, connectionInfo.AccessToken);
var taskCompleSource = new TaskCompletionSource();
completionSources.TryAdd(user, taskCompleSource);
connection.On(target, (LiteServiceEndpoint endpoint) =>
{
var expectedHost = new Uri(connectionInfo.Url).Host;
var actualHost = new Uri(endpoint.Endpoint).Host;
if (expectedHost.Equals(actualHost) && endpoint.Online)
{
completionSources[user].SetResult();
}
else
{
completionSources[user].SetException(new Exception($"Expected host:{expectedHost}, Actual host:{actualHost}"));
}
});
await connection.StartAsync();
return connection;
}).ToArray();
var connections = await Task.WhenAll(tasks);
// send a message to endpoint[0]
await Client.Send(url, 0, target);
await Task.WhenAny(completionSources.Values.Select(s => s.Task.OrTimeout()));
// received the correct messsage
Assert.True(completionSources[users[0]].Task.IsCompletedSuccessfully);
// not received messages yet
Assert.False(completionSources[users[1]].Task.IsCompleted);
//reset
foreach (var user in users)
{
completionSources[user] = new TaskCompletionSource();
}
// send a message to endpoint[1]
await Client.Send(url, 1, target);
await Task.WhenAny(completionSources.Values.Select(s => s.Task.OrTimeout()));
Assert.True(completionSources[users[1]].Task.IsCompletedSuccessfully);
Assert.False(completionSources[users[0]].Task.IsCompleted);
}
}
}

Просмотреть файл

@ -15,6 +15,7 @@
<PackageReference Include="xunit" Version="$(XunitPackageVersion)" />
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitRunnerVisualstudioPackageVersion)" />
<PackageReference Include="Microsoft.Azure.SignalR.Management" Version="$(MicrosoftAzureSignalRManagement)" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="$(MicrosoftAspNetCoreSignalRProtocolsNewtonsoftJson)" />
</ItemGroup>
<ItemGroup>

Просмотреть файл

@ -8,6 +8,7 @@ using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR.Client;
using Microsoft.Extensions.Configuration;
using Xunit;
using Microsoft.Extensions.DependencyInjection;
namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
{
@ -38,7 +39,9 @@ namespace Microsoft.Azure.Webjobs.Extensions.SignalRService.E2ETests
{
return Task.FromResult(accessToken);
};
}).Build();
})
.AddNewtonsoftJsonProtocol()
.Build();
public static async Task OrTimeout(this Task task, TimeSpan timeout = default)
{

Просмотреть файл

@ -1,6 +1,8 @@
{
"FunctionBaseUrl": {
"SimpleChat": {
},
"ExternalRouting": {
}
}
}

43
test/e2etest-functions/external-routing/js/.gitignore поставляемый Normal file
Просмотреть файл

@ -0,0 +1,43 @@
bin
obj
csx
.vs
edge
Publish
*.user
*.suo
*.cscfg
*.Cache
project.lock.json
/packages
/TestResults
/tools/NuGet.exe
/App_Data
/secrets
/data
.secrets
appsettings.json
local.settings.json
node_modules
dist
# Local python packages
.python_packages/
# Python Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class

Просмотреть файл

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>
<AzureFunctionsVersion>v3</AzureFunctionsVersion>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\..\src\SignalRServiceExtension\Microsoft.Azure.WebJobs.Extensions.SignalRService.csproj" />
</ItemGroup>
<ItemGroup>
<None Update="host.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project>

Просмотреть файл

@ -0,0 +1,11 @@
{
"version": "2.0",
"logging": {
"applicationInsights": {
"samplingSettings": {
"isEnabled": true,
"excludedTypes": "Request"
}
}
}
}

Просмотреть файл

@ -0,0 +1,26 @@
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"type": "signalRNegotiation",
"direction": "in",
"name": "negoCtx",
"userId": "{query.userId}",
"hubName": "%hub%"
}
]
}

Просмотреть файл

@ -0,0 +1,5 @@
module.exports = async function (context, req, negoCtx) {
var id = req.query.endpointId;
var endpointNum = negoCtx.endpoints.length;
context.res.body = negoCtx.endpoints[id % endpointNum].connectionInfo;
}

Просмотреть файл

@ -0,0 +1,31 @@
{
"bindings": [
{
"authLevel": "function",
"type": "httpTrigger",
"direction": "in",
"name": "req",
"methods": [
"get",
"post"
]
},
{
"type": "http",
"direction": "out",
"name": "res"
},
{
"type": "signalREndpoints",
"direction": "in",
"name": "endpoints",
"hubName": "%hub%"
},
{
"type": "signalR",
"direction": "out",
"name": "signalRMessages",
"hubName": "%hub%"
}
]
}

Просмотреть файл

@ -0,0 +1,12 @@
module.exports = async function (context, req, endpoints) {
var id = req.query.endpointId;
var target = req.query.target;
var endpoint = endpoints[id % endpoints.length];
context.bindings.signalRMessages = [{
"target": target,
"endpoints": [endpoint],
"arguments": [endpoint]
}];
context.done();
}