using System; using System.Collections.Generic; using System.IO; using System.Linq; using BulkArchiveMailer.Core; using Xunit; namespace BulkArchiveMailer.Core.Tests { /// /// ArchiveScanner.Scan touches the real file system (Directory.Exists/ /// EnumerateFiles), so these tests set up a throwaway folder under /// Path.GetTempPath() per test and delete it afterwards (IDisposable), /// rather than mocking - the existing precedent in this test project /// (SendLogStoreTests-equivalent file-based tests) already treats this /// as an acceptable pattern for .NET file-system-touching code. /// public class ArchiveScannerTests : IDisposable { private readonly string tempDir; public ArchiveScannerTests() { tempDir = Path.Combine(Path.GetTempPath(), "BulkArchiveMailerTests_" + Guid.NewGuid().ToString("N")); Directory.CreateDirectory(tempDir); } public void Dispose() { try { if (Directory.Exists(tempDir)) { Directory.Delete(tempDir, recursive: true); } } catch { // Best-effort cleanup only - never fail a passing test over // a locked/leftover temp file. } } private static void WriteArchive(string path) => File.WriteAllBytes(path, new byte[] { 1, 2, 3 }); [Fact] public void Scan_LoginMatchesTemplateLoginPattern_IsReady() { var filePath = Path.Combine(tempDir, "a-makarov_ssh_access.7z"); WriteArchive(filePath); var templates = new List { new MailTemplateDefinition { DisplayName = "SSH-доступ (только a-*)", FileSuffix = "ssh_access", Subject = "Доступ — {Login}", BodyHtml = "

{Login}

", LoginPattern = "^a-.*", }, }; var result = ArchiveScanner.Scan(tempDir, "redlab.lo", templates); var job = Assert.Single(result.Jobs); Assert.Equal(SendStatus.Ready, job.Status); Assert.Equal("a-makarov", job.Login); Assert.Equal("a.makarov@redlab.lo", job.Email); Assert.Empty(result.UnrecognizedFiles); } [Fact] public void Scan_LoginDoesNotMatchTemplateLoginPattern_IsErrorInUnrecognized() { var filePath = Path.Combine(tempDir, "b-petrov_ssh_access.7z"); WriteArchive(filePath); var templates = new List { new MailTemplateDefinition { DisplayName = "SSH-доступ (только a-*)", FileSuffix = "ssh_access", Subject = "Доступ — {Login}", BodyHtml = "

{Login}

", LoginPattern = "^a-.*", }, }; var result = ArchiveScanner.Scan(tempDir, "redlab.lo", templates); Assert.Empty(result.Jobs); var row = Assert.Single(result.UnrecognizedFiles); Assert.Equal(SendStatus.Error, row.Status); Assert.Contains("b-petrov", row.ErrorMessage); Assert.Contains("SSH-доступ (только a-*)", row.ErrorMessage); Assert.Contains("^a-.*", row.ErrorMessage); } [Fact] public void Scan_EmptyLoginPattern_MatchesAnyLogin_UnchangedLegacyBehavior() { var filePath = Path.Combine(tempDir, "z-unrelated_ssh_access.7z"); WriteArchive(filePath); var templates = new List { new MailTemplateDefinition { DisplayName = "SSH-доступ", FileSuffix = "ssh_access", Subject = "Доступ — {Login}", BodyHtml = "

{Login}

", LoginPattern = "", }, }; var result = ArchiveScanner.Scan(tempDir, "redlab.lo", templates); var job = Assert.Single(result.Jobs); Assert.Equal(SendStatus.Ready, job.Status); Assert.Equal("z-unrelated", job.Login); } [Fact] public void Scan_InvalidLoginPatternRegex_TreatedAsNoMatchRatherThanThrowing() { var filePath = Path.Combine(tempDir, "a-makarov_ssh_access.7z"); WriteArchive(filePath); var templates = new List { new MailTemplateDefinition { DisplayName = "SSH-доступ (битый regex)", FileSuffix = "ssh_access", Subject = "Доступ — {Login}", BodyHtml = "

{Login}

", // Unbalanced group - not a valid regex. LoginPattern = "(a-", }, }; var result = ArchiveScanner.Scan(tempDir, "redlab.lo", templates); Assert.Empty(result.Jobs); var row = Assert.Single(result.UnrecognizedFiles); Assert.Equal(SendStatus.Error, row.Status); } } }