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);
}
}
}