using System; using System.Collections.Generic; using System.Linq; using Paca.Mobile.Core; using Xunit; namespace Paca.Tests.Core; public class MediaStorageGarbageCollectorGateTests { [Fact] public void Gate321_MediaStorageGarbageCollector_ProtectsActiveFiles_AndSelectsStaleOnly() { var now = new DateTime(2026, 9, 4, 20, 30, 0); var candidateFiles = new List { // Stale part file from 3 days ago (150 MB) new("C:\\downloads\\movie.mp4.part", 150 * 1024 * 1024, now.AddDays(-3)), // Active part file created 10 minutes ago (50 MB) -> MUST BE PROTECTED! new("C:\\downloads\\active.mp4.part", 50 * 1024 * 1024, now.AddMinutes(-10)), // Normal finished mp4 from 2 days ago -> MUST NOT BE TOUCHED! new("C:\\downloads\\finished.mp4", 500 * 1024 * 1024, now.AddDays(-2)), // Stale tmp fragment from 40 hours ago (80 MB) new("C:\\downloads\\stream_frag.tmp", 80 * 1024 * 1024, now.AddHours(-40)) }; var minAge = TimeSpan.FromHours(24); var orphans = MediaStorageGarbageCollector.FilterOrphanFiles(candidateFiles, minAge, now); Assert.Equal(2, orphans.Count); Assert.Contains(orphans, x => x.Path.EndsWith("movie.mp4.part")); Assert.Contains(orphans, x => x.Path.EndsWith("stream_frag.tmp")); Assert.DoesNotContain(orphans, x => x.Path.EndsWith("active.mp4.part")); Assert.DoesNotContain(orphans, x => x.Path.EndsWith("finished.mp4")); } [Fact] public void Gate322_MediaStorageGarbageCollector_CalculatesReclaimableSpace_Correctly() { var now = new DateTime(2026, 9, 4, 20, 30, 0); var candidateFiles = new List { new("C:\\temp\\a.part", 200 * 1024 * 1024, now.AddDays(-2)), new("C:\\temp\\b.frag", 300 * 1024 * 1024, now.AddDays(-5)) }; var result = MediaStorageGarbageCollector.AnalyzeReclaimableSpace(candidateFiles, TimeSpan.FromHours(24), now); Assert.Equal(2, result.OrphanCount); Assert.Equal(500 * 1024 * 1024, result.TotalReclaimableBytes); Assert.Equal(500.0, result.TotalReclaimableMb); } [Fact] public void Gate323_MediaStorageGarbageCollector_RecognizesAllTemporaryExtensions() { var tempExts = new[] { ".part", ".tmp", ".frag", ".ytdl", ".download" }; foreach (var ext in tempExts) { Assert.True(MediaStorageGarbageCollector.IsTemporaryExtension($"file{ext}")); } Assert.False(MediaStorageGarbageCollector.IsTemporaryExtension("video.mp4")); Assert.False(MediaStorageGarbageCollector.IsTemporaryExtension("audio.mp3")); Assert.False(MediaStorageGarbageCollector.IsTemporaryExtension("subtitles.vtt")); } [Fact] public void Gate324_MediaStorageGarbageCollector_PerformsBatchDeletion_Gracefully() { var filesToDelete = new List { "file1.part", "file2.part", "file3.part" }; var deletedList = new List(); var result = MediaStorageGarbageCollector.ExecuteCleanUpWithDeleter( filesToDelete, deleter: path => { if (path == "file2.part") throw new System.IO.IOException("File in use"); deletedList.Add(path); } ); Assert.Equal(2, result.SuccessCount); Assert.Equal(1, result.FailedCount); Assert.Equal(2, deletedList.Count); Assert.Contains("file2.part", result.FailedFiles[0].Path); } }