1
0
Fork 0
Everything2Everything/src/Everything2Everything.Tests/E2EUserScenarioTests.cs

151 lines
6.3 KiB
C#

using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using Everything2Everything.App.ViewModels;
using Everything2Everything.Core;
using Everything2Everything.Core.Providers;
using Everything2Everything.Core.Filters;
using Xunit;
namespace Everything2Everything.Tests;
/// <summary>
/// 복합 사용자 유즈케이스 E2E 시나리오 TDD 테스트 스위트
/// 실제 사용자의 큐 조작, 포맷 교집합 필터링, 인코딩 옵션 튜닝, 진행률 클램프 및 취소 시나리오를 검증한다.
/// </summary>
public class E2EUserScenarioTests
{
private readonly ConversionEngine _engine;
public E2EUserScenarioTests()
{
_engine = Everything2EverythingBootstrap.CreateDefault();
}
[Fact]
public void Scenario1_FileQueue_ComputesCommonFormatIntersection_AndSortsCorrectly()
{
// 사용자가 .png 이미지와 .docx 문서를 함께 드래그 & 드롭했을 때,
// 사이드바의 출력 포맷 콤보박스는 두 입력이 "동시에 변환 가능한 출력의 교집합"만 남겨야 한다.
var pngOutputs = _engine.Providers.OutputsForInput(".png").ToHashSet(StringComparer.OrdinalIgnoreCase);
var docxOutputs = _engine.Providers.OutputsForInput(".docx").ToHashSet(StringComparer.OrdinalIgnoreCase);
// 교집합 계산
var intersection = pngOutputs.Intersect(docxOutputs, StringComparer.OrdinalIgnoreCase).ToList();
Assert.NotEmpty(intersection);
// PDF, JPG 등은 PNG와 DOCX 양쪽 모두에서 공통 출력 가능해야 함
Assert.Contains(".pdf", intersection);
Assert.Contains(".jpg", intersection);
// 만약 여기에 다른 포맷(.csv 등)이 추가되면 교집합이 변하거나 비어있어야 함
var csvOutputs = _engine.Providers.OutputsForInput(".csv").ToHashSet(StringComparer.OrdinalIgnoreCase);
var threeWayIntersection = intersection.Intersect(csvOutputs, StringComparer.OrdinalIgnoreCase).ToList();
// CSV는 텍스트/표 기반이므로 이미지/PDF와의 교집합은 없어야 함
Assert.DoesNotContain(".jpg", threeWayIntersection);
}
[Fact]
public void Scenario2_OptionsViewModel_Tuning_ReflectsInEncoderParameters()
{
// 사용자가 슬라이더와 콤보박스로 인코딩 옵션을 조정하는 시나리오
var vm = new OptionsViewModel
{
Quality = 92,
Crf = 18,
ResolutionIndex = 3, // 1080p
AudioBitrateIndex = 4, // 320 kbps
VideoPreferGpu = false
};
Assert.Equal(92, vm.Quality);
Assert.Equal(18, vm.Crf);
Assert.Equal(3, vm.ResolutionIndex);
Assert.Equal(4, vm.AudioBitrateIndex);
Assert.False(vm.VideoPreferGpu);
// ConvertOptions 에 매핑되는 로직 검증
var options = vm.ToConvertOptions();
Assert.Equal(92, options.Jpeg.Quality);
Assert.Equal(92, options.Webp.Quality);
Assert.Equal(18, options.Video.Crf);
Assert.False(options.VideoPreferGpu);
Assert.Equal(1920, options.Video.ScaleWidth);
}
[Theory]
[InlineData(-10.0, 0.0)]
[InlineData(0.0, 0.0)]
[InlineData(45.5, 45.5)]
[InlineData(100.0, 100.0)]
[InlineData(125.0, 100.0)]
public void Scenario3_ProgressReporting_ClampsValuesCleanlyBetween0And100(double rawValue, double expectedClamped)
{
// 백그라운드 인코더에서 비정상적인 퍼센트(-5% 또는 105%)가 전달되더라도
// UI 프로그레스 바가 깨지지 않도록 [0.0, 100.0] 범위로 정밀 클램프되어야 한다.
double clamped = Math.Clamp(rawValue, 0.0, 100.0);
Assert.Equal(expectedClamped, clamped);
}
[Fact]
public void Scenario4_ConversionGraph_FindBestPath_ProvidesValidSteps()
{
// 다단계 변환 그래프 탐색 유즈케이스 검증
var path = _engine.Providers.Graph.FindBestPath(".png", ".jpg");
Assert.NotNull(path);
Assert.NotEmpty(path);
Assert.Equal(".png", path![0].From);
}
[Fact]
public void Scenario5_MultiHopTransitive_FindBestPath_FindsValidIntermediateHops()
{
// 직접적인 단일 Provider가 없더라도 유효한 2홉 변환 경로를 찾아내는지 검증
// 예: .md -> .html -> .png
var path = _engine.Providers.Graph.FindBestPath(".md", ".png");
Assert.NotNull(path);
Assert.True(path!.Count >= 2, "MD to PNG should traverse at least 2 hops (.md -> .html/.pdf -> .png)");
Assert.Equal(".md", path[0].From);
Assert.Equal(".png", path[^1].To);
// 중간 홉이 불가능한 도메인 도약(예: .docx)을 포함하지 않는지 검증
foreach (var hop in path)
{
Assert.True(MediaConversionNegotiator.CanConvert(hop.From, hop.To),
$"Hop {hop.From} -> {hop.To} must satisfy domain negotiation rules");
}
}
[Theory]
[InlineData(1000, 200, 80.0)] // 1000B -> 200B (80% 절감)
[InlineData(1000, 1000, 0.0)] // 동일 크기 (0% 절감)
[InlineData(1000, 1500, -50.0)] // 크기 증가 (-50% 절감)
[InlineData(0, 500, 0.0)] // 0바이트 원본 예외 방어 (DivideByZero 방어)
public void Scenario6_StorageSavingsCalculation_HandlesAllEdgeCasesSafely(long originalBytes, long outputBytes, double expectedSavings)
{
double savings = ComputeStorageSavings(originalBytes, outputBytes);
Assert.Equal(expectedSavings, savings, precision: 1);
}
private static double ComputeStorageSavings(long orig, long @out)
{
if (orig <= 0) return 0.0;
return ((double)(orig - @out) / orig) * 100.0;
}
[Fact]
public void Scenario7_MediaConversionNegotiator_StrictRejection_AcrossAllCrossDomains()
{
// 텍스트/표/오디오/이미지/비디오 간의 부적절한 도약 전수 거부 시나리오
Assert.False(MediaConversionNegotiator.CanConvert(".png", ".docx"));
Assert.False(MediaConversionNegotiator.CanConvert(".jpg", ".xlsx"));
Assert.False(MediaConversionNegotiator.CanConvert(".mp3", ".gif"));
Assert.False(MediaConversionNegotiator.CanConvert(".csv", ".mp4"));
Assert.False(MediaConversionNegotiator.CanConvert(".docx", ".mp3"));
Assert.False(MediaConversionNegotiator.CanConvert(".xlsx", ".png"));
}
}