From 8ca4a331e2766efe243993cabbbbca79f6498d17 Mon Sep 17 00:00:00 2001 From: Javier Calvarro Nelson Date: Thu, 3 Apr 2014 17:45:05 -0700 Subject: [PATCH] Created an in memory host for testing purposes and added the appropriate APIs to allow sending requests through the pipeline easily. --- Hosting.sln | 24 +- .../RequestInformation.cs | 34 +++ .../ResponseInformation.cs | 29 +++ src/Microsoft.AspNet.TestHost/TestClient.cs | 206 ++++++++++++++++++ src/Microsoft.AspNet.TestHost/TestServer.cs | 109 +++++++++ src/Microsoft.AspNet.TestHost/project.json | 32 +++ .../TestApplicationEnvironment.cs | 29 +++ .../TestClientTests.cs | 153 +++++++++++++ .../TestServerTests.cs | 74 +++++++ .../project.json | 41 ++++ 10 files changed, 726 insertions(+), 5 deletions(-) create mode 100644 src/Microsoft.AspNet.TestHost/RequestInformation.cs create mode 100644 src/Microsoft.AspNet.TestHost/ResponseInformation.cs create mode 100644 src/Microsoft.AspNet.TestHost/TestClient.cs create mode 100644 src/Microsoft.AspNet.TestHost/TestServer.cs create mode 100644 src/Microsoft.AspNet.TestHost/project.json create mode 100644 test/Microsoft.AspNet.TestHost.Tests/TestApplicationEnvironment.cs create mode 100644 test/Microsoft.AspNet.TestHost.Tests/TestClientTests.cs create mode 100644 test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs create mode 100644 test/Microsoft.AspNet.TestHost.Tests/project.json diff --git a/Hosting.sln b/Hosting.sln index 76c17f7d3e..164d969b42 100644 --- a/Hosting.sln +++ b/Hosting.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 -VisualStudioVersion = 12.0.21005.1 +VisualStudioVersion = 12.0.30319.0 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{E0497F39-AFFB-4819-A116-E39E361915AB}" EndProject @@ -27,6 +27,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.k10", "samples\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "KWebStartup.net45", "samples\KWebStartup\KWebStartup.net45.csproj", "{B5939234-73ED-4DAD-A2C6-D61AA6DFB406}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.TestHost.Tests.net45", "test\Microsoft.AspNet.TestHost.Tests\Microsoft.AspNet.TestHost.Tests.net45.csproj", "{A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Microsoft.AspNet.TestHost.net45", "src\Microsoft.AspNet.TestHost\Microsoft.AspNet.TestHost.net45.csproj", "{25E1550A-A73F-4E06-9F13-9F72695ADFC8}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -61,19 +65,29 @@ Global {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Debug|Any CPU.Build.0 = Debug|Any CPU {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Release|Any CPU.ActiveCfg = Release|Any CPU {B5939234-73ED-4DAD-A2C6-D61AA6DFB406}.Release|Any CPU.Build.0 = Release|Any CPU + {A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145}.Release|Any CPU.Build.0 = Release|Any CPU + {25E1550A-A73F-4E06-9F13-9F72695ADFC8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25E1550A-A73F-4E06-9F13-9F72695ADFC8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25E1550A-A73F-4E06-9F13-9F72695ADFC8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25E1550A-A73F-4E06-9F13-9F72695ADFC8}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} = {E0497F39-AFFB-4819-A116-E39E361915AB} - {EB784E77-FD42-46EC-9C8C-502B78962407} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {D546290B-E280-4D99-BA9C-0D364A0AFB54} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} + {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91} = {EB784E77-FD42-46EC-9C8C-502B78962407} {80588AF3-6B14-4D11-9DC4-1EF6453B54C9} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} {F4C7B46C-B39F-4172-9C3A-05352183D469} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} - {D546290B-E280-4D99-BA9C-0D364A0AFB54} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} {FD9833B9-3FB1-4612-BBB4-64F539DB0F8B} = {EB784E77-FD42-46EC-9C8C-502B78962407} - {DBB72F0F-755D-41CF-8FE2-F4B6AE214E91} = {EB784E77-FD42-46EC-9C8C-502B78962407} + {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} = {E0497F39-AFFB-4819-A116-E39E361915AB} + {EB784E77-FD42-46EC-9C8C-502B78962407} = {E0497F39-AFFB-4819-A116-E39E361915AB} {C0235BA1-9198-42C0-92D8-7578E9B9D96B} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} {B5939234-73ED-4DAD-A2C6-D61AA6DFB406} = {C30C98CD-3D69-4AE9-B680-0E0E6D8834C6} + {A19DBFCE-7B43-4BC6-9C3B-A47EBB65E145} = {FEB39027-9158-4DE2-997F-7ADAEF8188D0} + {25E1550A-A73F-4E06-9F13-9F72695ADFC8} = {93FB86DB-7D2A-46CE-AFD2-0B53E6A8CF69} EndGlobalSection EndGlobal diff --git a/src/Microsoft.AspNet.TestHost/RequestInformation.cs b/src/Microsoft.AspNet.TestHost/RequestInformation.cs new file mode 100644 index 0000000000..eabfc0213a --- /dev/null +++ b/src/Microsoft.AspNet.TestHost/RequestInformation.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.AspNet.HttpFeature; + +namespace Microsoft.AspNet.TestHost +{ + internal class RequestInformation : IHttpRequestInformation + { + public RequestInformation() + { + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + PathBase = ""; + Body = Stream.Null; + Protocol = "HTTP/1.1"; + } + + public Stream Body { get; set; } + + public IDictionary Headers { get; set; } + + public string Method { get; set; } + + public string Path { get; set; } + + public string PathBase { get; set; } + + public string Protocol { get; set; } + + public string QueryString { get; set; } + + public string Scheme { get; set; } + } +} diff --git a/src/Microsoft.AspNet.TestHost/ResponseInformation.cs b/src/Microsoft.AspNet.TestHost/ResponseInformation.cs new file mode 100644 index 0000000000..7000b081c5 --- /dev/null +++ b/src/Microsoft.AspNet.TestHost/ResponseInformation.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.AspNet.HttpFeature; + +namespace Microsoft.AspNet.TestHost +{ + internal class ResponseInformation : IHttpResponseInformation + { + public ResponseInformation() + { + Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); + Body = new MemoryStream(); + } + + public int StatusCode { get; set; } + + public string ReasonPhrase { get; set; } + + public IDictionary Headers { get; set; } + + public Stream Body { get; set; } + + public void OnSendingHeaders(Action callback, object state) + { + // TODO: Figure out how to implement this thing. + } + } +} diff --git a/src/Microsoft.AspNet.TestHost/TestClient.cs b/src/Microsoft.AspNet.TestHost/TestClient.cs new file mode 100644 index 0000000000..e5f5fb6778 --- /dev/null +++ b/src/Microsoft.AspNet.TestHost/TestClient.cs @@ -0,0 +1,206 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.FeatureModel; +using Microsoft.AspNet.HttpFeature; +using Microsoft.AspNet.PipelineCore; + +namespace Microsoft.AspNet.TestHost +{ + public class TestClient + { + private readonly Func _pipeline; + + public TestClient(Func pipeline) + { + _pipeline = pipeline; + } + + public async Task SendAsync(string method, + string url, + IDictionary headers = null, + Stream body = null, + Action onSendingRequest = null) + { + return await SendAsync(method, new Uri(url), headers, body, onSendingRequest); + } + + public async Task SendAsync(string method, + Uri uri, + IDictionary headers = null, + Stream body = null, + Action onSendingRequest = null) + { + var request = CreateRequest(method, uri, headers, body); + var response = new ResponseInformation(); + + var features = new FeatureCollection(); + features.Add(typeof(IHttpRequestInformation), request); + features.Add(typeof(IHttpResponseInformation), response); + var httpContext = new DefaultHttpContext(features); + + if (onSendingRequest != null) + { + onSendingRequest(httpContext.Request); + } + await _pipeline(features); + + response.Body.Seek(0, SeekOrigin.Begin); + return httpContext.Response; + } + + private static IHttpRequestInformation CreateRequest( + string method, + Uri uri, + IDictionary headers, + Stream body) + { + var request = new RequestInformation(); + request.Method = method; + request.Scheme = uri.Scheme; + request.Path = PathString.FromUriComponent(uri).Value; + request.QueryString = QueryString.FromUriComponent(uri).Value; + request.Headers = headers ?? request.Headers; + if (!request.Headers.ContainsKey("Host")) + { + var host = new string[1]; + if (uri.IsDefaultPort) + { + host[0] = uri.Host; + } + else + { + host[0] = uri.GetComponents(UriComponents.HostAndPort, UriFormat.UriEscaped); + } + request.Headers["Host"] = host; + } + + if (body != null) + { + EnsureContentLength(request.Headers, body); + request.Body = body; + } + else + { + request.Body = Stream.Null; + } + return request; + } + + public async Task GetAsync(string url) + { + var uri = new Uri(url); + return await GetAsync(uri); + } + + public async Task GetAsync(Uri uri) + { + return await SendAsync("GET", uri); + } + + public async Task GetStringAsync(string url) + { + var uri = new Uri(url); + return await GetStringAsync(uri); + } + + public async Task GetStringAsync(Uri uri) + { + var response = await GetAsync(uri); + return await new StreamReader(response.Body).ReadToEndAsync(); + } + + public async Task GetStreamAsync(string url) + { + var uri = new Uri(url); + return await GetStreamAsync(uri); + } + + public async Task GetStreamAsync(Uri uri) + { + var response = await GetAsync(uri); + return response.Body; + } + + public async Task PostAsync( + string url, + string content, + string contentType, + Action onSendingRequest = null) + { + return await PostAsync(new Uri(url), content, contentType, onSendingRequest); + } + + public async Task PostAsync( + Uri url, + string content, + string contentType, + Action onSendingRequest = null) + { + var bytes = GetBytes(content); + var headers = CreateContentHeaders(contentType, bytes.Length); + var body = new MemoryStream(bytes); + + return await SendAsync("POST", url, headers, body, onSendingRequest); + } + + public async Task PutAsync( + string url, + string content, + string contentType, + Action onSendingRequest = null) + { + return await PutAsync(new Uri(url), content, contentType, onSendingRequest); + } + + public async Task PutAsync( + Uri url, + string content, + string contentType, + Action onSendingRequest = null) + { + var bytes = GetBytes(content); + var headers = CreateContentHeaders(contentType, bytes.Length); + var body = new MemoryStream(bytes); + + return await SendAsync("PUT", url, headers, body, onSendingRequest); + } + + public async Task DeleteAsync(string url) + { + return await DeleteAsync(new Uri(url)); + } + + public async Task DeleteAsync(Uri uri) + { + return await SendAsync("DELETE", uri); + } + + private static void EnsureContentLength(IDictionary dictionary, Stream body) + { + if (!dictionary.ContainsKey("Content-Length")) + { + dictionary["Content-Length"] = new[] { body.Length.ToString(CultureInfo.InvariantCulture) }; + } + } + + private static byte[] GetBytes(string content) + { + var bytes = Encoding.UTF8.GetBytes(content); + return bytes; + } + + private static Dictionary CreateContentHeaders(string contentType, int contentLength) + { + return new Dictionary(StringComparer.OrdinalIgnoreCase) + { + { "Content-Type", new [] { contentType } }, + { "Content-Length", new [] { contentLength.ToString(CultureInfo.InvariantCulture) } } + }; + } + } +} diff --git a/src/Microsoft.AspNet.TestHost/TestServer.cs b/src/Microsoft.AspNet.TestHost/TestServer.cs new file mode 100644 index 0000000000..9256501426 --- /dev/null +++ b/src/Microsoft.AspNet.TestHost/TestServer.cs @@ -0,0 +1,109 @@ +using System; +using System.Collections.Generic; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.ConfigurationModel; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.DependencyInjection.Fallback; +using Microsoft.AspNet.Hosting; +using Microsoft.AspNet.Hosting.Server; +using Microsoft.AspNet.Hosting.Startup; +using Microsoft.Net.Runtime; + +namespace Microsoft.AspNet.TestHost +{ + public class TestServer : IServerFactory, IDisposable + { + private static readonly string ServerName = typeof(TestServer).FullName; + private static readonly ServerInformation ServerInfo = new ServerInformation(); + private Func _appDelegate; + private TestClient _handler; + + public TestServer(IConfiguration config, IServiceProvider serviceProvider, Action appStartup) + { + var env = serviceProvider.GetService(); + if (env == null) + { + throw new ArgumentException("IApplicationEnvironment couldn't be resolved.", "serviceProvider"); + } + + HostingContext hostContext = new HostingContext() + { + ApplicationName = env.ApplicationName, + Configuration = config, + ServerFactory = this, + Services = serviceProvider, + ApplicationStartup = appStartup + }; + + var engine = serviceProvider.GetService(); + var disposable = engine.Start(hostContext); + } + + public static TestServer Create(IServiceProvider provider) + { + var startupLoader = new StartupLoader(provider, new NullStartupLoader()); + var name = typeof(TStartup).AssemblyQualifiedName; + var diagnosticMessages = new List(); + return Create(provider, startupLoader.LoadStartup(name, diagnosticMessages)); + } + + public static TestServer Create(IServiceProvider provider, Action app) + { + var collection = new ServiceCollection(); + var hostingServices = HostingServices.GetDefaultServices(); + + var config = new Configuration(); + collection.Add(hostingServices); + + var serviceProvider = collection.BuildServiceProvider(provider); + return new TestServer(config, serviceProvider, app); + } + + public TestClient Handler + { + get + { + if (_handler == null) + { + _handler = new TestClient(_appDelegate); + } + + return _handler; + } + } + + public IServerInformation Initialize(IConfiguration configuration) + { + return ServerInfo; + } + + public IDisposable Start(IServerInformation serverInformation, Func application) + { + if (!(serverInformation.GetType() == typeof(ServerInformation))) + { + throw new ArgumentException(string.Format("The server must be {0}", ServerName), "serverInformation"); + } + + _appDelegate = application; + + return this; + } + + public void Dispose() + { + // IServerFactory.Start needs to return an IDisposable. Typically this IDisposable instance is used to + // clear any server resources when tearing down the host. In our case we don't have anything to clear + // so we just implement IDisposable and do nothing. + } + + private class ServerInformation : IServerInformation + { + public string Name + { + get { return TestServer.ServerName; } + } + } + } +} diff --git a/src/Microsoft.AspNet.TestHost/project.json b/src/Microsoft.AspNet.TestHost/project.json new file mode 100644 index 0000000000..48c00f0871 --- /dev/null +++ b/src/Microsoft.AspNet.TestHost/project.json @@ -0,0 +1,32 @@ +{ + "version" : "0.1-alpha-*", + "dependencies": { + "System.Reflection": "4.0.10.0", + "System.Runtime" : "4.0.20.0", + "Microsoft.AspNet.Abstractions": "0.1-alpha-*", + "Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*", + "Microsoft.AspNet.DependencyInjection": "0.1-alpha-*", + "Microsoft.AspNet.FeatureModel": "0.1-alpha-*", + "Microsoft.AspNet.Hosting": "0.1-alpha-*", + "Microsoft.AspNet.HttpFeature": "0.1-alpha-*", + "Microsoft.AspNet.PipelineCore": "0.1-alpha-*", + "Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*" + }, + "configurations": { + "net45": { + "dependencies": { + "System.Collections": "4.0.0.0", + "System.ComponentModel": "4.0.0.0", + "System.Globalization": "4.0.10.0", + "System.Linq": "4.0.0.0", + "System.Reflection": "4.0.10.0", + "System.Runtime": "4.0.20.0", + "System.Runtime.Extensions": "4.0.10.0", + "System.Runtime.Serialization.Primitives": "4.0.0.0", + "System.Threading": "4.0.0.0", + "System.Threading.Tasks": "4.0.0.0", + "System.Threading.Thread": "4.0.0.0" + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNet.TestHost.Tests/TestApplicationEnvironment.cs b/test/Microsoft.AspNet.TestHost.Tests/TestApplicationEnvironment.cs new file mode 100644 index 0000000000..93fb40c3f5 --- /dev/null +++ b/test/Microsoft.AspNet.TestHost.Tests/TestApplicationEnvironment.cs @@ -0,0 +1,29 @@ +using System; +using System.Runtime.Versioning; +using Microsoft.Net.Runtime; + +namespace Microsoft.AspNet.TestHost.Tests +{ + public class TestApplicationEnvironment : IApplicationEnvironment + { + public string ApplicationName + { + get { return "Test App environment"; } + } + + public string Version + { + get { return "1.0.0"; } + } + + public string ApplicationBasePath + { + get { return Environment.CurrentDirectory; } + } + + public FrameworkName TargetFramework + { + get { return new FrameworkName(".NETFramework", new Version(4, 5)); } + } + } +} diff --git a/test/Microsoft.AspNet.TestHost.Tests/TestClientTests.cs b/test/Microsoft.AspNet.TestHost.Tests/TestClientTests.cs new file mode 100644 index 0000000000..b18dd0ccfa --- /dev/null +++ b/test/Microsoft.AspNet.TestHost.Tests/TestClientTests.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.DependencyInjection.Fallback; +using Microsoft.Net.Runtime; +using Xunit; + +namespace Microsoft.AspNet.TestHost.Tests +{ + public class TestClientTests + { + private readonly IServiceProvider _services; + private readonly TestServer _server; + + public TestClientTests() + { + _services = new ServiceCollection() + .AddSingleton() + .BuildServiceProvider(); + + _server = TestServer.Create(_services, app => app.Run(async ctx => { })); + } + + [Fact] + public async Task SendAsync_ConfiguresRequestProperly() + { + // Arrange + var client = _server.Handler; + + // Act + var response = await client.SendAsync("GET", "http://localhost:12345/Home/Index?id=3&name=peter#fragment"); + var request = response.HttpContext.Request; + + // Assert + Assert.NotNull(request); + Assert.Equal("HTTP/1.1", request.Protocol); + Assert.Equal("GET", request.Method); + Assert.Equal("http", request.Scheme); + Assert.Equal("localhost:12345", request.Host.Value); + Assert.Equal("", request.PathBase.Value); + Assert.True(request.Path.HasValue); + Assert.Equal("/Home/Index", request.Path.Value); + Assert.Equal("?id=3&name=peter", request.QueryString.Value); + Assert.Null(request.ContentLength); + Assert.Equal(1, request.Headers.Count); + Assert.True(request.Headers.ContainsKey("Host")); + } + + [Fact] + public async Task SendAsync_InvokesCallbackWhenPassed() + { + // Arrange + var client = _server.Handler; + var invoked = false; + + // Act + var response = await client.SendAsync("GET", "http://localhost:12345/", null, null, _ => invoked = true); + + // Assert + Assert.True(invoked); + } + + [Fact] + public async Task SendAsync_RespectsExistingHost() + { + // Arrange + var client = _server.Handler; + var headers = new Dictionary { { "Host", new string[] { "server:12345" } } }; + + // Act + var response = await client.SendAsync("GET", "http://localhost:12345/Home/", headers); + var request = response.HttpContext.Request; + + // Assert + Assert.Equal("server:12345", request.Host.Value); + } + + [Fact] + public async Task SendAsync_RespectsArgumentBody() + { + // Arrange + var client = _server.Handler; + var headers = new Dictionary { { "Content-Type", new string[] { "text/plain" } } }; + var body = new MemoryStream(); + new StreamWriter(body).Write("Hello world"); + body.Position = 0; + + // Act + var response = await client.SendAsync("POST", "http://host/", headers, body); + var request = response.HttpContext.Request; + + // Assert + Assert.Same(body, request.Body); + Assert.Equal(0, request.Body.Position); + Assert.Equal(body.Length, request.ContentLength); + } + + [Fact] + public async Task SendAsync_RewindsTheResponseStream() + { + // Arrange + var server = TestServer.Create(_services, app => app.Run(ctx => ctx.Response.WriteAsync("Hello world"))); + var client = server.Handler; + + // Act + var response = await client.SendAsync("GET", "http://localhost"); + + // Assert + Assert.Equal(0, response.Body.Position); + Assert.Equal("Hello world", new StreamReader(response.Body).ReadToEnd()); + } + + [Fact] + public async Task PutAsyncWorks() + { + // Arrange + RequestDelegate appDelegate = async ctx => + await ctx.Response.WriteAsync(new StreamReader(ctx.Request.Body).ReadToEnd()); + var server = TestServer.Create(_services, app => app.Run(appDelegate)); + var client = server.Handler; + + // Act + var response = await client.PutAsync("http://localhost:12345", "Hello world", "text/plain"); + var request = response.HttpContext.Request; + + // Assert + Assert.Equal("PUT", request.Method); + Assert.Equal("Hello world", new StreamReader(response.Body).ReadToEnd()); + } + + [Fact] + public async Task PostAsyncWorks() + { + // Arrange + RequestDelegate appDelegate = async ctx => + await ctx.Response.WriteAsync(new StreamReader(ctx.Request.Body).ReadToEnd()); + var server = TestServer.Create(_services, app => app.Run(appDelegate)); + var client = server.Handler; + + // Act + var response = await client.PostAsync("http://localhost:12345", "Hello world", "text/plain"); + var request = response.HttpContext.Request; + + // Assert + Assert.Equal("POST", request.Method); + Assert.Equal("Hello world", new StreamReader(response.Body).ReadToEnd()); + } + } +} diff --git a/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs b/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs new file mode 100644 index 0000000000..62416f99f1 --- /dev/null +++ b/test/Microsoft.AspNet.TestHost.Tests/TestServerTests.cs @@ -0,0 +1,74 @@ +using System; +using System.IO; +using System.Runtime.Versioning; +using System.Threading.Tasks; +using Microsoft.AspNet.Abstractions; +using Microsoft.AspNet.DependencyInjection; +using Microsoft.AspNet.DependencyInjection.Fallback; +using Microsoft.Net.Runtime; +using Xunit; + +namespace Microsoft.AspNet.TestHost.Tests +{ + public class TestServerTests + { + [Fact] + public void CreateWithDelegate() + { + // Arrange + var services = new ServiceCollection() + .AddSingleton() + .BuildServiceProvider(); + + // Act & Assert + Assert.DoesNotThrow(() => TestServer.Create(services, app => { })); + } + + [Fact] + public async Task CreateWithGeneric() + { + // Arrange + var services = new ServiceCollection() + .AddSingleton() + .BuildServiceProvider(); + + var server = TestServer.Create(services); + var client = server.Handler; + + // Act + var response = await client.GetAsync("http://any"); + + // Assert + Assert.Equal("Startup", new StreamReader(response.Body).ReadToEnd()); + } + + [Fact] + public void ThrowsIfNoApplicationEnvironmentIsRegisteredWithTheProvider() + { + // Arrange + var services = new ServiceCollection() + .BuildServiceProvider(); + + // Act & Assert + Assert.Throws( + "serviceProvider", + () => TestServer.Create(services)); + } + + public class Startup + { + public void Configuration(IBuilder builder) + { + builder.Run(ctx => ctx.Response.WriteAsync("Startup")); + } + } + + public class AnotherStartup + { + public void Configuration(IBuilder builder) + { + builder.Run(ctx => ctx.Response.WriteAsync("Another Startup")); + } + } + } +} diff --git a/test/Microsoft.AspNet.TestHost.Tests/project.json b/test/Microsoft.AspNet.TestHost.Tests/project.json new file mode 100644 index 0000000000..34ba146fef --- /dev/null +++ b/test/Microsoft.AspNet.TestHost.Tests/project.json @@ -0,0 +1,41 @@ +{ + "version" : "0.1-alpha-*", + "dependencies": { + "System.Reflection": "4.0.10.0", + "System.Runtime" : "4.0.20.0", + "Microsoft.AspNet.Abstractions": "0.1-alpha-*", + "Microsoft.AspNet.ConfigurationModel": "0.1-alpha-*", + "Microsoft.AspNet.DependencyInjection": "0.1-alpha-*", + "Microsoft.AspNet.FeatureModel": "0.1-alpha-*", + "Microsoft.AspNet.Hosting": "", + "Microsoft.AspNet.TestHost": "", + "Microsoft.AspNet.HttpFeature": "0.1-alpha-*", + "Microsoft.AspNet.PipelineCore": "0.1-alpha-*", + "Microsoft.AspNet.Security.DataProtection": "0.1-alpha-*", + "xunit.abstractions": "2.0.0-aspnet-*", + "xunit.assert": "2.0.0-aspnet-*", + "xunit.core": "2.0.0-aspnet-*", + "xunit.execution": "2.0.0-aspnet-*", + "Xunit.KRunner": "0.1-alpha-*" + }, + "commands": { + "test": "Xunit.KRunner" + }, + "configurations": { + "net45": { + "dependencies": { + "System.Collections": "4.0.0.0", + "System.ComponentModel": "4.0.0.0", + "System.Globalization": "4.0.10.0", + "System.Linq": "4.0.0.0", + "System.Reflection": "4.0.10.0", + "System.Runtime": "4.0.20.0", + "System.Runtime.Extensions": "4.0.10.0", + "System.Runtime.Serialization.Primitives": "4.0.0.0", + "System.Threading": "4.0.0.0", + "System.Threading.Tasks": "4.0.0.0", + "System.Threading.Thread": "4.0.0.0" + } + } + } +} \ No newline at end of file