-
Notifications
You must be signed in to change notification settings - Fork 385
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1a9ace7
commit 6a45d0c
Showing
7 changed files
with
338 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
239 changes: 239 additions & 0 deletions
239
src/Microsoft.DotNet.Interactive.AspNetCore.Tests/AspNetCoreTests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,239 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System; | ||
using System.Threading.Tasks; | ||
using FluentAssertions; | ||
using Microsoft.DotNet.Interactive.AspNetCore; | ||
using Microsoft.DotNet.Interactive.Commands; | ||
using Microsoft.DotNet.Interactive.CSharp; | ||
using Microsoft.DotNet.Interactive.Events; | ||
using Microsoft.DotNet.Interactive.Tests.Utility; | ||
using Xunit; | ||
|
||
namespace Microsoft.DotNet.Interactive.App.Tests | ||
{ | ||
public class AspNetCoreTests : IDisposable | ||
{ | ||
private readonly CompositeKernel _kernel; | ||
|
||
public AspNetCoreTests() | ||
{ | ||
_kernel = new CompositeKernel | ||
{ | ||
new CSharpKernel(), | ||
}; | ||
|
||
var loadTask = new AspNetCoreKernelExtension().OnLoadAsync(_kernel); | ||
Assert.Same(Task.CompletedTask, loadTask); | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
_kernel.Dispose(); | ||
} | ||
|
||
[Fact] | ||
public async Task can_define_aspnet_endpoint_with_MapGet() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
Endpoints.MapGet(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapGet!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapGet!"); | ||
} | ||
|
||
[Fact] | ||
public async Task can_redefine_aspnet_endpoint_with_MapInteractive() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
Endpoints.MapGet(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapGet!""); | ||
}); | ||
Endpoints.MapInteractive(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapInteractive!""); | ||
}); | ||
Endpoints.MapInteractive(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapInteractive 2!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapInteractive 2!"); | ||
} | ||
|
||
[Fact] | ||
public async Task can_define_aspnet_middleware_with_Use() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
App.Use(next => | ||
{ | ||
return async httpContext => | ||
{ | ||
await httpContext.Response.WriteAsync(""Hello from middleware!""); | ||
}; | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from middleware!"); | ||
} | ||
|
||
[Fact] | ||
public async Task endpoints_take_precedence_over_new_middleware() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
App.Use(next => | ||
{ | ||
return async httpContext => | ||
{ | ||
await httpContext.Response.WriteAsync(""Hello from middleware!""); | ||
}; | ||
}); | ||
Endpoints.MapGet(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapGet!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapGet!"); | ||
|
||
// Re-adding the middleware makes no difference since it's added to the end of the pipeline. | ||
var result2 = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
App.Use(next => | ||
{ | ||
return async httpContext => | ||
{ | ||
await httpContext.Response.WriteAsync(""Hello from middleware!""); | ||
}; | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result2.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapGet!"); | ||
} | ||
|
||
[Fact] | ||
public async Task repeatedly_invoking_aspnet_command_noops() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
#!aspnet | ||
Endpoints.MapGet(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapGet!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapGet!"); | ||
} | ||
|
||
[Fact] | ||
public async Task aspnet_command_is_only_necessary_in_first_submission() | ||
{ | ||
var commandResult = await _kernel.SendAsync(new SubmitCode("#!aspnet")); | ||
|
||
commandResult.KernelEvents.ToSubscribedList().Should().NotContainErrors(); | ||
|
||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
Endpoints.MapGet(""/"", async context => | ||
{ | ||
await context.Response.WriteAsync($""Hello from MapGet!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Hello from MapGet!"); | ||
} | ||
|
||
[Fact] | ||
public async Task result_includes_trace_level_logs() | ||
{ | ||
var commandResult = await _kernel.SendAsync(new SubmitCode("#!aspnet")); | ||
|
||
commandResult.KernelEvents.ToSubscribedList().Should().NotContainErrors(); | ||
|
||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
using Microsoft.Extensions.DependencyInjection; | ||
using Microsoft.Extensions.Logging; | ||
Endpoints.MapGet(""/"", async httpContext => | ||
{ | ||
var loggerFactory = httpContext.RequestServices.GetRequiredService<ILoggerFactory>(); | ||
var logger = loggerFactory.CreateLogger(""interactive""); | ||
logger.LogTrace(""Log from MapGet!""); | ||
await httpContext.Response.WriteAsync(""Hello from MapGet!""); | ||
}); | ||
await HttpClient.GetAsync(""/"")")); | ||
|
||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.FormattedValues.Should().ContainSingle(f => f.MimeType == "text/html") | ||
.Which.Value.Should().Contain("Log from MapGet!"); | ||
} | ||
|
||
[Fact] | ||
public async Task server_listens_on_ephemeral_port() | ||
{ | ||
var result = await _kernel.SendAsync(new SubmitCode(@" | ||
#!aspnet | ||
HttpClient.BaseAddress")); | ||
|
||
// Assume any port higher than 1000 is ephemeral. In practice, the start of the ephemeral port range is | ||
// usually even higher (Windows XP and older Windows releases notwithstanding). | ||
// https://en.wikipedia.org/wiki/Ephemeral_port | ||
result.KernelEvents.ToSubscribedList().Should().NotContainErrors() | ||
.And.ContainSingle<ReturnValueProduced>() | ||
.Which.Value.Should().Match(uri => uri.As<Uri>().Port > 1_000); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
src/Microsoft.DotNet.Interactive.AspNetCore.Tests/Directory.Build.props
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
<Project> | ||
|
||
<PropertyGroup> | ||
<UseBetaVersion>true</UseBetaVersion> | ||
</PropertyGroup> | ||
|
||
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" /> | ||
|
||
</Project> |
39 changes: 39 additions & 0 deletions
39
....DotNet.Interactive.AspNetCore.Tests/Microsoft.DotNet.Interactive.AspNetCore.Tests.csproj
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup> | ||
<TargetFramework>net6.0</TargetFramework> | ||
<NoWarn>$(NoWarn);VSTHRD200</NoWarn><!-- Ignore: Use "Async" suffix for async methods --> | ||
</PropertyGroup> | ||
|
||
<ItemGroup> | ||
<Compile Remove="TestResults\**" /> | ||
<EmbeddedResource Remove="TestResults\**" /> | ||
<None Remove="TestResults\**" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Include="HtmlAgilityPack" Version="1.11.30" /> | ||
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" Version="3.1.8" /> | ||
<PackageReference Include="pocketlogger.subscribe" Version="0.7.0"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
<PackageReference Include="Assent" Version="1.7.0" /> | ||
<PackageReference Include="FluentAssertions" Version="5.10.3" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<ProjectReference Include="..\dotnet-interactive\dotnet-interactive.csproj" /> | ||
<ProjectReference Include="..\interface-generator\interface-generator.csproj" /> | ||
<ProjectReference Include="..\Microsoft.DotNet.Interactive.AspNetCore\Microsoft.DotNet.Interactive.AspNetCore.csproj" /> | ||
<ProjectReference Include="..\Microsoft.DotNet.Interactive.Tests\Microsoft.DotNet.Interactive.Tests.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup> | ||
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3"> | ||
<PrivateAssets>all</PrivateAssets> | ||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets> | ||
</PackageReference> | ||
</ItemGroup> | ||
|
||
</Project> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
24 changes: 24 additions & 0 deletions
24
src/Microsoft.DotNet.Interactive.AspNetCore/AspNetCoreKernelExtension.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
// Copyright (c) .NET Foundation and contributors. All rights reserved. | ||
// Licensed under the MIT license. See LICENSE file in the project root for full license information. | ||
|
||
using System.Threading.Tasks; | ||
using Microsoft.DotNet.Interactive.CSharp; | ||
|
||
namespace Microsoft.DotNet.Interactive.AspNetCore | ||
{ | ||
public class AspNetCoreKernelExtension : IKernelExtension | ||
{ | ||
public Task OnLoadAsync(Kernel kernel) | ||
{ | ||
kernel.VisitSubkernelsAndSelf(kernel => | ||
{ | ||
if (kernel is CSharpKernel cSharpKernel) | ||
{ | ||
cSharpKernel.UseAspNetCore(); | ||
} | ||
}); | ||
|
||
return Task.CompletedTask; | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters