최종 감사: 계획↔구현 매핑 문서(IMPLEMENTATION_AUDIT.md), slnx에 Mobile 추가, HlsDownloader._muxer ctor 누락 회귀 RED→GREEN 수정(103 tests), 빌드머신 아티팩트 갱신(linux-x64/osx-arm64)

This commit is contained in:
Yun Chan 2026-08-15 17:20:13 +09:00
parent 432b94fcbe
commit 17f3e41cd2
4 changed files with 214 additions and 0 deletions

View file

@ -0,0 +1,72 @@
using System.Net;
using System.Net.Http;
using VideoDownloader.Core.Config;
using VideoDownloader.Core.Hls;
using VideoDownloader.Core.Models;
using VideoDownloader.Core.Platform;
namespace VideoDownloader.Tests.Platform;
/// <summary>HlsDownloader 가 주입된 IMediaMuxer 를 실제 리먹스 경로에서 사용하는지 검증(RED: ctor 미할당 회귀 방지).</summary>
public class HlsMuxerWiringTests
{
private sealed class RecordingMuxer : IMediaMuxer
{
public int RemuxCalls;
public Task RemuxAsync(string ffmpeg, string input, string output, CancellationToken ct)
{
RemuxCalls++;
File.WriteAllText(output, "remuxed"); // 성공 시뮬레이션
return Task.CompletedTask;
}
public Task DownloadDashAsync(string ffmpeg, Uri mpdUrl, string output, double durationSec,
HttpClient http, IProgress<DownloadProgress>? progress, CancellationToken ct) => Task.CompletedTask;
public string? ResolveFfmpeg(AppConfig config) => "ffmpeg";
}
[Fact]
public async Task HlsDownloader_주입된_뮥서로_리먹스한다()
{
if (!OperatingSystem.IsWindows()) return;
using var server = new HttpListener();
var port = Random.Shared.Next(20000, 60000);
server.Prefixes.Add($"http://127.0.0.1:{port}/");
server.Start();
var seg = new byte[16];
using var listenerTask = Task.Run(async () =>
{
for (var i = 0; i < 3; i++)
{
var ctx = await server.GetContextAsync();
var path = ctx.Request.Url!.AbsolutePath;
var body = path.EndsWith(".m3u8")
? "#EXTM3U\n#EXT-X-TARGETDURATION:2\n#EXTINF:2.0,\nseg0.ts\n#EXTINF:2.0,\nseg1.ts\n#EXT-X-ENDLIST\n"
: null;
var buf = body != null ? System.Text.Encoding.UTF8.GetBytes(body) : seg;
ctx.Response.ContentType = body != null ? "application/vnd.apple.mpegurl" : "video/mp2t";
await ctx.Response.OutputStream.WriteAsync(buf);
ctx.Response.Close();
}
});
var muxer = new RecordingMuxer();
var config = new AppConfig();
var dl = new HlsDownloader(new HttpClient(), config, muxer);
var outBase = Path.Combine(Path.GetTempPath(), $"hls-{Guid.NewGuid():N}");
try
{
var result = await dl.DownloadAsync(new Uri($"http://127.0.0.1:{port}/index.m3u8"), outBase);
Assert.True(muxer.RemuxCalls > 0, "주입한 IMediaMuxer.RemuxAsync 가 호출되어야 한다");
Assert.EndsWith(".mp4", result);
}
finally
{
server.Stop();
try { await listenerTask; } catch { }
foreach (var f in Directory.GetFiles(Path.GetTempPath(), Path.GetFileName(outBase) + "*"))
File.Delete(f);
if (Directory.Exists(outBase + ".parts")) Directory.Delete(outBase + ".parts", true);
}
}
}