Refactor media storage and management functionalities: enhance IMediaStorage interface with manual inbox handling, update FileSystemMediaStorage to support manual file imports, and improve MediaPathResolver for better path management. Extend MediaEndpoints to include new manual inbox features and update frontend components for improved media management experience.
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
using NSubstitute;
|
||||
using TeleWave.Application.Common.Interfaces;
|
||||
using TeleWave.Application.Media.ManualInbox;
|
||||
using TeleWave.Domain.Library;
|
||||
using TeleWave.Domain.Media;
|
||||
using TeleWave.Infrastructure.Persistence;
|
||||
using Xunit;
|
||||
|
||||
namespace TeleWave.Integration.Tests;
|
||||
|
||||
/// <summary>
|
||||
/// Ручной разбор <c>manual/</c>: выбранные файлы уходят из каталога в шоу. Проверяется против
|
||||
/// настоящей БД, потому что вся суть операции — в связке «ассет + серия шоу + файл на диске».
|
||||
/// </summary>
|
||||
[Collection("postgres")]
|
||||
public sealed class ManualInboxIntegrationTests(PostgresFixture fixture)
|
||||
{
|
||||
[SkippableFact]
|
||||
public async Task Import_AddsEpisodesToShow_AndConsumesFiles()
|
||||
{
|
||||
Skip.IfNot(fixture.Available, "Docker недоступен");
|
||||
|
||||
await using var seedDb = fixture.CreateContext();
|
||||
var showId = await SeedShowAsync(seedDb);
|
||||
var tag = Guid.NewGuid().ToString("N")[..8];
|
||||
var first = $"{tag}-s01e01.mkv";
|
||||
var second = $"{tag}-s01e02.mkv";
|
||||
|
||||
var storage = Substitute.For<IMediaStorage>();
|
||||
storage
|
||||
.ListManualInbox(Arg.Any<int>())
|
||||
.Returns(
|
||||
[
|
||||
new IMediaStorage.ManualInboxFile($"Сериал/{first}", first, 1000),
|
||||
new IMediaStorage.ManualInboxFile($"Сериал/{second}", second, 1000),
|
||||
]
|
||||
);
|
||||
|
||||
await using var db = fixture.CreateContext();
|
||||
var result = await new ImportManualInboxCommandHandler(
|
||||
db,
|
||||
storage,
|
||||
Substitute.For<IMediaProcessingQueue>()
|
||||
).Handle(
|
||||
new ImportManualInboxCommand([$"Сериал/{first}", $"Сериал/{second}"], showId),
|
||||
default
|
||||
);
|
||||
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(2, result.Value.Imported);
|
||||
Assert.Empty(result.Value.Failed);
|
||||
await db.SaveChangesAsync();
|
||||
|
||||
// Файлы должны быть перенесены из manual/ — ровно как из обычного inbox.
|
||||
await storage
|
||||
.Received(2)
|
||||
.PromoteToOriginalAsync(
|
||||
MediaSource.ManualInbox,
|
||||
Arg.Any<string>(),
|
||||
Arg.Any<Guid>(),
|
||||
".mkv",
|
||||
Arg.Any<CancellationToken>()
|
||||
);
|
||||
|
||||
await using var verify = fixture.CreateContext();
|
||||
var episodes = verify
|
||||
.Shows.Where(s => s.Id == showId)
|
||||
.SelectMany(s => s.Episodes)
|
||||
.OrderBy(e => e.Position)
|
||||
.ToList();
|
||||
|
||||
Assert.Equal(2, episodes.Count);
|
||||
// Номера сезона и серии разбираются из имени файла, как при обычном добавлении.
|
||||
Assert.Equal(1, episodes[0].Season);
|
||||
Assert.Equal(1, episodes[0].Episode);
|
||||
Assert.Equal(2, episodes[1].Episode);
|
||||
|
||||
// Считаем ассеты своего шоу: база в интеграционных тестах общая, и соседний тест тоже
|
||||
// заводит записи из ручного inbox.
|
||||
var assetIds = episodes.Select(e => e.MediaAssetId).ToList();
|
||||
var assets = verify.MediaAssets.Where(a => assetIds.Contains(a.Id)).ToList();
|
||||
Assert.Equal(2, assets.Count);
|
||||
Assert.All(assets, a => Assert.Equal(MediaSource.ManualInbox, a.Source));
|
||||
Assert.All(assets, a => Assert.Equal(MediaAssetStatus.Pending, a.Status));
|
||||
}
|
||||
|
||||
[SkippableFact]
|
||||
public async Task Import_SkipsUnsupportedAndMissing_WithoutFailingTheBatch()
|
||||
{
|
||||
Skip.IfNot(fixture.Available, "Docker недоступен");
|
||||
|
||||
await using var seedDb = fixture.CreateContext();
|
||||
var showId = await SeedShowAsync(seedDb);
|
||||
|
||||
var tag = Guid.NewGuid().ToString("N")[..8];
|
||||
var good = $"{tag}-ok.mkv";
|
||||
|
||||
var storage = Substitute.For<IMediaStorage>();
|
||||
storage
|
||||
.ListManualInbox(Arg.Any<int>())
|
||||
.Returns(
|
||||
[
|
||||
new IMediaStorage.ManualInboxFile(good, good, 1000),
|
||||
new IMediaStorage.ManualInboxFile("readme.txt", "readme.txt", 10),
|
||||
]
|
||||
);
|
||||
|
||||
await using var db = fixture.CreateContext();
|
||||
var result = await new ImportManualInboxCommandHandler(
|
||||
db,
|
||||
storage,
|
||||
Substitute.For<IMediaProcessingQueue>()
|
||||
).Handle(
|
||||
new ImportManualInboxCommand([good, "readme.txt", "ушёл.mkv"], showId),
|
||||
default
|
||||
);
|
||||
|
||||
// Один взяли, два отклонили — и каждый со своей причиной, а не одной ошибкой на пакет.
|
||||
Assert.True(result.IsSuccess);
|
||||
Assert.Equal(1, result.Value.Imported);
|
||||
Assert.Equal(2, result.Value.Failed.Count);
|
||||
Assert.Contains(result.Value.Failed, f => f.RelativePath == "readme.txt");
|
||||
Assert.Contains(result.Value.Failed, f => f.RelativePath == "ушёл.mkv");
|
||||
}
|
||||
|
||||
private static async Task<Guid> SeedShowAsync(AppDbContext db)
|
||||
{
|
||||
var show = Show.Create($"Сериал {Guid.NewGuid():N}"[..20], ShowKind.Series);
|
||||
db.Shows.Add(show);
|
||||
await db.SaveChangesAsync();
|
||||
return show.Id;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user