Added tests

This commit is contained in:
2026-05-29 13:49:44 +02:00
parent 0d5a6df27b
commit 51dd877a44
29 changed files with 2212 additions and 6 deletions
@@ -0,0 +1,42 @@
using FluentAssertions;
using Microsoft.EntityFrameworkCore;
using Xunit;
namespace Musify.Application.Tests.Infrastructure;
public sealed class SchemaSmokeTests
{
[Fact]
public async Task Schema_creates_and_persists_all_core_entities()
{
await using var ctx = new SqliteTestContext();
var user = TestData.User();
ctx.Database.Users.Add(user);
ctx.Database.UploadIntents.Add(TestData.UploadIntent(user.Id));
ctx.Database.PlayLists.Add(TestData.PlayList(user.Id));
await ctx.Database.SaveChangesAsync(CancellationToken.None);
var verify = ctx.NewContext();
(await verify.Users.CountAsync()).Should().Be(1);
(await verify.UploadIntents.CountAsync()).Should().Be(1);
(await verify.PlayLists.CountAsync()).Should().Be(1);
}
[Fact]
public async Task Transactions_are_supported_under_sqlite()
{
await using var ctx = new SqliteTestContext();
var user = TestData.User();
await using (var tx = await ctx.Database.BeginTransactionAsync(
System.Data.IsolationLevel.Serializable, CancellationToken.None))
{
ctx.Database.Users.Add(user);
await ctx.Database.SaveChangesAsync(CancellationToken.None);
await tx.CommitAsync(CancellationToken.None);
}
(await ctx.NewContext().Users.CountAsync()).Should().Be(1);
}
}
@@ -0,0 +1,44 @@
using Microsoft.Data.Sqlite;
using Microsoft.EntityFrameworkCore;
namespace Musify.Application.Tests.Infrastructure;
public sealed class SqliteTestContext : IAsyncDisposable, IDisposable
{
private readonly SqliteConnection _connection;
public TestDatabase Database { get; }
public SqliteTestContext()
{
_connection = new SqliteConnection("DataSource=:memory:;Foreign Keys=False");
_connection.Open();
var options = new DbContextOptionsBuilder<TestDatabase>()
.UseSqlite(_connection)
.Options;
Database = new TestDatabase(options);
Database.Database.EnsureCreated();
}
public TestDatabase NewContext()
{
var options = new DbContextOptionsBuilder<TestDatabase>()
.UseSqlite(_connection)
.Options;
return new TestDatabase(options);
}
public void Dispose()
{
Database.Dispose();
_connection.Dispose();
}
public async ValueTask DisposeAsync()
{
await Database.DisposeAsync();
await _connection.DisposeAsync();
}
}
@@ -0,0 +1,75 @@
using Musify.Application.Configuration;
using Musify.Domain.Entities;
using Musify.Domain.ValueObjects;
namespace Musify.Application.Tests.Infrastructure;
public static class TestData
{
public const string Bucket = "musify-test";
public static ApplicationStorageConfiguration StorageConfig() => new() { Bucket = Bucket };
public static UploadIntentConfiguration UploadIntentConfig() => new();
public static TrackConfiguration TrackConfig() => new();
public static PlayListConfiguration PlayListConfig() => new();
public static User User(Guid? id = null, string name = "Alice") => new()
{
Id = id ?? Guid.NewGuid(),
Name = name,
NormalizedName = name.ToUpperInvariant(),
};
public static UploadIntent UploadIntent(
Guid userId,
UploadIntentPurpose purpose = UploadIntentPurpose.TrackPicture,
UploadIntentStatus status = UploadIntentStatus.Issued,
DateTime? expiresAt = null,
long? expectedSizeBytes = null,
string objectName = "object.webp",
string? key = null) => new()
{
UserId = userId,
Bucket = Bucket,
Key = key ?? $"temp/{userId}/Tracks/{objectName}",
ObjectName = objectName,
ContentType = "image/webp",
ExpectedSizeBytes = expectedSizeBytes,
Purpose = purpose,
Status = status,
ExpiresAt = expiresAt ?? DateTime.UtcNow.AddMinutes(5),
};
public static Track Track(
string title = "Song",
ProcessingStatus audioStatus = ProcessingStatus.Completed,
LifeCycleStatus lifeCycle = LifeCycleStatus.Active) => new()
{
Title = title,
NormalizedTitle = title.ToUpperInvariant(),
OriginalPictureName = "pic.webp",
OriginalAudioName = "audio.mp3",
AudioTranscodeProcessingStatus = audioStatus,
PicturesProcessingStatus = ProcessingStatus.Completed,
LifeCycleStatus = lifeCycle,
};
public static PlayList PlayList(
Guid userId,
string name = "My PlayList",
LifeCycleStatus lifeCycle = LifeCycleStatus.Active) => new()
{
UserId = userId,
Name = name,
NormalizedName = name.ToUpperInvariant(),
Description = "desc",
OriginalPictureName = "orig.webp",
SmallPictureName = "small.webp",
MediumPictureName = "medium.webp",
LargePictureName = "large.webp",
LifeCycleStatus = lifeCycle,
};
}
@@ -0,0 +1,39 @@
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Storage;
using Musify.Domain.Entities;
using AppIDatabase = Musify.Application.Contracts.IDatabase;
using IDatabaseTransaction = Musify.Application.Contracts.IDatabaseTransaction;
namespace Musify.Application.Tests.Infrastructure;
public sealed class TestDatabase(DbContextOptions<TestDatabase> options) : DbContext(options), AppIDatabase
{
public DbSet<User> Users => Set<User>();
public DbSet<Track> Tracks => Set<Track>();
public DbSet<PlayList> PlayLists => Set<PlayList>();
public DbSet<UserHasTrack> UserHasTracks => Set<UserHasTrack>();
public DbSet<PlayListHasTrack> PlayListHasTracks => Set<PlayListHasTrack>();
public DbSet<Upload> Uploads => Set<Upload>();
public DbSet<UploadIntent> UploadIntents => Set<UploadIntent>();
public async Task<IDatabaseTransaction> BeginTransactionAsync(
System.Data.IsolationLevel isolationLevel, CancellationToken cancellationToken)
{
var transaction = await Database.BeginTransactionAsync(isolationLevel, cancellationToken);
return new TestDatabaseTransaction(transaction);
}
private sealed class TestDatabaseTransaction(IDbContextTransaction inner) : IDatabaseTransaction
{
public Task CommitAsync(CancellationToken cancellationToken = default) =>
inner.CommitAsync(cancellationToken);
public ValueTask DisposeAsync() => inner.DisposeAsync();
}
}