Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
66 changes: 66 additions & 0 deletions activate.ps1
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#
# This file must be used by invoking ". .\activate.ps1" from the command line.
# You cannot run it directly. See https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_scripts#script-scope-and-dot-sourcing
#
# To exit from the environment this creates, execute the 'deactivate' function.
#

if ($MyInvocation.CommandOrigin -eq 'runspace') {
Write-Host -f Red "This script cannot be invoked directly."
Write-Host -f Red "To function correctly, this script file must be 'dot sourced' by calling `". $PSCommandPath`" (notice the dot at the beginning)."
exit 1
}

function deactivate ([switch]$init) {

# reset old environment variables
if (Test-Path variable:_OLD_PATH) {
$env:PATH = $_OLD_PATH
Remove-Item variable:_OLD_PATH
}

if (test-path function:_old_prompt) {
Set-Item Function:prompt -Value $function:_old_prompt -ea ignore
remove-item function:_old_prompt
}

Remove-Item env:DOTNET_ROOT -ea ignore
Remove-Item 'env:DOTNET_ROOT(x86)' -ea Ignore
Remove-Item env:DOTNET_MULTILEVEL_LOOKUP -ea ignore
if (-not $init) {
# Remove the deactivate function
Remove-Item function:deactivate
}
}

# Cleanup the environment
deactivate -init

$_OLD_PATH = $env:PATH
# Tell dotnet where to find itself
$env:DOTNET_ROOT = "$PSScriptRoot\.dotnet"
${env:DOTNET_ROOT(x86)} = "$PSScriptRoot\.dotnet\x86"
# Tell dotnet not to look beyond the DOTNET_ROOT folder for more dotnet things
$env:DOTNET_MULTILEVEL_LOOKUP = 0
# Put dotnet first on PATH
$env:PATH = "${env:DOTNET_ROOT};${env:PATH}"

# Set the shell prompt
if (-not $env:DISABLE_CUSTOM_PROMPT) {
$function:_old_prompt = $function:prompt
function dotnet_prompt {
# Add a prefix to the current prompt, but don't discard it.
write-host "($( split-path $PSScriptRoot -leaf )) " -nonewline
& $function:_old_prompt
}

Set-Item Function:prompt -Value $function:dotnet_prompt -ea ignore
}

Write-Host -f Magenta "Enabled the .NET Core environment. Execute 'deactivate' to exit."
if (-not (Test-Path "${env:DOTNET_ROOT}\dotnet.exe")) {
Write-Host -f Yellow ".NET Core has not been installed yet. Run $PSScriptRoot\restore.cmd to install it."
}
else {
Write-Host "dotnet = ${env:DOTNET_ROOT}\dotnet.exe"
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
{
"Name": "Microsoft.AspNetCore.Diagnostics.Middleware, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"Types": [
{
"Type": "static class Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions",
"Stage": "Experimental",
"Methods": [
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLogEnricher<T>(this Microsoft.Extensions.DependencyInjection.IServiceCollection services);",
"Stage": "Experimental"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLoggingRedaction(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action<Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions>? configure = null);",
"Stage": "Experimental"
},
{
"Member": "static Microsoft.Extensions.DependencyInjection.IServiceCollection Microsoft.Extensions.DependencyInjection.HttpLoggingServiceCollectionExtensions.AddHttpLoggingRedaction(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, Microsoft.Extensions.Configuration.IConfigurationSection section);",
"Stage": "Experimental"
}
]
},
{
"Type": "static class Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames",
"Stage": "Stable",
Expand Down Expand Up @@ -33,7 +51,7 @@
{
"Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.RequestHeaderPrefix",
"Stage": "Stable",
"Value": "RequestHeader_"
"Value": "RequestHeader."
},
{
"Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseBody",
Expand All @@ -43,7 +61,7 @@
{
"Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.ResponseHeaderPrefix",
"Stage": "Stable",
"Value": "ResponseHeader_"
"Value": "ResponseHeader."
},
{
"Member": "const string Microsoft.AspNetCore.Diagnostics.Logging.HttpLoggingTagNames.StatusCode",
Expand All @@ -58,6 +76,16 @@
}
]
},
{
"Type": "interface Microsoft.AspNetCore.Diagnostics.Logging.IHttpLogEnricher",
"Stage": "Experimental",
"Methods": [
{
"Member": "void Microsoft.AspNetCore.Diagnostics.Logging.IHttpLogEnricher.Enrich(Microsoft.Extensions.Diagnostics.Enrichment.IEnrichmentTagCollector collector, Microsoft.AspNetCore.Http.HttpContext httpContext);",
"Stage": "Experimental"
}
]
},
{
"Type": "enum Microsoft.AspNetCore.Diagnostics.Logging.IncomingPathLoggingMode",
"Stage": "Stable",
Expand All @@ -80,6 +108,42 @@
}
]
},
{
"Type": "class Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions",
"Stage": "Experimental",
"Methods": [
{
"Member": "Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.LoggingRedactionOptions();",
"Stage": "Experimental"
}
],
"Properties": [
{
"Member": "System.Collections.Generic.ISet<string> Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.ExcludePathStartsWith { get; set; }",
"Stage": "Experimental"
},
{
"Member": "System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Compliance.Classification.DataClassification> Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.RequestHeadersDataClasses { get; set; }",
"Stage": "Experimental"
},
{
"Member": "Microsoft.AspNetCore.Diagnostics.Logging.IncomingPathLoggingMode Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.RequestPathLoggingMode { get; set; }",
"Stage": "Experimental"
},
{
"Member": "Microsoft.Extensions.Http.Diagnostics.HttpRouteParameterRedactionMode Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.RequestPathParameterRedactionMode { get; set; }",
"Stage": "Experimental"
},
{
"Member": "System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Compliance.Classification.DataClassification> Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.ResponseHeadersDataClasses { get; set; }",
"Stage": "Experimental"
},
{
"Member": "System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Compliance.Classification.DataClassification> Microsoft.AspNetCore.Diagnostics.Logging.LoggingRedactionOptions.RouteParameterDataClasses { get; set; }",
"Stage": "Experimental"
}
]
},
{
"Type": "static class Microsoft.AspNetCore.Diagnostics.Latency.RequestCheckpointConstants",
"Stage": "Stable",
Expand Down Expand Up @@ -136,7 +200,7 @@
],
"Properties": [
{
"Member": "System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Compliance.Classification.DataClassification> Microsoft.AspNetCore.Diagnostics.RequestHeadersLogEnricherOptions.Logging.HeadersDataClasses { get; set; }",
"Member": "System.Collections.Generic.IDictionary<string, Microsoft.Extensions.Compliance.Classification.DataClassification> Microsoft.AspNetCore.Diagnostics.Logging.RequestHeadersLogEnricherOptions.HeadersDataClasses { get; set; }",
"Stage": "Experimental"
}
]
Expand Down Expand Up @@ -194,4 +258,4 @@
]
}
]
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,33 @@
"Name": "Microsoft.AspNetCore.Testing, Version=8.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35",
"Types": [
{
"Type": "static class Microsoft.AspNetCore.Testing.ServiceFakesExtensions",
"Type": "static class Microsoft.Extensions.Hosting.ServiceFakesHostExtensions",
"Stage": "Experimental",
"Methods": [
{
"Member": "static System.Net.Http.HttpClient Microsoft.AspNetCore.Testing.ServiceFakesExtensions.CreateClient(this Microsoft.Extensions.Hosting.IHost host, System.Net.Http.HttpMessageHandler? handler = null, System.Func<System.Uri, bool>? addressFilter = null);",
"Member": "static System.Net.Http.HttpClient Microsoft.Extensions.Hosting.ServiceFakesHostExtensions.CreateClient(this Microsoft.Extensions.Hosting.IHost host, System.Net.Http.HttpMessageHandler? handler = null, System.Func<System.Uri, bool>? addressFilter = null);",
"Stage": "Experimental"
},
{
"Member": "static System.Collections.Generic.IEnumerable<System.Uri> Microsoft.AspNetCore.Testing.ServiceFakesExtensions.GetListenUris(this Microsoft.Extensions.Hosting.IHost host);",
"Member": "static System.Collections.Generic.IEnumerable<System.Uri> Microsoft.Extensions.Hosting.ServiceFakesHostExtensions.GetListenUris(this Microsoft.Extensions.Hosting.IHost host);",
"Stage": "Experimental"
},
}
]
},
{
"Type": "static class Microsoft.AspNetCore.Hosting.ServiceFakesWebHostExtensions",
"Stage": "Experimental",
"Methods": [
{
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Testing.ServiceFakesExtensions.ListenHttpOnAnyPort(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder);",
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Hosting.ServiceFakesWebHostExtensions.ListenHttpOnAnyPort(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder);",
"Stage": "Experimental"
},
{
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Testing.ServiceFakesExtensions.ListenHttpsOnAnyPort(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Security.Cryptography.X509Certificates.X509Certificate2? sslCertificate = null);",
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Hosting.ServiceFakesWebHostExtensions.ListenHttpsOnAnyPort(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder, System.Security.Cryptography.X509Certificates.X509Certificate2? sslCertificate = null);",
"Stage": "Experimental"
},
{
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Testing.ServiceFakesExtensions.UseFakeStartup(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder);",
"Member": "static Microsoft.AspNetCore.Hosting.IWebHostBuilder Microsoft.AspNetCore.Hosting.ServiceFakesWebHostExtensions.UseFakeStartup(this Microsoft.AspNetCore.Hosting.IWebHostBuilder builder);",
"Stage": "Experimental"
}
]
Expand Down
51 changes: 50 additions & 1 deletion src/Libraries/Microsoft.AspNetCore.Testing/README.md
Original file line number Diff line number Diff line change
@@ -1 +1,50 @@
README
# Microsoft.AspNetCore.Testing

This package provides test fakes for integration testing of ASP.NET Core applications.

In particular:

- `IWebHostBuilder` extensions to setup the test web app.
- `IHost` extensions to access that test web app.

## Install the package

From the command-line:

```dotnetcli
dotnet add package Microsoft.AspNetCore.Testing
```

Or directly in the C# project file:

```xml
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Testing" Version="[CURRENTVERSION]" />
</ItemGroup>
```

## Usage Example

### Creating a Test Web App

The [`IWebHostBuilder`](https://learn.microsoft.com/dotnet/api/microsoft.aspnetcore.hosting.iwebhostbuilder) extensions can help set up a host for testing.

```csharp
using var host = await FakeHost.CreateBuilder()
.ConfigureWebHost(webHost => webHost.UseFakeStartup().ListenHttpOnAnyPort())
.StartAsync();
```

### Accessing the test Web App

The [`IHost`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.hosting.ihost) extensions can help access the test host that was created above.

```csharp
using var client = host.CreateClient();

var response = await client.GetAsync("/");
```

## Feedback & Contributing

We welcome feedback and contributions in [our GitHub repo](https://github.com/dotnet/extensions).
Original file line number Diff line number Diff line change
Expand Up @@ -5,71 +5,24 @@
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Security.Cryptography.X509Certificates;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Hosting.Server;
using Microsoft.AspNetCore.Hosting.Server.Features;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Shared.Diagnostics;

namespace Microsoft.AspNetCore.Testing;
namespace Microsoft.Extensions.Hosting;

/// <summary>
/// Extension methods supporting Kestrel server unit testing scenarios.
/// </summary>
public static class ServiceFakesExtensions
public static class ServiceFakesHostExtensions
{
private static readonly Func<Uri, bool> _defaultAddressFilter = static _ => true;

/// <summary>
/// Adds an empty Startup class to satisfy ASP.NET check.
/// </summary>
/// <param name="builder">An <see cref="IWebHostBuilder"/> instance.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
public static IWebHostBuilder UseFakeStartup(this IWebHostBuilder builder)
{
return builder.UseStartup<FakeStartup>();
}

/// <summary>
/// Adds Kestrel server instance listening on the given HTTP port.
/// </summary>
/// <param name="builder">An <see cref="IWebHostBuilder"/> instance.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
/// <remarks>When a concrete port is set by caller, it's not further validated if the port is really free.</remarks>
public static IWebHostBuilder ListenHttpOnAnyPort(this IWebHostBuilder builder)
=> Throw.IfNull(builder)
.UseKestrel(options => options.Listen(new IPEndPoint(IPAddress.Loopback, 0)));

/// <summary>
/// Adds Kestrel server instance listening on a random HTTPS port.
/// </summary>
/// <param name="builder">An <see cref="IWebHostBuilder"/> instance.</param>
/// <param name="sslCertificate">An SSL certificate for the port. If null, a self-signed certificate is created and used.</param>
/// <returns>The value of <paramref name="builder"/>.</returns>
/// <remarks>When a concrete port is set by caller, it's not further validated if the port is really free.</remarks>
[SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Dispose objects before losing scope")]
public static IWebHostBuilder ListenHttpsOnAnyPort(this IWebHostBuilder builder, X509Certificate2? sslCertificate = null)
{
sslCertificate ??= FakeSslCertificateFactory.CreateSslCertificate();

return builder
.UseKestrel(options =>
{
options.Listen(new IPEndPoint(IPAddress.Loopback, 0), listenOptions =>
{
_ = listenOptions.UseHttps(sslCertificate);
});
})
.ConfigureServices(services =>
services.Configure<FakeCertificateOptions>(options =>
options.Certificate = sslCertificate));
}

/// <summary>
/// Creates an <see cref="HttpClient"/> to call the hosted application.
/// </summary>
Expand Down
Loading