Added tests

This commit is contained in:
David Fowler 2016-10-04 01:32:30 -07:00
parent e5e8d1bee3
commit 3032909f3c
12 changed files with 398 additions and 10 deletions

View File

@ -19,6 +19,10 @@ Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Socket
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "ClientSample", "samples\ClientSample\ClientSample.xproj", "{BA99C2A1-48F9-4FA5-B95A-9687A73B7CC9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{6A35B453-52EC-48AF-89CA-D4A69800F131}"
EndProject
Project("{8BB2217D-0F2D-49D1-97BC-3654ED321F3B}") = "Microsoft.AspNetCore.Sockets.Tests", "test\Microsoft.AspNetCore.Sockets.Tests\Microsoft.AspNetCore.Sockets.Tests.xproj", "{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -37,6 +41,10 @@ Global
{BA99C2A1-48F9-4FA5-B95A-9687A73B7CC9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BA99C2A1-48F9-4FA5-B95A-9687A73B7CC9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BA99C2A1-48F9-4FA5-B95A-9687A73B7CC9}.Release|Any CPU.Build.0 = Release|Any CPU
{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -45,5 +53,6 @@ Global
{C4AEAB04-F341-4539-B6C0-52368FB4BF9E} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{1715EA8D-8E13-4ACF-8BCA-57D048E55ED8} = {DA69F624-5398-4884-87E4-B816698CDE65}
{BA99C2A1-48F9-4FA5-B95A-9687A73B7CC9} = {C4BC9889-B49F-41B6-806B-F84941B2549B}
{AAD719D5-5E31-4ED1-A60F-6EB92EFA66D9} = {6A35B453-52EC-48AF-89CA-D4A69800F131}
EndGlobalSection
EndGlobal

View File

@ -5,7 +5,7 @@ namespace Microsoft.AspNetCore.Sockets
/// <summary>
/// Represents an end point that multiple connections connect to. For HTTP, endpoints are URLs, for non HTTP it can be a TCP listener (or similar)
/// </summary>
public class EndPoint
public abstract class EndPoint
{
/// <summary>
/// Live list of connections for this <see cref="EndPoint"/>
@ -17,9 +17,6 @@ namespace Microsoft.AspNetCore.Sockets
/// </summary>
/// <param name="connection">The new <see cref="Connection"/></param>
/// <returns>A <see cref="Task"/> that represents the connection lifetime. When the task completes, the connection is complete.</returns>
public virtual Task OnConnected(Connection connection)
{
return Task.CompletedTask;
}
public abstract Task OnConnected(Connection connection);
}
}

View File

@ -10,8 +10,14 @@ namespace Microsoft.AspNetCore.Sockets
{
public class HttpConnectionDispatcher
{
private readonly ConnectionManager _manager = new ConnectionManager();
private readonly ChannelFactory _channelFactory = new ChannelFactory();
private readonly ConnectionManager _manager;
private readonly ChannelFactory _channelFactory;
public HttpConnectionDispatcher(ConnectionManager manager, ChannelFactory factory)
{
_manager = manager;
_channelFactory = factory;
}
public async Task Execute<TEndPoint>(string path, HttpContext context) where TEndPoint : EndPoint
{
@ -201,7 +207,7 @@ namespace Microsoft.AspNetCore.Sockets
return context.Request.Body.CopyToAsync(httpChannel.Input);
}
return Task.CompletedTask;
throw new InvalidOperationException("Unknown connection id");
}
private ConnectionState GetOrCreateConnection(HttpContext context)

View File

@ -1,4 +1,5 @@
using System;
using Channels;
using Microsoft.AspNetCore.Routing;
using Microsoft.AspNetCore.Sockets;
using Microsoft.AspNetCore.Sockets.Routing;
@ -9,7 +10,9 @@ namespace Microsoft.AspNetCore.Builder
{
public static IApplicationBuilder UseSockets(this IApplicationBuilder app, Action<SocketRouteBuilder> callback)
{
var dispatcher = new HttpConnectionDispatcher();
var manager = new ConnectionManager();
var factory = new ChannelFactory();
var dispatcher = new HttpConnectionDispatcher(manager, factory);
var routes = new RouteBuilder(app);
callback(new SocketRouteBuilder(routes, dispatcher));

View File

@ -7,6 +7,7 @@
},
"frameworks": {
"netstandard1.3": {
}
},
"net46": { }
}
}

View File

