using LiveRecorder.Domain.Entities; using LiveRecorder.Domain.Enums; using LiveRecorder.Infrastructure.Persistence; using Microsoft.EntityFrameworkCore; namespace LiveRecorder.Tests; public sealed class ShutdownRecoveryDispatchTests : IDisposable { private readonly string _temporaryDirectory = Path.Combine( Path.GetTempPath(), $"liverecorder-shutdown-dispatch-{Guid.NewGuid():N}"); [Fact] public async Task RecoveredProcessingTask_CreatesDurableCompletionDispatch() { Directory.CreateDirectory(_temporaryDirectory); var outputPath = Path.Combine(_temporaryDirectory, "recovered.mp4"); await File.WriteAllBytesAsync(outputPath, [1, 2, 3, 4]); var options = new DbContextOptionsBuilder() .UseInMemoryDatabase($"shutdown-recovery-dispatch-{Guid.NewGuid():N}") .Options; await using var context = new LiveRecorderDbContext(options); var now = DateTimeOffset.UtcNow; var room = new LiveRoom( LivePlatformType.Douyin, "https://live.example/recovery", "recovery-room", "https://live.example/recovery", now.AddHours(-1)); var session = new RecordSession( room.Id, "origin", RecordOutputFormat.Mp4, RecordSaveMode.Segmented, now.AddMinutes(-30)); session.MarkStopped(now, "Application shutdown deferred MP4 finalization."); var task = new RecordTask( room.Id, session.Id, 1, "origin", RecordOutputFormat.Mp4, now.AddMinutes(-30)); task.MarkStarting("https://stream.example/recovery", outputPath, now.AddMinutes(-30)); task.MarkProcessing("Waiting for restart recovery.", now); var result = new RecordResult( task.Id, outputPath, new FileInfo(outputPath).Length, 1_800, null, 0, RecordTaskStatus.Processing, task.ErrorMessage, now); context.AddRange(room, session, task, result); await context.SaveChangesAsync(); Assert.Empty(context.RecordCompletionDispatches); task.MarkCompleted(now.AddMinutes(1), 1_800); session.MarkCompleted(now.AddMinutes(1)); await context.SaveChangesAsync(); var dispatch = await context.RecordCompletionDispatches.SingleAsync(); Assert.Equal(task.Id, dispatch.RecordTaskId); Assert.False(dispatch.UploadDispatched); Assert.Null(dispatch.CompletedAt); } public void Dispose() { if (Directory.Exists(_temporaryDirectory)) { Directory.Delete(_temporaryDirectory, recursive: true); } } }