Files
TeleWave/backend/tests/TeleWave.Integration.Tests/ManualInboxIntegrationTests.cs
T
Leonid Pershin 6ca629c13a
ci / build-backend (push) Successful in 1m36s
ci / build-frontend (push) Successful in 57s
ci / tests (push) Successful in 1m36s
ci / sonar (push) Successful in 4m40s
Refactor media import handlers to utilize ManualInboxTaker for improved functionality
Updated the ImportManualInboxCommandHandler and ImportMoviesCommandHandler to replace direct storage interactions with the ManualInboxTaker, streamlining the import process. This change enhances code clarity and maintains consistency across media import functionalities. Additionally, refactored related tests to accommodate the new handler structure, ensuring robust testing of the import logic.
2026-07-28 10:46:21 +03:00

177 lines
7.2 KiB
C#

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,
new ManualInboxTaker(db, storage),
Substitute.For<IMediaProcessingQueue>()
).Handle(
new ImportManualInboxCommand(
[Item($"Сериал/{first}"), Item($"Сериал/{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,
new ManualInboxTaker(db, storage),
Substitute.For<IMediaProcessingQueue>()
).Handle(
new ImportManualInboxCommand(
[Item(good), Item("readme.txt"), Item("ушёл.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");
}
[SkippableFact]
public async Task Import_WithExplicitNumbers_UsesThemInsteadOfFileName()
{
Skip.IfNot(fixture.Available, "Docker недоступен");
await using var seedDb = fixture.CreateContext();
var showId = await SeedShowAsync(seedDb);
var name = $"{Guid.NewGuid():N}"[..8] + "-без-номеров.mkv";
var storage = Substitute.For<IMediaStorage>();
storage
.ListManualInbox(Arg.Any<int>())
.Returns([new IMediaStorage.ManualInboxFile(name, name, 1000)]);
await using var db = fixture.CreateContext();
var result = await new ImportManualInboxCommandHandler(
db,
new ManualInboxTaker(db, storage),
Substitute.For<IMediaProcessingQueue>()
).Handle(
new ImportManualInboxCommand([new ManualImportItem(name, 4, 12)], showId),
default
);
Assert.True(result.IsSuccess);
await db.SaveChangesAsync();
await using var verify = fixture.CreateContext();
var episode = verify.Shows.Where(s => s.Id == showId).SelectMany(s => s.Episodes).Single();
// Из имени номера не вытащить — значит сохранилось ровно то, что видел пользователь.
Assert.Equal(4, episode.Season);
Assert.Equal(12, episode.Episode);
// И спутники рядом с забранным файлом убираются.
await storage.Received(1).CleanupManualLeftoversAsync(name, Arg.Any<CancellationToken>());
}
/// <summary>Файл без явных номеров — сервер разбирает имя сам, как при обычном добавлении.</summary>
private static ManualImportItem Item(string relativePath) => new(relativePath, null, null);
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;
}
}