@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Channels;
using Xunit;
namespace Microsoft.AspNetCore.Sockets.Tests
{
public class ConnectionManagerTests
{
[Fact]
public void ReservedConnectionsHaveConnectionId()
{
var connectionManager = new ConnectionManager();
var state = connectionManager.ReserveConnection();
Assert.NotNull(state.Connection);
Assert.NotNull(state.Connection.ConnectionId);
Assert.True(state.Active);
Assert.Null(state.Close);
Assert.Null(state.Connection.Channel);
}
[Fact]
public void ReservedConnectionsCanBeRetrieved()
{
var connectionManager = new ConnectionManager();
var state = connectionManager.ReserveConnection();
Assert.NotNull(state.Connection);
Assert.NotNull(state.Connection.ConnectionId);
ConnectionState newState;
Assert.True(connectionManager.TryGetConnection(state.Connection.ConnectionId, out newState));
Assert.Same(newState, state);
}
[Fact]
public void AddNewConnection()
{
using (var factory = new ChannelFactory())
using (var channel = new HttpChannel(factory))
{
var connectionManager = new ConnectionManager();
var state = connectionManager.AddNewConnection(channel);
Assert.NotNull(state.Connection);
Assert.NotNull(state.Connection.ConnectionId);
Assert.NotNull(state.Connection.Channel);
ConnectionState newState;
Assert.True(connectionManager.TryGetConnection(state.Connection.ConnectionId, out newState));
Assert.Same(newState, state);
Assert.Same(channel, newState.Connection.Channel);
}
}
[Fact]
public void RemoveConnection()
{
using (var factory = new ChannelFactory())
using (var channel = new HttpChannel(factory))
{
var connectionManager = new ConnectionManager();
var state = connectionManager.AddNewConnection(channel);
Assert.NotNull(state.Connection);
Assert.NotNull(state.Connection.ConnectionId);
Assert.NotNull(state.Connection.Channel);
ConnectionState newState;
Assert.True(connectionManager.TryGetConnection(state.Connection.ConnectionId, out newState));
Assert.Same(newState, state);
Assert.Same(channel, newState.Connection.Channel);
connectionManager.RemoveConnection(state.Connection.ConnectionId);
Assert.False(connectionManager.TryGetConnection(state.Connection.ConnectionId, out newState));
}
}
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Channels;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.Internal;
using Microsoft.Extensions.Primitives;
using Xunit;
namespace Microsoft.AspNetCore.Sockets.Tests
{
public class HttpConnectionDispatcherTests
{
[Fact]
public async Task GetIdReservesConnectionIdAndReturnsIt()
{
var manager = new ConnectionManager();
using (var factory = new ChannelFactory())
{
var dispatcher = new HttpConnectionDispatcher(manager, factory);
var context = new DefaultHttpContext();
var ms = new MemoryStream();
context.Request.Path = "/getid";
context.Response.Body = ms;
await dispatcher.Execute<TestEndPoint>("", context);
var id = Encoding.UTF8.GetString(ms.ToArray());
ConnectionState state;
Assert.True(manager.TryGetConnection(id, out state));
Assert.Equal(id, state.Connection.ConnectionId);
}
}
[Fact]
public async Task SendingToReservedConnectionsThatHaveNotConnectedThrows()
{
var manager = new ConnectionManager();
var state = manager.ReserveConnection();
using (var factory = new ChannelFactory())
{
var dispatcher = new HttpConnectionDispatcher(manager, factory);
var context = new DefaultHttpContext();
context.Request.Path = "/send";
var values = new Dictionary<string, StringValues>();
values["id"] = state.Connection.ConnectionId;
var qs = new QueryCollection(values);
context.Request.Query = qs;
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await dispatcher.Execute<TestEndPoint>("", context);
});
}
}
[Fact]
public async Task SendingToUnknownConnectionIdThrows()
{
var manager = new ConnectionManager();
using (var factory = new ChannelFactory())
{
var dispatcher = new HttpConnectionDispatcher(manager, factory);
var context = new DefaultHttpContext();
context.Request.Path = "/send";
var values = new Dictionary<string, StringValues>();
values["id"] = "unknown";
var qs = new QueryCollection(values);
context.Request.Query = qs;
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await dispatcher.Execute<TestEndPoint>("", context);
});
}
}
[Fact]
public async Task SendingWithoutConnectionIdThrows()
{
var manager = new ConnectionManager();
using (var factory = new ChannelFactory())
{
var dispatcher = new HttpConnectionDispatcher(manager, factory);
var context = new DefaultHttpContext();
context.Request.Path = "/send";
await Assert.ThrowsAsync<InvalidOperationException>(async () =>
{
await dispatcher.Execute<TestEndPoint>("", context);
});
}
}
}
public class TestEndPoint : EndPoint
{
public override Task OnConnected(Connection connection)
{
throw new NotImplementedException();
}
}
}

