- Directory.Packages.props: xunit 2.9.3 -> xunit.v3 4.0.0, bump xunit.runner.visualstudio to the matching 4.0.0 - All 5 test csproj: xunit -> xunit.v3 package reference, add <OutputType>Exe</OutputType> (v3 test projects are standalone executables) - backend/global.json: opt dotnet test into the new MTP-native mode (.NET 10 SDK removed the old VSTest bridge for MTP-based test projects like xunit v3) - IAsyncLifetime implementations (HandlerTestBase, ApiTestFixture, InfrastructureTestFixture, StorageServiceTests): v3's IAsyncLifetime now IS IAsyncDisposable, so InitializeAsync/DisposeAsync return ValueTask instead of Task. ApiTestFixture specifically now overrides WebApplicationFactory.DisposeAsync instead of implementing IAsyncLifetime.DisposeAsync explicitly, since v3 collapses both to the same single DisposeAsync slot - Replaced CancellationToken.None with TestContext.Current.CancellationToken across the suite (a v3-only API) and added it to every call that was silently omitting a CancellationToken argument, per the new xUnit1051 analyzer warnings that came with the v3 package Verified: dotnet build (0 warnings, 0 errors) and dotnet test, 250/250 passing under the new MTP runner.
103 lines
4.2 KiB
C#
103 lines
4.2 KiB
C#
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging.Abstractions;
|
|
using Musify.Application.Configuration;
|
|
using Musify.Application.Contracts;
|
|
using Musify.Infrastructure.Configuration;
|
|
using Musify.Infrastructure.Services;
|
|
using Musify.Infrastructure.Tests.TestSupport;
|
|
using Xunit;
|
|
|
|
namespace Musify.Infrastructure.Tests.Services;
|
|
|
|
[Collection(InfrastructureCollection.Name)]
|
|
public sealed class PlayListPresetSeederTests(InfrastructureTestFixture fixture)
|
|
{
|
|
private static PlayListConfiguration NewConfiguration(bool overwritePresetPictures) => new()
|
|
{
|
|
Routes = new PlayListRoutes { ParentFolders = $"PlayLists-{Guid.NewGuid():N}" },
|
|
SeedPresetPictures = true,
|
|
OverwritePresetPictures = overwritePresetPictures
|
|
};
|
|
|
|
private (PlayListPresetSeeder Seeder, IStorageService Storage) Build(PlayListConfiguration configuration)
|
|
{
|
|
var services = new ServiceCollection();
|
|
services.AddSingleton(configuration);
|
|
services.AddSingleton(new ApplicationStorageConfiguration { Bucket = InfrastructureTestFixture.S3Bucket });
|
|
services.AddSingleton<IStorageService>(_ => new StorageService(
|
|
fixture.CreateDatabase(),
|
|
fixture.CreateS3Client(),
|
|
NullLogger<StorageService>.Instance,
|
|
new InfrastructureStorageConfiguration
|
|
{
|
|
Address = fixture.S3ServiceUrl,
|
|
AccessKey = InfrastructureTestFixture.S3AccessKey,
|
|
SecretAccessKey = InfrastructureTestFixture.S3SecretKey,
|
|
ForcePathStyle = true,
|
|
UseHttp = true,
|
|
}));
|
|
services.AddSingleton<IPictureService>(new PictureService(NullLogger<PictureService>.Instance));
|
|
|
|
var provider = services.BuildServiceProvider();
|
|
var seeder = new PlayListPresetSeeder(provider.GetRequiredService<IServiceScopeFactory>(), NullLogger<PlayListPresetSeeder>.Instance);
|
|
return (seeder, provider.GetRequiredService<IStorageService>());
|
|
}
|
|
|
|
private static async Task RunToCompletionAsync(PlayListPresetSeeder seeder)
|
|
{
|
|
await seeder.StartAsync(TestContext.Current.CancellationToken);
|
|
await (seeder.ExecuteTask ?? Task.CompletedTask);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExecuteAsync_SeedsAllFourPresetPictures()
|
|
{
|
|
var configuration = NewConfiguration(overwritePresetPictures: false);
|
|
var (seeder, storage) = Build(configuration);
|
|
|
|
await RunToCompletionAsync(seeder);
|
|
|
|
var routes = configuration.Routes;
|
|
foreach (var key in new[]
|
|
{
|
|
routes.BuildOriginalPicturePath(routes.PresetOriginalPicture),
|
|
routes.PresetSmallPicturePath,
|
|
routes.PresetMediumPicturePath,
|
|
routes.PresetLargePicturePath
|
|
})
|
|
{
|
|
var metadata = await storage.HeadObjectAsync(InfrastructureTestFixture.S3Bucket, key, TestContext.Current.CancellationToken);
|
|
Assert.True(metadata is not null, $"expected {key} to have been seeded");
|
|
}
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExecuteAsync_RunTwiceWithoutOverwrite_StaysIdempotent()
|
|
{
|
|
var configuration = NewConfiguration(overwritePresetPictures: false);
|
|
var (firstSeeder, storage) = Build(configuration);
|
|
await RunToCompletionAsync(firstSeeder);
|
|
|
|
var (secondSeeder, _) = Build(configuration);
|
|
await RunToCompletionAsync(secondSeeder);
|
|
|
|
var metadata = await storage.HeadObjectAsync(
|
|
InfrastructureTestFixture.S3Bucket, configuration.Routes.PresetSmallPicturePath, TestContext.Current.CancellationToken);
|
|
Assert.NotNull(metadata);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ExecuteAsync_SeedPresetPicturesDisabled_UploadsNothing()
|
|
{
|
|
var configuration = NewConfiguration(overwritePresetPictures: false);
|
|
configuration.SeedPresetPictures = false;
|
|
var (seeder, storage) = Build(configuration);
|
|
|
|
await RunToCompletionAsync(seeder);
|
|
|
|
var metadata = await storage.HeadObjectAsync(
|
|
InfrastructureTestFixture.S3Bucket, configuration.Routes.PresetSmallPicturePath, TestContext.Current.CancellationToken);
|
|
Assert.Null(metadata);
|
|
}
|
|
}
|