Implement asynchronous bumper rendering and add integration tests: refactor BumperAsset to support async rendering, introduce BumperRenderBackgroundService for processing, and create TeleWave.Integration.Tests with Testcontainers-Postgres for comprehensive testing of scheduling and bumper generation scenarios. Update documentation and ensure tests skip if Docker is not available.
This commit is contained in:
@@ -0,0 +1,100 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using NSubstitute;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Maintenance.DeleteShowMedia;
|
||||
using TeleWave.Application.Media.Register;
|
||||
using TeleWave.Domain.Broadcast;
|
||||
using TeleWave.Domain.Library;
|
||||
using TeleWave.Domain.Media;
|
||||
using Xunit;
|
||||
|
||||
namespace TeleWave.Integration.Tests;
|
||||
|
||||
[Collection("postgres")]
|
||||
public sealed class TransactionIntegrationTests(PostgresFixture fixture)
|
||||
{
|
||||
[SkippableFact]
|
||||
public async Task RegisterMediaAsset_RollsBack_WhenFilePromoteFails()
|
||||
{
|
||||
Skip.IfNot(fixture.Available, "Docker недоступен");
|
||||
var fileName = $"clip-{Guid.NewGuid():N}.mkv";
|
||||
var storage = Substitute.For<IMediaStorage>();
|
||||
storage
|
||||
.PromoteToOriginalAsync(
|
||||
Arg.Any<MediaSource>(),
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<Guid>(),
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<CancellationToken>()
|
||||
)
|
||||
.Returns(Task.FromException(new IOException("disk full")));
|
||||
|
||||
await using var db = fixture.CreateContext();
|
||||
var handler = new RegisterMediaAssetCommandHandler(db, storage);
|
||||
|
||||
await Assert.ThrowsAsync<IOException>(() =>
|
||||
handler.Handle(
|
||||
new RegisterMediaAssetCommand(fileName, MediaSource.Upload, fileName),
|
||||
CancellationToken.None
|
||||
)
|
||||
);
|
||||
|
||||
// Перенос файла упал — регистрация должна откатиться, чтобы не осталось orphan-строки.
|
||||
await using var verify = fixture.CreateContext();
|
||||
var exists = await verify.MediaAssets.AnyAsync(a => a.OriginalFileName == fileName);
|
||||
Assert.False(exists);
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task DeleteShowMedia_AtomicallyRemovesEntriesAssetsAndEpisodes()
|
||||
{
|
||||
Skip.IfNot(fixture.Available, "Docker недоступен");
|
||||
var channel = Channel.Create("c", $"c-{Guid.NewGuid():N}", DateTimeOffset.UnixEpoch);
|
||||
var show = Show.Create("Show", ShowKind.Series);
|
||||
var asset = MediaAsset.Register("ep.mkv", ".mkv", MediaSource.Upload);
|
||||
asset.MarkReady(TimeSpan.FromMinutes(20), 2, 600, 1920, 1080, "h264", "aac", "assets/x");
|
||||
show.AddEpisode(asset.Id);
|
||||
var entry = ScheduleEntry.Program(
|
||||
channel.Id,
|
||||
asset.Id,
|
||||
DateTimeOffset.UnixEpoch,
|
||||
DateTimeOffset.UnixEpoch.AddMinutes(20),
|
||||
show.Id,
|
||||
0
|
||||
);
|
||||
|
||||
await using (var seed = fixture.CreateContext())
|
||||
{
|
||||
seed.Channels.Add(channel);
|
||||
seed.Shows.Add(show);
|
||||
seed.MediaAssets.Add(asset);
|
||||
seed.ScheduleEntries.Add(entry);
|
||||
await seed.SaveChangesAsync(CancellationToken.None);
|
||||
}
|
||||
|
||||
var storage = Substitute.For<IMediaStorage>();
|
||||
storage
|
||||
.DeleteAssetArtifactsAsync(Arg.Any<Guid>(), Arg.Any<string>(), Arg.Any<CancellationToken>())
|
||||
.Returns(Task.CompletedTask);
|
||||
|
||||
await using (var db = fixture.CreateContext())
|
||||
{
|
||||
var result = await new DeleteShowMediaCommandHandler(db, storage).Handle(
|
||||
new DeleteShowMediaCommand(show.Id),
|
||||
CancellationToken.None
|
||||
);
|
||||
Assert.True(result.IsSuccess);
|
||||
}
|
||||
|
||||
await using var verify = fixture.CreateContext();
|
||||
Assert.False(await verify.MediaAssets.AnyAsync(a => a.Id == asset.Id));
|
||||
Assert.False(await verify.ScheduleEntries.AnyAsync(e => e.MediaAssetId == asset.Id));
|
||||
var reloaded = await verify
|
||||
.Shows.Include(s => s.Episodes)
|
||||
.FirstAsync(s => s.Id == show.Id);
|
||||
Assert.Empty(reloaded.Episodes);
|
||||
await storage
|
||||
.Received()
|
||||
.DeleteAssetArtifactsAsync(asset.Id, ".mkv", Arg.Any<CancellationToken>());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user