View File

@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Channels;
using Microsoft.AspNetCore.Http;
using Xunit;
namespace Microsoft.AspNetCore.Sockets.Tests
{
public class LongPollingTests
{
[Fact]
public async Task Set204StatusCodeWhenChannelComplete()
{
using (var factory = new ChannelFactory())
{
var connection = new Connection();
connection.ConnectionId = Guid.NewGuid().ToString();
var channel = new HttpChannel(factory);
connection.Channel = channel;
var context = new DefaultHttpContext();
var poll = new LongPolling(connection);
channel.Output.CompleteWriter();
await poll.ProcessRequest(context);
Assert.Equal(204, context.Response.StatusCode);
}
}
[Fact]
public async Task NoFramingAddedWhenDataSent()
{
using (var factory = new ChannelFactory())
{
var connection = new Connection();
connection.ConnectionId = Guid.NewGuid().ToString();
var channel = new HttpChannel(factory);
connection.Channel = channel;
var context = new DefaultHttpContext();
var ms = new MemoryStream();
context.Response.Body = ms;
var poll = new LongPolling(connection);
await channel.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
channel.Output.CompleteWriter();
await poll.ProcessRequest(context);
Assert.Equal("Hello World", Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
}

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">14.0</VisualStudioVersion>
<VSToolsPath Condition="'$(VSToolsPath)' == ''">$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.Props" Condition="'$(VSToolsPath)' != ''" />
<PropertyGroup Label="Globals">
<ProjectGuid>aad719d5-5e31-4ed1-a60f-6eb92efa66d9</ProjectGuid>
<RootNamespace>Microsoft.AspNetCore.Sockets.Tests</RootNamespace>
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
<TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

View File

@ -0,0 +1,19 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Microsoft.AspNetCore.Sockets.Tests")]
[assembly: AssemblyTrademark("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("aad719d5-5e31-4ed1-a60f-6eb92efa66d9")]

View File

@ -0,0 +1,61 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Channels;
using Microsoft.AspNetCore.Http;
using Xunit;
namespace Microsoft.AspNetCore.Sockets.Tests
{
public class ServerSentEventsTests
{
[Fact]
public async Task SSESetsContentType()
{
using (var factory = new ChannelFactory())
{
var connection = new Connection();
connection.ConnectionId = Guid.NewGuid().ToString();
var channel = new HttpChannel(factory);
connection.Channel = channel;
var sse = new ServerSentEvents(connection);
var context = new DefaultHttpContext();
channel.Output.CompleteWriter();
await sse.ProcessRequest(context);
Assert.Equal("text/event-stream", context.Response.ContentType);
Assert.Equal("no-cache", context.Response.Headers["Cache-Control"]);
}
}
[Fact]
public async Task SSEAddsAppropriateFraming()
{
using (var factory = new ChannelFactory())
{
var connection = new Connection();
connection.ConnectionId = Guid.NewGuid().ToString();
var channel = new HttpChannel(factory);
connection.Channel = channel;
var sse = new ServerSentEvents(connection);
var context = new DefaultHttpContext();
var ms = new MemoryStream();
context.Response.Body = ms;
await channel.Output.WriteAsync(Encoding.UTF8.GetBytes("Hello World"));
channel.Output.CompleteWriter();
await sse.ProcessRequest(context);
var expected = "data: Hello World\n\n";
Assert.Equal(expected, Encoding.UTF8.GetString(ms.ToArray()));
}
}
}
}

View File

@ -0,0 +1,25 @@
{
"buildOptions": {
"warningsAsErrors": true
},
"dependencies": {
"dotnet-test-xunit": "2.2.0-*",
"Microsoft.AspNetCore.Http": "1.1.0-*",
"Microsoft.AspNetCore.Sockets": {
"target": "project"
},
"xunit": "2.2.0-*"
},
"frameworks": {
"netcoreapp1.0": {
"dependencies": {
"Microsoft.NETCore.App": {
"version": "1.0.0",
"type": "platform"
}
}
},
"net46": {}
},
"testRunner": "xunit"
}