Add editorconfig and analyzers, format backend code

This commit is contained in:
2026-09-24 04:54:17 +02:00
parent 87c3f97529
commit c8fe28b07b
367 changed files with 12179 additions and 12459 deletions
@@ -11,92 +11,91 @@ using NSubstitute;
using Testcontainers.PostgreSql;
using Xunit;
namespace Musify.Api.Tests.TestSupport
namespace Musify.Api.Tests.TestSupport;
public sealed class ApiTestFixture : WebApplicationFactory<Program>, IAsyncLifetime
{
public sealed class ApiTestFixture : WebApplicationFactory<Program>, IAsyncLifetime
private readonly PostgreSqlContainer postgres = new PostgreSqlBuilder("postgres:17-alpine")
.WithDatabase("musify_api_test")
.WithUsername("postgres")
.WithPassword("postgres")
.Build();
public IStorageService StorageService { get; } = Substitute.For<IStorageService>();
public IEventBus EventBus { get; } = Substitute.For<IEventBus>();
public IStreamTicketService StreamTicketService { get; } = Substitute.For<IStreamTicketService>();
public async ValueTask InitializeAsync()
{
private readonly PostgreSqlContainer postgres = new PostgreSqlBuilder("postgres:17-alpine")
.WithDatabase("musify_api_test")
.WithUsername("postgres")
.WithPassword("postgres")
.Build();
await postgres.StartAsync();
public IStorageService StorageService { get; } = Substitute.For<IStorageService>();
public IEventBus EventBus { get; } = Substitute.For<IEventBus>();
public IStreamTicketService StreamTicketService { get; } = Substitute.For<IStreamTicketService>();
public async ValueTask InitializeAsync()
{
await postgres.StartAsync();
using var scope = Services.CreateScope();
var database = scope.ServiceProvider.GetRequiredService<Musify.Infrastructure.Persistence.Database>();
await database.Database.MigrateAsync();
}
public override async ValueTask DisposeAsync()
{
await base.DisposeAsync();
await postgres.DisposeAsync();
}
public HttpClient CreateAnonymousClient() => CreateClient();
public HttpClient CreateAuthenticatedClient(long userId, string userName = "test-user")
{
var client = CreateClient();
client.DefaultRequestHeaders.Add(FakeAuthenticationHandler.UserIdHeader, userId.ToString());
client.DefaultRequestHeaders.Add("X-Test-User-Name", userName);
return client;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((_, configuration) =>
{
configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
["Database:ConnectionString"] = postgres.GetConnectionString(),
["Authentication:MetadataAddress"] = "https://auth.musify.test/.well-known/openid-configuration",
["Authentication:IssuerAddress"] = "https://auth.musify.test",
["Authentication:AudienceAddress"] = "musify-api-test",
["Authentication:ClientId"] = "musify-api-test",
["Authentication:AuthorizationEndpoint"] = "https://auth.musify.test/authorize",
["Authentication:TokenEndpoint"] = "https://auth.musify.test/token",
["Authentication:Scopes:0"] = "openid",
["StreamTicket:PrivateKeyPath"] = "unused-in-tests.pem",
["OpenTelemetry:OtlpEndpoint"] = "http://localhost:4317",
["MassTransit:Address"] = "rabbitmq://localhost:5672",
["MassTransit:Username"] = "guest",
["MassTransit:Password"] = "guest",
["InfrastructureStorage:Address"] = "http://localhost:8333",
["InfrastructureStorage:AccessKey"] = "unused-in-tests",
["InfrastructureStorage:SecretAccessKey"] = "unused-in-tests",
["InfrastructureStorage:ForcePathStyle"] = "true",
["InfrastructureStorage:UseHttp"] = "true",
["ApplicationStorage:Bucket"] = "musify-api-test",
["StreamGateway:PublicBaseUrl"] = "http://stream.musify.test",
});
});
builder.ConfigureTestServices(services =>
{
services.Replace(ServiceDescriptor.Singleton(StorageService));
services.Replace(ServiceDescriptor.Scoped(_ => EventBus));
services.Replace(ServiceDescriptor.Singleton(StreamTicketService));
services
.AddAuthentication(FakeAuthenticationHandler.SchemeName)
.AddScheme<AuthenticationSchemeOptions, FakeAuthenticationHandler>(FakeAuthenticationHandler.SchemeName, _ => { });
});
}
using var scope = Services.CreateScope();
var database = scope.ServiceProvider.GetRequiredService<Musify.Infrastructure.Persistence.Database>();
await database.Database.MigrateAsync();
}
[CollectionDefinition(Name)]
public sealed class ApiCollection : ICollectionFixture<ApiTestFixture>
public override async ValueTask DisposeAsync()
{
public const string Name = "Api";
await base.DisposeAsync();
await postgres.DisposeAsync();
}
public HttpClient CreateAnonymousClient() => CreateClient();
public HttpClient CreateAuthenticatedClient(long userId, string userName = "test-user")
{
var client = CreateClient();
client.DefaultRequestHeaders.Add(FakeAuthenticationHandler.UserIdHeader, userId.ToString());
client.DefaultRequestHeaders.Add("X-Test-User-Name", userName);
return client;
}
protected override void ConfigureWebHost(IWebHostBuilder builder)
{
builder.ConfigureAppConfiguration((_, configuration) =>
{
configuration.AddInMemoryCollection(new Dictionary<string, string?>
{
["Database:ConnectionString"] = postgres.GetConnectionString(),
["Authentication:MetadataAddress"] = "https://auth.musify.test/.well-known/openid-configuration",
["Authentication:IssuerAddress"] = "https://auth.musify.test",
["Authentication:AudienceAddress"] = "musify-api-test",
["Authentication:ClientId"] = "musify-api-test",
["Authentication:AuthorizationEndpoint"] = "https://auth.musify.test/authorize",
["Authentication:TokenEndpoint"] = "https://auth.musify.test/token",
["Authentication:Scopes:0"] = "openid",
["StreamTicket:PrivateKeyPath"] = "unused-in-tests.pem",
["OpenTelemetry:OtlpEndpoint"] = "http://localhost:4317",
["MassTransit:Address"] = "rabbitmq://localhost:5672",
["MassTransit:Username"] = "guest",
["MassTransit:Password"] = "guest",
["InfrastructureStorage:Address"] = "http://localhost:8333",
["InfrastructureStorage:AccessKey"] = "unused-in-tests",
["InfrastructureStorage:SecretAccessKey"] = "unused-in-tests",
["InfrastructureStorage:ForcePathStyle"] = "true",
["InfrastructureStorage:UseHttp"] = "true",
["ApplicationStorage:Bucket"] = "musify-api-test",
["StreamGateway:PublicBaseUrl"] = "http://stream.musify.test",
});
});
builder.ConfigureTestServices(services =>
{
services.Replace(ServiceDescriptor.Singleton(StorageService));
services.Replace(ServiceDescriptor.Scoped(_ => EventBus));
services.Replace(ServiceDescriptor.Singleton(StreamTicketService));
services
.AddAuthentication(FakeAuthenticationHandler.SchemeName)
.AddScheme<AuthenticationSchemeOptions, FakeAuthenticationHandler>(FakeAuthenticationHandler.SchemeName, _ => { });
});
}
}
[CollectionDefinition(Name)]
public sealed class ApiCollection : ICollectionFixture<ApiTestFixture>
{
public const string Name = "Api";
}
@@ -4,29 +4,28 @@ using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace Musify.Api.Tests.TestSupport
namespace Musify.Api.Tests.TestSupport;
public sealed class FakeAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
public sealed class FakeAuthenticationHandler(
IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
public const string SchemeName = "Test";
public const string UserIdHeader = "X-Test-User-Id";
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
public const string SchemeName = "Test";
public const string UserIdHeader = "X-Test-User-Id";
if (!Request.Headers.TryGetValue(UserIdHeader, out var userId) || string.IsNullOrEmpty(userId))
return Task.FromResult(AuthenticateResult.NoResult());
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Request.Headers.TryGetValue(UserIdHeader, out var userId) || string.IsNullOrEmpty(userId))
return Task.FromResult(AuthenticateResult.NoResult());
var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, userId!) };
if (Request.Headers.TryGetValue("X-Test-User-Name", out var userName) && !string.IsNullOrEmpty(userName))
claims.Add(new Claim(ClaimTypes.Name, userName!));
var claims = new List<Claim> { new(ClaimTypes.NameIdentifier, userId!) };
if (Request.Headers.TryGetValue("X-Test-User-Name", out var userName) && !string.IsNullOrEmpty(userName))
claims.Add(new Claim(ClaimTypes.Name, userName!));
var identity = new ClaimsIdentity(claims, SchemeName);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), SchemeName);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
var identity = new ClaimsIdentity(claims, SchemeName);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(identity), SchemeName);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
}