using System; using System.IO; using System.Text.RegularExpressions; using Paca.App.Utils; using Paca.Tests.Infrastructure; using Xunit; namespace Paca.Tests.App; public class DownloadFolderOpenGateTests { [Fact] public void Gate480_ExplorerLauncher_Arguments_HaveSelectOutsideQuotes_AndUseShellExecuteTrue() { // DoD: 공백 및 한글/특수문자가 포함된 다운로드 파일명에 대해 // explorer.exe 인자 구성 시 /select, 가 큰따옴표 바깥에 위치하고 UseShellExecute = true 여야 함 var testDir = Path.Combine(Path.GetTempPath(), "PacaTest_Gate480_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(testDir); try { var testFile = Path.Combine(testDir, "(13) 손만 잡아도 2500만 원 순삭_ 베트남 국제결혼의 비밀!.mp4"); File.WriteAllText(testFile, "dummy video data"); var psi = ExplorerLauncher.BuildProcessStartInfo(testFile, testDir); Assert.NotNull(psi); Assert.True(psi.UseShellExecute, "UseShellExecute must be true for Windows Shell explorer invocation"); Assert.Equal("explorer.exe", psi.FileName, ignoreCase: true); // 중요: "/select,C:\..." 형태는 Windows explorer.exe 가 옵션으로 인식하지 못하고 종료코드 1로 실패함 // 올바른 형식: /select,"C:\..." Assert.StartsWith("/select,\"", psi.Arguments); Assert.EndsWith("\"", psi.Arguments); Assert.False(psi.Arguments.StartsWith("\"/select,"), "Arguments must NOT enclose /select within quotes"); } finally { try { Directory.Delete(testDir, true); } catch { } } } [Fact] public void Gate481_ExplorerLauncher_FallbackToDirectory_WhenFileDoesNotExist() { var testDir = Path.Combine(Path.GetTempPath(), "PacaTest_Gate481_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(testDir); try { var nonExistentFile = Path.Combine(testDir, "deleted_video.mp4"); var psi = ExplorerLauncher.BuildProcessStartInfo(nonExistentFile, testDir); Assert.NotNull(psi); Assert.True(psi.UseShellExecute); // 파일이 없으면 상위 폴더(testDir) 자체를 직접 열도록 구성 Assert.Equal(Path.GetFullPath(testDir), Path.GetFullPath(psi.FileName), ignoreCase: true); Assert.True(string.IsNullOrEmpty(psi.Arguments)); } finally { try { Directory.Delete(testDir, true); } catch { } } } [Fact] public void Gate482_ExplorerLauncher_RelativePath_ResolvedWithOutputFolder() { var testDir = Path.Combine(Path.GetTempPath(), "PacaTest_Gate482_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(testDir); try { const string fileName = "relative_clip.mp4"; var fullPath = Path.Combine(testDir, fileName); File.WriteAllText(fullPath, "content"); // 상대 경로가 넘어왔을 때 OutputFolder 기준으로 전체 경로 해석 검증 var psi = ExplorerLauncher.BuildProcessStartInfo(fileName, testDir); Assert.NotNull(psi); Assert.True(psi.UseShellExecute); Assert.Equal("explorer.exe", psi.FileName, ignoreCase: true); Assert.Contains(fullPath, psi.Arguments); Assert.StartsWith("/select,\"", psi.Arguments); } finally { try { Directory.Delete(testDir, true); } catch { } } } [Fact] public void Gate483_OpenInExplorer_SourceCode_DoesNotUseArgumentListWithUseShellExecuteFalse() { // DoD: MainWindow.xaml.cs의 OpenInExplorer가 ArgumentList.Add(/select...) + UseShellExecute = false 조합을 사용하지 않아야 함 var mwCs = File.ReadAllText(SourceTree.AppPath("MainWindow.xaml.cs")); Assert.DoesNotContain("psi.ArgumentList.Add($\"/select,{selectFile}\");", mwCs); Assert.DoesNotContain("new System.Diagnostics.ProcessStartInfo(\"explorer.exe\") { UseShellExecute = false }", mwCs); } [Fact] public void Gate484_ExplorerLauncher_CreatesDirectory_IfOutputFolderDoesNotExist() { var nonExistentDir = Path.Combine(Path.GetTempPath(), "PacaTest_Gate484_AutoCreate_" + Guid.NewGuid().ToString("N")); Assert.False(Directory.Exists(nonExistentDir)); try { var psi = ExplorerLauncher.BuildProcessStartInfo(null, nonExistentDir); Assert.NotNull(psi); Assert.True(Directory.Exists(nonExistentDir), "BuildProcessStartInfo or Launcher must ensure the output folder exists"); } finally { try { Directory.Delete(nonExistentDir, true); } catch { } } } }