Migrate Bff.Tests to xUnit v3 with MTP support

This commit is contained in:
Damian Hickey 2026-02-18 11:40:22 +01:00
parent 7cec4fe081
commit e6688b20d3
40 changed files with 101 additions and 240 deletions

31
@progress.txt Normal file
View file

@ -0,0 +1,31 @@
## Progress - xUnit v3 with MTP Migration
### Story 1: Upgrade shared test infrastructure (COMPLETED)
- Commit: c85a70f32
- Branch: dh/xunit-v3-mtp
#### Changes Made:
1. **Directory.Packages.props**: Removed xUnit v2 packages (xunit.core, xunit.runner.visualstudio, Xunit.SkippableFact, Verify.Xunit, Serilog.Sinks.XUnit, Meziantou.Extensions.Logging.Xunit, Microsoft.Playwright.Xunit, coverlet.collector). Added xUnit v3 packages (xunit.v3.core, xunit.v3.core.mtp-v2, xunit.v3.extensibility.core, Microsoft.Testing.Extensions.CodeCoverage, Verify.XunitV3, MartinCostello.Logging.XUnit.v3, Microsoft.Playwright.Xunit.v3).
2. **test.props**: Updated to use xunit.v3.core.mtp-v2 and Microsoft.Testing.Extensions.CodeCoverage. Added OutputType=exe and UseMicrosoftTestingPlatformRunner=true for MTP support.
3. **shared/Xunit.Playwright/Xunit.Playwright.csproj**: Changed xunit.core to xunit.v3.extensibility.core (since it's a library, not a test project). Changed Microsoft.Playwright.Xunit to Microsoft.Playwright.Xunit.v3. Removed Serilog.Sinks.XUnit and Xunit.SkippableFact.
4. **shared/Xunit.Playwright/PlaywrightTestBase.cs**: Updated to xUnit v3 APIs. Changed namespace from Microsoft.Playwright.Xunit to Microsoft.Playwright.Xunit.v3. Added using Xunit.v3 for BeforeAfterTestAttribute. Constructor no longer takes ITestOutputHelper (uses TestContext.Current.TestOutputHelper). Changed InitializeAsync/DisposeAsync from Task to ValueTask. Updated BeforeAfterTestAttribute.Before/After signatures to include IXunitTest parameter. Renamed Output to TestOutputHelper.
5. **shared/Xunit.Playwright/IntegrationTestBase.cs**: Same pattern as PlaywrightTestBase - removed ITestOutputHelper from constructor, use TestContext.Current.TestOutputHelper. Renamed Output to TestOutputHelper. Changed Skip.If to Assert.Skip.
6. **shared/Xunit.Playwright/AppHostFixture.cs**: Changed InitializeAsync/DisposeAsync from Task to ValueTask. Changed Skip.If to Assert.Skip.
7. **Deleted files**: Retries directory (RetryableFact.cs, RetryableTestCase.cs, RetryableTestDiscoverer.cs) - incompatible with v3. xunit.runner.json files from IdentityServer test projects.
#### Important Notes for Next Stories:
- `xunit.v3.extensibility.core` is needed for library projects (like Xunit.Playwright) that aren't test projects themselves. `xunit.v3.core` requires OutputType=exe.
- xUnit v3 `BeforeAfterTestAttribute` is in `Xunit.v3` namespace and methods take `(MethodInfo, IXunitTest)` instead of just `(MethodInfo)`.
- `IAsyncLifetime.InitializeAsync()` returns `ValueTask` (not `Task`) in xUnit v3. Same for `DisposeAsync()`.
- `PageTest` from Microsoft.Playwright.Xunit.v3 also uses `ValueTask` for InitializeAsync/DisposeAsync.
- `ITestOutputHelper` is in `Xunit` namespace (not `Xunit.Abstractions`).
- `TestContext.Current.TestOutputHelper!` replaces constructor-injected `ITestOutputHelper`.
- Property renamed from `Output` to `TestOutputHelper` - downstream test classes that reference `Output` will need updating.
- `Skip.If(true, ...)` replaced with `Assert.Skip(...)` (built-in to xUnit v3).
- Test projects (Stories 2-5) still reference old packages and won't build until they are migrated.

View file

@ -0,0 +1,4 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
[assembly: CaptureConsole]

View file

@ -10,13 +10,21 @@ public static class BenchmarkTests
public class Login(Login.Fixture fixture)
: TestBase<Login.Fixture, MultiFrontendBenchmarks>(fixture)
{
public class Fixture : MultiFrontendBenchmarks, IAsyncLifetime;
public class Fixture : MultiFrontendBenchmarks, IAsyncLifetime
{
ValueTask IAsyncLifetime.InitializeAsync() => new ValueTask(InitializeAsync());
ValueTask IAsyncDisposable.DisposeAsync() => new ValueTask(DisposeAsync());
}
}
public class Proxy(Proxy.Fixture fixture)
: TestBase<Proxy.Fixture, SingleFrontendBenchmarks>(fixture)
{
public class Fixture : SingleFrontendBenchmarks, IAsyncLifetime;
public class Fixture : SingleFrontendBenchmarks, IAsyncLifetime
{
ValueTask IAsyncLifetime.InitializeAsync() => new ValueTask(InitializeAsync());
ValueTask IAsyncDisposable.DisposeAsync() => new ValueTask(DisposeAsync());
}
}
//public class Yarp(Yarp.Fixture fixture)

View file

@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
@ -17,7 +17,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="PublicApiGenerator" />
<PackageReference Include="Verify.Xunit" />
<PackageReference Include="Verify.XunitV3" />
<PackageReference Include="RichardSzalay.MockHttp" />
</ItemGroup>

View file

@ -8,13 +8,11 @@ using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Hybrid;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffFrontendIndexTests : BffTestBase
{
public BffFrontendIndexTests(ITestOutputHelper output) : base(output) =>
public BffFrontendIndexTests() : base() =>
// Disable the map to '/' for the test
Bff.MapGetForRoot = false;

View file

@ -5,15 +5,13 @@ using System.Net;
using Duende.Bff.DynamicFrontends;
using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.HttpOverrides;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffFrontendMatchingTests : BffTestBase
{
private static readonly BffFrontendName NoFrontendSelected = BffFrontendName.Parse("no_frontend_selected");
public BffFrontendMatchingTests(ITestOutputHelper output) : base(output)
public BffFrontendMatchingTests() : base()
{
// Add a frontend that should never be matched
AddOrUpdateFrontend(Some.NeverMatchingFrontEnd());

View file

@ -12,13 +12,11 @@ using Duende.Bff.Yarp;
using Duende.IdentityServer.Extensions;
using Microsoft.Extensions.Caching.Hybrid;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffFrontendSigninTests : BffTestBase
{
public BffFrontendSigninTests(ITestOutputHelper output) : base(output) =>
public BffFrontendSigninTests() : base() =>
Bff.OnConfigureApp += app =>
{
app.MapGet("/secret", (HttpContext c) =>

View file

@ -7,11 +7,9 @@ using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.Options;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffOptionsConfigurationTests(ITestOutputHelper output) : BffTestBase(output)
public class BffOptionsConfigurationTests : BffTestBase
{
[Theory]
[MemberData(nameof(AllSetups))]

View file

@ -10,14 +10,13 @@ using Duende.Bff.Configuration;
using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Xunit.Abstractions;
using Resource = Duende.Bff.AccessTokenManagement.Resource;
namespace Duende.Bff.Tests;
public class BffRemoteApiTests : BffTestBase
{
public BffRemoteApiTests(ITestOutputHelper output) : base(output) =>
public BffRemoteApiTests() : base() =>
Bff.OnConfigureBff += bff =>
{
bff.AddRemoteApis();

View file

@ -3,11 +3,9 @@
using Duende.AccessTokenManagement;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffScenarioTests(ITestOutputHelper output) : BffTestBase(output)
public class BffScenarioTests : BffTestBase
{
[Fact]
public async Task When_using_bff_as_host_and_client_credentials_token_manager_with_no_http_context_still_works()

View file

@ -3,13 +3,11 @@
using Duende.Bff.Tests.TestInfra;
using Duende.IdentityServer.Extensions;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class BffWithoutExplicitFrontendTests(ITestOutputHelper output) : BffTestBase(output)
public class BffWithoutExplicitFrontendTests : BffTestBase
{
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
Bff.OnConfigureApp += app =>
{

View file

@ -8,14 +8,12 @@ using Duende.Bff.Tests.Blazor.Components;
using Duende.Bff.Tests.TestInfra;
using Microsoft.EntityFrameworkCore;
using UserSessionDb;
using Xunit.Abstractions;
namespace Bff.Tests.Blazor;
public class BffBlazorTests(ITestOutputHelper testOutputHelper) : BffTestBase(testOutputHelper)
public class BffBlazorTests : BffTestBase
{
private bool _addServerSideSessions = true;
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
Bff.MapGetForRoot = false;
Bff.OnConfigureServices += services =>

View file

@ -6,7 +6,6 @@ using Duende.Bff.DynamicFrontends;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.Extensions.Options;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Configuration;
@ -133,9 +132,9 @@ public class OptionsMonitorExplorationTests
}
public class ConfigBindingTests(ITestOutputHelper output) : BffTestBase(output)
public class ConfigBindingTests : BffTestBase
{
private readonly ITestOutputHelper _output = output;
private readonly ITestOutputHelper _output = TestContext.Current.TestOutputHelper!;
/// <summary>

View file

@ -15,12 +15,12 @@ using Duende.Bff.Internal;
using Duende.Bff.SessionManagement.SessionStore;
using Duende.Bff.SessionManagement.TicketStore;
using Duende.Bff.Yarp;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class ConventionTests(ITestOutputHelper output)
public class ConventionTests
{
private readonly ITestOutputHelper _output = TestContext.Current.TestOutputHelper!;
public static readonly Assembly BffAssembly = typeof(BffBuilder).Assembly;
public static readonly Assembly BffBlazorAssembly = typeof(BffBlazorServerOptions).Assembly;
public static readonly Assembly BffBlazorClientAssembly = typeof(BffBlazorClientOptions).Assembly;
@ -224,7 +224,7 @@ public class ConventionTests(ITestOutputHelper output)
foreach (var failure in failures)
{
output.WriteLine(failure);
_output.WriteLine(failure);
}
failures.ShouldBeEmpty();
@ -240,128 +240,6 @@ public class ConventionTests(ITestOutputHelper output)
return type.IsNestedPrivate || type.IsNotPublic;
}
#nullable disable
[Fact]
public void AccessTokenManagement_is_not_exposed()
{
var accessTokenManagementNamespace = typeof(IClientCredentialsTokenManager).Namespace!;
List<string> errors = new();
foreach (var type in AllTypes)
{
// Only consider public types
if (!type.IsPublic)
{
continue;
}
// Check if the type itself is in the forbidden namespace
if (type.Namespace != null && type.Namespace.StartsWith(accessTokenManagementNamespace))
{
errors.Add($"Type {type.FullName} is public and in forbidden namespace.");
}
// Check public members for forbidden types
var members = type.GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static |
BindingFlags.DeclaredOnly);
foreach (var member in members)
{
switch (member)
{
case MethodInfo method:
// Skip implicit and explicit conversion operators
if (method.Name == "op_Implicit")
{
break;
}
if (IsForbiddenType(method.ReturnType))
{
errors.Add(
$"{type.FullName}.{method.Name} returns forbidden type {method.ReturnType.FullName}");
}
foreach (var param in method.GetParameters())
{
if (IsForbiddenType(param.ParameterType))
{
errors.Add(
$"{type.FullName}.{method.Name} parameter '{param.Name}' is forbidden type {param.ParameterType.FullName}");
}
}
break;
case PropertyInfo prop:
if (IsForbiddenType(prop.PropertyType))
{
errors.Add(
$"{type.FullName}.{prop.Name} property is forbidden type {prop.PropertyType.FullName}");
}
break;
case FieldInfo field:
if (IsForbiddenType(field.FieldType))
{
errors.Add(
$"{type.FullName}.{field.Name} field is forbidden type {field.FieldType.FullName}");
}
break;
case EventInfo evt:
if (IsForbiddenType(evt.EventHandlerType!))
{
errors.Add(
$"{type.FullName}.{evt.Name} event is forbidden type {evt.EventHandlerType.FullName}");
}
break;
}
}
}
if (errors.Any())
{
output.WriteLine("AccessTokenManagement is exposed. Errors found:");
foreach (var error in errors)
{
output.WriteLine(error);
}
errors.ShouldBeEmpty(
"AccessTokenManagement types should not be exposed in BFF public API. Please review the types and ensure they are internal or private.");
}
bool IsForbiddenType(Type t)
{
if (t.Namespace != null && t.Namespace.StartsWith(accessTokenManagementNamespace))
{
return true;
}
if (t.IsGenericType)
{
foreach (var arg in t.GetGenericArguments())
{
if (IsForbiddenType(arg))
{
return true;
}
}
}
if (t.IsArray)
{
return IsForbiddenType(t.GetElementType()!);
}
return false;
}
}
#nullable enable
private static List<Type> GetStrongTypedStringTypes()
{
// Find all types implementing IStringValue<TSelf>
@ -413,7 +291,7 @@ public class ConventionTests(ITestOutputHelper output)
foreach (var failure in failures)
{
output.WriteLine(failure);
_output.WriteLine(failure);
}
failures.ShouldBeEmpty();

View file

@ -3,11 +3,9 @@
using Duende.Bff.DynamicFrontends;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Diagnostics;
public class FrontendCountDiagnosticEntryTests(ITestOutputHelper testOutputHelper) : BffTestBase(testOutputHelper)
public class FrontendCountDiagnosticEntryTests : BffTestBase
{
[Fact]
public async Task Should_print_the_number_of_frontends_during_defined_interval()

View file

@ -6,13 +6,11 @@ using Duende.Bff.AccessTokenManagement;
using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints;
public class DPoPRemoteEndpointTests(ITestOutputHelper output) : BffTestBase(output)
public class DPoPRemoteEndpointTests : BffTestBase
{
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
var idSrvClient = IdentityServer.AddClient(The.ClientId, Bff.Url());

View file

@ -7,13 +7,12 @@ using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints;
public class DPoPTestsWithManualAuthentication(ITestOutputHelper output) : BffTestBase(output), IAsyncLifetime
public class DPoPTestsWithManualAuthentication : BffTestBase, IAsyncLifetime
{
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
Bff.EnableBackChannelHandler = false;
var idSrvClient = IdentityServer.AddClient(The.ClientId, Bff.Url());

View file

@ -6,11 +6,9 @@ using Duende.Bff.DynamicFrontends;
using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.Authentication;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints;
public class LocalEndpointTests(ITestOutputHelper output) : BffTestBase(output)
public class LocalEndpointTests : BffTestBase
{
public HttpStatusCode LocalApiResponseStatus { get; set; } = HttpStatusCode.OK;

View file

@ -5,18 +5,17 @@ using System.Net;
using Duende.Bff.DynamicFrontends;
using Duende.Bff.SessionManagement.SessionStore;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints.Management;
public class BackchannelLogoutEndpointTests : BffTestBase
{
public BackchannelLogoutEndpointTests(ITestOutputHelper output) : base(output) => Bff.OnConfigureBff += bff =>
public BackchannelLogoutEndpointTests() : base() => Bff.OnConfigureBff += bff =>
{
bff.AddServerSideSessions();
};
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
await base.InitializeAsync();

View file

@ -4,13 +4,11 @@
using System.Net;
using Duende.Bff.Configuration;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints.Management;
public class LoginEndpointTests(ITestOutputHelper output) : BffTestBase(output)
public class LoginEndpointTests : BffTestBase
{
public override async Task InitializeAsync()
public override async ValueTask InitializeAsync()
{
await base.InitializeAsync();
Bff.BffOptions.ConfigureOpenIdConnectDefaults = opt => { The.DefaultOpenIdConnectConfiguration(opt); };

View file

@ -6,11 +6,9 @@ using System.Security.Claims;
using Duende.Bff.Tests.TestInfra;
using Duende.IdentityModel;
using Microsoft.AspNetCore.Authentication;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints.Management;
public class LogoutEndpointTests(ITestOutputHelper output) : BffTestBase(output)
public class LogoutEndpointTests : BffTestBase
{
[Theory]
[MemberData(nameof(AllSetups))]

View file

@ -4,11 +4,9 @@
using System.Net;
using Duende.Bff.Configuration;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints.Management;
public class ManagementBasePathTests(ITestOutputHelper output) : BffTestBase(output)
public class ManagementBasePathTests : BffTestBase
{
[Theory]
[InlineData(Constants.ManagementEndpoints.Login, HttpStatusCode.Redirect)]

View file

@ -10,14 +10,12 @@ using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints.Management;
public class UserEndpointTests : BffTestBase
{
public UserEndpointTests(ITestOutputHelper output) : base(output) =>
public UserEndpointTests() : base() =>
Bff.OnConfigureApp += app =>
{
// Setup a login endpoint that allows you to simulate signing in as a specific

View file

@ -14,7 +14,6 @@ using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using Microsoft.AspNetCore.Authentication;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Xunit.Abstractions;
using Yarp.ReverseProxy.Forwarder;
using Yarp.ReverseProxy.Transforms;
using Yarp.ReverseProxy.Transforms.Builder;
@ -24,7 +23,7 @@ namespace Duende.Bff.Tests.Endpoints;
public class RemoteEndpointTests : BffTestBase
{
public RemoteEndpointTests(ITestOutputHelper output) : base(output)
public RemoteEndpointTests() : base()
{
Bff.OnConfigureServices += services =>
{

View file

@ -3,11 +3,9 @@
using System.Net;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Endpoints;
public class WireupTests(ITestOutputHelper output) : BffTestBase(output)
public class WireupTests : BffTestBase
{
[Fact]
public async Task Without_auto_wireup_management_endpoints_are_not_mapped()

View file

@ -7,14 +7,13 @@ using Duende.Bff.DynamicFrontends;
using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Xunit.Abstractions;
using Yarp.ReverseProxy.Configuration;
namespace Duende.Bff.Tests.Endpoints;
public class YarpTests : BffTestBase
{
public YarpTests(ITestOutputHelper output) : base(output) =>
public YarpTests() : base() =>
Bff.OnConfigureApp += app =>
{
app.MapReverseProxy(proxyApp =>

View file

@ -6,11 +6,9 @@ using Duende.Bff.EntityFramework.Internal;
using Duende.Bff.SessionManagement.SessionStore;
using Duende.Bff.Tests.TestInfra;
using Microsoft.EntityFrameworkCore;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.BffHostBuilder;
public class HostBuilder_SessionTests(ITestOutputHelper output) : BffTestBase(output)
public class HostBuilder_SessionTests : BffTestBase
{
private string _databaseName = Guid.NewGuid().ToString();
@ -167,7 +165,7 @@ public class HostBuilder_SessionTests(ITestOutputHelper output) : BffTestBase(ou
};
IdentityServer.AddClient(The.ClientId, Bff.Url());
var exception = await Should.ThrowAsync<InvalidOperationException>(InitializeAsync);
var exception = await Should.ThrowAsync<InvalidOperationException>(() => InitializeAsync().AsTask());
exception.Message.ShouldBe("No IUserSessionStoreCleanup is registered. Did you add session storage, such as EntityFramework?");
}

View file

@ -8,7 +8,6 @@ using Duende.Bff.Tests.TestInfra;
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
using UserSessionDb;
using Xunit.Abstractions;
namespace Duende.Bff.EntityFramework.Tests;
@ -25,8 +24,9 @@ public class UserSessionStoreTests : IAsyncLifetime
private UserSessionKey invalidUserSessionKey => new UserSessionKey(The.PartitionKey, UserKey.Parse("wrong"));
public UserSessionStoreTests(ITestOutputHelper output)
public UserSessionStoreTests()
{
var output = TestContext.Current.TestOutputHelper!;
var services = new ServiceCollection();
_fakeHttpContextAccessor = new FakeHttpContextAccessor();
_dbFilePath = Path.Combine(Path.GetTempPath(), $"test-{Guid.NewGuid():N}.sqlite");
@ -839,12 +839,12 @@ public class UserSessionStoreTests : IAsyncLifetime
});
}
public async Task InitializeAsync() =>
public async ValueTask InitializeAsync() =>
// Ensure the database is created and migrations are applied
await _database.Database.MigrateAsync();
public async Task DisposeAsync()
public async ValueTask DisposeAsync()
{
// Close all open SQLite connections used by EF
var dbConn = _database.Database.GetDbConnection();

View file

@ -5,13 +5,11 @@ using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.AspNetCore.HttpOverrides;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Headers;
public class ApiAndBffUseForwardedHeaders : BffTestBase, IAsyncLifetime
{
public ApiAndBffUseForwardedHeaders(ITestOutputHelper output) : base(output)
public ApiAndBffUseForwardedHeaders() : base()
{
Bff.OnConfigureApp += app =>
{

View file

@ -5,13 +5,11 @@ using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.AspNetCore.HttpOverrides;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.Headers;
public class ApiUseForwardedHeaders : BffTestBase
{
public ApiUseForwardedHeaders(ITestOutputHelper output) : base(output)
public ApiUseForwardedHeaders() : base()
{
Bff.OnConfigureBff += bff => bff.AddRemoteApis();

View file

@ -6,12 +6,11 @@ using Duende.Bff.AccessTokenManagement;
using Duende.Bff.Tests.TestFramework;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Xunit.Abstractions;
using ApiHost = Duende.Bff.Tests.TestInfra.ApiHost;
namespace Duende.Bff.Tests.Headers;
public class GeneralTests(ITestOutputHelper output) : BffTestBase(output)
public class GeneralTests : BffTestBase
{
[Theory, MemberData(nameof(AllSetups))]
public async Task local_endpoint_should_receive_standard_headers(BffSetupType setup)

View file

@ -6,8 +6,6 @@ using Duende.Bff.Internal;
using Duende.Bff.Tests.TestInfra;
using Duende.Bff.Yarp;
using Microsoft.Extensions.Logging.Abstractions;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
/// <summary>
@ -17,7 +15,7 @@ public class IAccessTokenRetriever_Extensibility_tests : BffTestBase
{
private ContextCapturingAccessTokenRetriever _customAccessTokenReceiver { get; } = new(NullLogger<DefaultAccessTokenRetriever>.Instance);
public IAccessTokenRetriever_Extensibility_tests(ITestOutputHelper output) : base(output)
public IAccessTokenRetriever_Extensibility_tests() : base()
{
IdentityServer.AddClient(The.ClientId, Bff.Url());
Bff.OnConfigureBff += bff => bff

View file

@ -3,11 +3,9 @@
using Duende.Bff.Licensing;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
namespace Duende.Bff.Tests;
public class LicensingTests(ITestOutputHelper output) : BffTestBase(output)
public class LicensingTests : BffTestBase
{
[Fact]
public async Task Given_no_license_then_error_log()

View file

@ -6,7 +6,6 @@ using Duende.Bff.Configuration;
using Duende.Bff.DynamicFrontends;
using Duende.Bff.DynamicFrontends.Internal;
using Duende.Bff.Tests.TestInfra;
using Xunit.Abstractions;
using TestLoggerProvider = Duende.Bff.Tests.TestFramework.TestLoggerProvider;
namespace Duende.Bff.Tests.MultiFrontend;
@ -19,8 +18,9 @@ public class FrontendSelectorTests
private static readonly TestData The = new();
internal TestDataBuilder Some => new(The);
private readonly StringBuilder _logMessages = new();
private readonly ITestOutputHelper _output = TestContext.Current.TestOutputHelper!;
public FrontendSelectorTests(ITestOutputHelper output)
public FrontendSelectorTests()
{
_frontendCollection = new(plugins: [],
bffConfiguration: TestOptionsMonitor.Create(new BffConfiguration()),
@ -28,7 +28,7 @@ public class FrontendSelectorTests
);
var testLoggerProvider = new TestLoggerProvider((s) =>
{
output.WriteLine(s);
_output.WriteLine(s);
_logMessages.AppendLine(s);
}, "", forceToWriteOutput: true);
var loggerFactory = new LoggerFactory([testLoggerProvider]);

View file

@ -6,16 +6,13 @@ using Duende.Bff.SessionManagement.TicketStore;
using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.SessionManagement;
public class CookieSlidingTests : BffTestBase
{
private InMemoryUserSessionStore _sessionStore => (InMemoryUserSessionStore)Bff.Resolve<IUserSessionStore>();
public CookieSlidingTests(ITestOutputHelper output) : base(output) =>
public CookieSlidingTests() : base() =>
Bff.OnConfigureBff += bff =>
bff.AddServerSideSessions();

View file

@ -3,11 +3,9 @@
using Duende.Bff.Tests.TestInfra;
using Duende.IdentityServer.Stores;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.SessionManagement;
public class RevokeRefreshTokenTests(ITestOutputHelper output) : BffTestBase(output)
public class RevokeRefreshTokenTests : BffTestBase
{
[Theory, MemberData(nameof(AllSetups))]
public async Task logout_should_revoke_refreshtoken(BffSetupType setup)

View file

@ -5,13 +5,11 @@ using Duende.Bff.DynamicFrontends;
using Duende.Bff.SessionManagement.SessionStore;
using Duende.Bff.Tests.TestInfra;
using Microsoft.AspNetCore.Authentication.Cookies;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.SessionManagement;
public class ServerSideTicketStoreTests : BffTestBase
{
public ServerSideTicketStoreTests(ITestOutputHelper output) : base(output) =>
public ServerSideTicketStoreTests() : base() =>
Bff.OnConfigureBff += bff =>
{
bff.AddServerSideSessions();

View file

@ -5,8 +5,6 @@ using System.Security.Claims;
using Duende.Bff.DynamicFrontends;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.TestInfra;
public abstract class BffTestBase : IAsyncDisposable
@ -22,9 +20,9 @@ public abstract class BffTestBase : IAsyncDisposable
private readonly List<BffFrontend> _frontendBuffer = new();
protected BffTestBase(ITestOutputHelper output)
protected BffTestBase()
{
Context = new TestHostContext(output);
Context = new TestHostContext(TestContext.Current.TestOutputHelper!);
IdentityServer = new IdentityServerTestHost(Context);
The = Context.The;
@ -131,7 +129,7 @@ public abstract class BffTestBase : IAsyncDisposable
protected IdentityServerTestHost IdentityServer { get; }
protected SimulatedInternet Internet => Context.Internet;
public virtual async Task InitializeAsync()
public virtual async ValueTask InitializeAsync()
{
if (_initialized)
{
@ -177,7 +175,7 @@ public abstract class BffTestBase : IAsyncDisposable
GC.SuppressFinalize(this);
}
public async Task DisposeAsync() => await ((IAsyncDisposable)this).DisposeAsync();
public async ValueTask DisposeAsync() => await ((IAsyncDisposable)this).DisposeAsync();
protected void AddOrUpdateFrontend(BffFrontend frontend)
{

View file

@ -3,8 +3,6 @@
using System.Diagnostics;
using System.Text;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.TestInfra;
public record TestHostContext(ITestOutputHelper OutputHelper)

View file

@ -3,11 +3,9 @@
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Xunit.Abstractions;
namespace Duende.Bff.Tests.TestInfra;
public class TestInfraTests(ITestOutputHelper output) : BffTestBase(output)
public class TestInfraTests : BffTestBase
{
[Fact]
public async Task Can_login_to_identity_server()