|
@@ -0,0 +1,592 @@
|
|
|
|
|
+using System.IO.Abstractions.TestingHelpers;
|
|
|
|
|
+using System.Text.Json;
|
|
|
|
|
+using Quadarax.Foundation.Core.Logging;
|
|
|
|
|
+using qdr.app.studiou.orders2printpack.EmailStorage;
|
|
|
|
|
+using Moq;
|
|
|
|
|
+
|
|
|
|
|
+namespace qdr.app.studiou.orders2printpack.Test
|
|
|
|
|
+{
|
|
|
|
|
+ [TestFixture]
|
|
|
|
|
+ public class EmailStorageProcessorTests
|
|
|
|
|
+ {
|
|
|
|
|
+ private Mock<ILogger> _mockLogger;
|
|
|
|
|
+ private Mock<ILog> _mockLog;
|
|
|
|
|
+ private MockFileSystem _mockFileSystem;
|
|
|
|
|
+ private EmailTemplate _emailTemplate;
|
|
|
|
|
+ private EmailProcessorConfiguration _configuration;
|
|
|
|
|
+ private string _testBasePath;
|
|
|
|
|
+ private EmailStorageProcessor _processor;
|
|
|
|
|
+
|
|
|
|
|
+ [SetUp]
|
|
|
|
|
+ public void SetUp()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Setup mocks
|
|
|
|
|
+ _mockLogger = new Mock<ILogger>();
|
|
|
|
|
+ _mockLog = new Mock<ILog>();
|
|
|
|
|
+ _mockLogger.Setup(x => x.GetLogger(It.IsAny<Type>())).Returns(_mockLog.Object);
|
|
|
|
|
+
|
|
|
|
|
+ // Setup test paths
|
|
|
|
|
+ _testBasePath = @"C:\TestInput";
|
|
|
|
|
+
|
|
|
|
|
+ // Setup mock file system with initial directory structure
|
|
|
|
|
+ _mockFileSystem = new MockFileSystem(new Dictionary<string, MockFileData>
|
|
|
|
|
+ {
|
|
|
|
|
+ // Create base directory
|
|
|
|
|
+ { _testBasePath, new MockDirectoryData() },
|
|
|
|
|
+ // Create a test recipient directory with JPG files
|
|
|
|
|
+ { Path.Combine(_testBasePath, "test@example.com"), new MockDirectoryData() },
|
|
|
|
|
+ { Path.Combine(_testBasePath, "test@example.com", "1001_photo1.jpg"), new MockFileData("fake jpg content") },
|
|
|
|
|
+ { Path.Combine(_testBasePath, "test@example.com", "1001_photo2.jpg"), new MockFileData("fake jpg content") },
|
|
|
|
|
+ { Path.Combine(_testBasePath, "test@example.com", "1002_photo1.jpg"), new MockFileData("fake jpg content") },
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ // Setup email template
|
|
|
|
|
+ _emailTemplate = new EmailTemplate
|
|
|
|
|
+ {
|
|
|
|
|
+ Code = "TEST_TEMPLATE",
|
|
|
|
|
+ Subject = "Your photos are ready - Orders {{ORDERS}}",
|
|
|
|
|
+ Body = "Hello {{RECIPIENT}},\n\nYour photos are ready for download:\n{{DOWNLOAD_LINK}}\nPassword: {{PASSWORD}}\n\nOrders: {{ORDERS}}"
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Setup configuration
|
|
|
|
|
+ _configuration = new EmailProcessorConfiguration
|
|
|
|
|
+ {
|
|
|
|
|
+ TssAPIKey = "test-api-key",
|
|
|
|
|
+ TssUrl = "https://test-tss-url.com",
|
|
|
|
|
+ SmtpServer = "smtp.test.com",
|
|
|
|
|
+ SmtpServerPort = 587,
|
|
|
|
|
+ SmtpServerUser = "test@smtp.com",
|
|
|
|
|
+ SmtpServerPassword = "test-password",
|
|
|
|
|
+ SmtpEnableSsl = true
|
|
|
|
|
+ };
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [TearDown]
|
|
|
|
|
+ public void TearDown()
|
|
|
|
|
+ {
|
|
|
|
|
+ _processor?.Dispose();
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private EmailStorageProcessor CreateProcessor()
|
|
|
|
|
+ {
|
|
|
|
|
+ return new EmailStorageProcessor(_mockFileSystem, _testBasePath, _emailTemplate, _configuration, _mockLogger.Object);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #region Constructor Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithValidParameters_InitializesSuccessfully()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_processor, Is.Not.Null);
|
|
|
|
|
+ Assert.That(_processor.Metadata, Is.Not.Null);
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items, Is.Not.Null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithNullFileSystem_ThrowsArgumentNullException()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.Throws<ArgumentNullException>(() =>
|
|
|
|
|
+ new EmailStorageProcessor(null, _testBasePath, _emailTemplate, _configuration, _mockLogger.Object));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithNullPath_ThrowsArgumentNullException()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.Throws<ArgumentNullException>(() =>
|
|
|
|
|
+ new EmailStorageProcessor(_mockFileSystem, null, _emailTemplate, _configuration, _mockLogger.Object));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithNullEmailTemplate_ThrowsArgumentNullException()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.Throws<ArgumentNullException>(() =>
|
|
|
|
|
+ new EmailStorageProcessor(_mockFileSystem, _testBasePath, null, _configuration, _mockLogger.Object));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithNullConfiguration_ThrowsArgumentNullException()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.Throws<ArgumentNullException>(() =>
|
|
|
|
|
+ new EmailStorageProcessor(_mockFileSystem, _testBasePath, _emailTemplate, null, _mockLogger.Object));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithNullLogger_ThrowsArgumentNullException()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.Throws<ArgumentNullException>(() =>
|
|
|
|
|
+ new EmailStorageProcessor(_mockFileSystem, _testBasePath, _emailTemplate, _configuration, null));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_CreatesRequiredDirectories()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_mockFileSystem.Directory.Exists(Path.Combine(_testBasePath, ".processing")), Is.True);
|
|
|
|
|
+ Assert.That(_mockFileSystem.Directory.Exists(Path.Combine(_testBasePath, ".sending")), Is.True);
|
|
|
|
|
+ Assert.That(_mockFileSystem.Directory.Exists(Path.Combine(_testBasePath, ".sent")), Is.True);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_ScansExistingDirectories_CreatesMetadataItems()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.Count, Is.EqualTo(1));
|
|
|
|
|
+
|
|
|
|
|
+ var item = _processor.Metadata.Items.First();
|
|
|
|
|
+ Assert.That(item.DirectoryName, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ Assert.That(item.EmailAddress, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ Assert.That(item.State, Is.EqualTo(EmailStateEnum.Ready));
|
|
|
|
|
+ Assert.That(item.InternalFilesCount, Is.EqualTo(3));
|
|
|
|
|
+ Assert.That(item.Orders, Is.EqualTo("1001,1002"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Property Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void CountTotal_ReturnsCorrectCount()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ var count = _processor.CountTotal;
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(count, Is.EqualTo(1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void CountProcessing_WithProcessingItems_ReturnsCorrectCount()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ _processor.Metadata.Items.First().State = EmailStateEnum.Processing;
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ var count = _processor.CountProcessing;
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(count, Is.EqualTo(1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void CountSending_WithSendingItems_ReturnsCorrectCount()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ _processor.Metadata.Items.First().State = EmailStateEnum.Sending;
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ var count = _processor.CountSending;
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(count, Is.EqualTo(1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void CountSent_WithSentItems_ReturnsCorrectCount()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ _processor.Metadata.Items.First().State = EmailStateEnum.Sent;
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ var count = _processor.CountSent;
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(count, Is.EqualTo(1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region ResetItem Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void ResetItem_WithValidRecipient_ResetsItemToReadyState()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ var item = _processor.Metadata.Items.First();
|
|
|
|
|
+ item.State = EmailStateEnum.Error;
|
|
|
|
|
+ item.FileNameAttachment = "test.zip";
|
|
|
|
|
+ item.SizeAttachment = 1000;
|
|
|
|
|
+ item.UploadPermalink = "http://test.com";
|
|
|
|
|
+ item.DownloadPassword = "password";
|
|
|
|
|
+ item.ProcessedDate = DateTime.Now;
|
|
|
|
|
+ item.SentDate = DateTime.Now;
|
|
|
|
|
+ item.ErrorMessage = "Test error";
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.ResetItem("test@example.com");
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(item.State, Is.EqualTo(EmailStateEnum.Ready));
|
|
|
|
|
+ Assert.That(item.FileNameAttachment, Is.EqualTo(string.Empty));
|
|
|
|
|
+ Assert.That(item.SizeAttachment, Is.EqualTo(0));
|
|
|
|
|
+ Assert.That(item.UploadPermalink, Is.EqualTo(string.Empty));
|
|
|
|
|
+ Assert.That(item.DownloadPassword, Is.EqualTo(string.Empty));
|
|
|
|
|
+ Assert.That(item.ProcessedDate, Is.Null);
|
|
|
|
|
+ Assert.That(item.SentDate, Is.Null);
|
|
|
|
|
+ Assert.That(item.ErrorMessage, Is.Null);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void ResetItem_WithNonExistentRecipient_LogsWarning()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.ResetItem("nonexistent@example.com");
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ _mockLog.Verify(x => x.Log(LogSeverityEnum.Warn, It.IsAny<string>()), Times.Once);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void ResetItem_CleansUpAssociatedFiles()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ var item = _processor.Metadata.Items.First();
|
|
|
|
|
+ item.FileNameAttachment = "test.zip";
|
|
|
|
|
+
|
|
|
|
|
+ // Create test files in processing, sending, and sent directories
|
|
|
|
|
+ var processingFile = Path.Combine(_testBasePath, ".processing", "test.zip");
|
|
|
|
|
+ var sendingFile = Path.Combine(_testBasePath, ".sending", "test.zip");
|
|
|
|
|
+ var sentFile = Path.Combine(_testBasePath, ".sent", "test.zip");
|
|
|
|
|
+
|
|
|
|
|
+ _mockFileSystem.AddFile(processingFile, new MockFileData("test"));
|
|
|
|
|
+ _mockFileSystem.AddFile(sendingFile, new MockFileData("test"));
|
|
|
|
|
+ _mockFileSystem.AddFile(sentFile, new MockFileData("test"));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.ResetItem("test@example.com");
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_mockFileSystem.File.Exists(processingFile), Is.False);
|
|
|
|
|
+ Assert.That(_mockFileSystem.File.Exists(sendingFile), Is.False);
|
|
|
|
|
+ Assert.That(_mockFileSystem.File.Exists(sentFile), Is.False);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region ProcessItemAsync Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public async Task ProcessItemAsync_WithNonExistentRecipient_LogsErrorAndReturns()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ await _processor.ProcessItemAsync("nonexistent@example.com");
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ _mockLog.Verify(x => x.Log(LogSeverityEnum.Error, It.IsAny<string>()), Times.Once);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public async Task ProcessItemAsync_WithValidRecipient_UpdatesStateToProcessing()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ bool stateChangeEventFired = false;
|
|
|
|
|
+ EmailStateEnum capturedState = EmailStateEnum.Ready;
|
|
|
|
|
+
|
|
|
|
|
+ _processor.EmailItemStateChanged += (sender, recipient, state, progress) =>
|
|
|
|
|
+ {
|
|
|
|
|
+ if (state == EmailStateEnum.Processing)
|
|
|
|
|
+ {
|
|
|
|
|
+ stateChangeEventFired = true;
|
|
|
|
|
+ capturedState = state;
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ await _processor.ProcessItemAsync("test@example.com");
|
|
|
|
|
+ }
|
|
|
|
|
+ catch
|
|
|
|
|
+ {
|
|
|
|
|
+ // Expected to fail due to missing external dependencies, but we want to check state change
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(stateChangeEventFired, Is.True);
|
|
|
|
|
+ Assert.That(capturedState, Is.EqualTo(EmailStateEnum.Processing));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region ProcessAllAsync Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public async Task ProcessAllAsync_ProcessesAllReadyItems()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Add another test item
|
|
|
|
|
+ _mockFileSystem.AddDirectory(Path.Combine(_testBasePath, "test2@example.com"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "test2@example.com", "2001_photo1.jpg"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ // Recreate processor to pick up new directory
|
|
|
|
|
+ _processor.Dispose();
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ await _processor.ProcessAllAsync();
|
|
|
|
|
+ }
|
|
|
|
|
+ catch
|
|
|
|
|
+ {
|
|
|
|
|
+ // Expected to fail due to missing external dependencies
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ // Verify that processing was attempted for all items
|
|
|
|
|
+ _mockLog.Verify(x => x.Log(LogSeverityEnum.Info, It.Is<string>(s => s.Contains("Starting processing for recipient:"))), Times.AtLeast(1));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Stop Method Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Stop_SetsCancellationToken()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.Stop();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ _mockLog.Verify(x => x.Log(LogSeverityEnum.Info, "Processing stop requested."), Times.Once);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Event Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void EmailItemStateChanged_WhenStateUpdated_FiresEvent()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+ bool eventFired = false;
|
|
|
|
|
+ string capturedRecipient = "";
|
|
|
|
|
+ EmailStateEnum capturedState = EmailStateEnum.Ready;
|
|
|
|
|
+ int capturedProgress = 0;
|
|
|
|
|
+
|
|
|
|
|
+ _processor.EmailItemStateChanged += (sender, recipient, state, progress) =>
|
|
|
|
|
+ {
|
|
|
|
|
+ eventFired = true;
|
|
|
|
|
+ capturedRecipient = recipient;
|
|
|
|
|
+ capturedState = state;
|
|
|
|
|
+ capturedProgress = progress;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var item = _processor.Metadata.Items.First();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.ResetItem("test@example.com"); // This should trigger state change
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(eventFired, Is.True);
|
|
|
|
|
+ Assert.That(capturedRecipient, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ Assert.That(capturedState, Is.EqualTo(EmailStateEnum.Ready));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Metadata Management Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithExistingMetadataFile_LoadsMetadata()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ var existingMetadata = new Metadata
|
|
|
|
|
+ {
|
|
|
|
|
+ Created = DateTime.Now.AddDays(-1),
|
|
|
|
|
+ Modified = DateTime.Now.AddHours(-1),
|
|
|
|
|
+ Items = new List<MetadataItem>
|
|
|
|
|
+ {
|
|
|
|
|
+ new MetadataItem
|
|
|
|
|
+ {
|
|
|
|
|
+ DirectoryName = "existing@example.com",
|
|
|
|
|
+ EmailAddress = "existing@example.com",
|
|
|
|
|
+ State = EmailStateEnum.Sent,
|
|
|
|
|
+ Subject = "Test Subject",
|
|
|
|
|
+ Body = "Test Body",
|
|
|
|
|
+ InternalFilesCount = 5,
|
|
|
|
|
+ Orders = "3001,3002"
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var metadataJson = JsonSerializer.Serialize(existingMetadata, new JsonSerializerOptions { WriteIndented = true });
|
|
|
|
|
+ var metadataPath = Path.Combine(_testBasePath, "metadata.json");
|
|
|
|
|
+ _mockFileSystem.AddFile(metadataPath, new MockFileData(metadataJson));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.Count, Is.GreaterThan(0));
|
|
|
|
|
+ var existingItem = _processor.Metadata.Items.FirstOrDefault(x => x.EmailAddress == "existing@example.com");
|
|
|
|
|
+ Assert.That(existingItem, Is.Null);
|
|
|
|
|
+ existingItem = _processor.Metadata.Items.FirstOrDefault(x => x.EmailAddress == "test@example.com");
|
|
|
|
|
+ Assert.That(existingItem.State, Is.EqualTo(EmailStateEnum.Ready));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_ScansDirectoriesAndUpdatesMetadata()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange - metadata file exists but doesn't contain current directory
|
|
|
|
|
+ var existingMetadata = new Metadata
|
|
|
|
|
+ {
|
|
|
|
|
+ Created = DateTime.Now.AddDays(-1),
|
|
|
|
|
+ Items = new List<MetadataItem>()
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ var metadataJson = JsonSerializer.Serialize(existingMetadata);
|
|
|
|
|
+ var metadataPath = Path.Combine(_testBasePath, "metadata.json");
|
|
|
|
|
+ _mockFileSystem.AddFile(metadataPath, new MockFileData(metadataJson));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.Count, Is.EqualTo(1));
|
|
|
|
|
+ var newItem = _processor.Metadata.Items.First();
|
|
|
|
|
+ Assert.That(newItem.DirectoryName, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ Assert.That(newItem.EmailAddress, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region File Operations Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithOrderNumbersInFilenames_ExtractsOrdersCorrectly()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange - setup files with different order patterns
|
|
|
|
|
+ _mockFileSystem.RemoveFile(Path.Combine(_testBasePath, "test@example.com", "1001_photo1.jpg"));
|
|
|
|
|
+ _mockFileSystem.RemoveFile(Path.Combine(_testBasePath, "test@example.com", "1001_photo2.jpg"));
|
|
|
|
|
+ _mockFileSystem.RemoveFile(Path.Combine(_testBasePath, "test@example.com", "1002_photo1.jpg"));
|
|
|
|
|
+
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "test@example.com", "5001_portrait.jpg"), new MockFileData("content"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "test@example.com", "5002_landscape.jpg"), new MockFileData("content"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "test@example.com", "5001_closeup.jpg"), new MockFileData("content"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "test@example.com", "invalid_name.jpg"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ var item = _processor.Metadata.Items.First();
|
|
|
|
|
+ Assert.That(item.Orders, Is.EqualTo("5001,5002")); // Should extract unique order numbers
|
|
|
|
|
+ Assert.That(item.InternalFilesCount, Is.EqualTo(4)); // Should count all JPG files
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Integration Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_WithMultipleRecipientDirectories_CreatesMultipleItems()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _mockFileSystem.AddDirectory(Path.Combine(_testBasePath, "user1@test.com"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "user1@test.com", "1001_photo.jpg"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ _mockFileSystem.AddDirectory(Path.Combine(_testBasePath, "user2@test.com"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, "user2@test.com", "2001_photo.jpg"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.Count, Is.EqualTo(3)); // Original + 2 new
|
|
|
|
|
+
|
|
|
|
|
+ var emails = _processor.Metadata.Items.Select(x => x.EmailAddress).ToList();
|
|
|
|
|
+ Assert.That(emails, Contains.Item("test@example.com"));
|
|
|
|
|
+ Assert.That(emails, Contains.Item("user1@test.com"));
|
|
|
|
|
+ Assert.That(emails, Contains.Item("user2@test.com"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Constructor_IgnoresHiddenDirectories()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _mockFileSystem.AddDirectory(Path.Combine(_testBasePath, ".hidden"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, ".hidden", "1001_photo.jpg"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ _mockFileSystem.AddDirectory(Path.Combine(_testBasePath, ".processing"));
|
|
|
|
|
+ _mockFileSystem.AddFile(Path.Combine(_testBasePath, ".processing", "test.zip"), new MockFileData("content"));
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert
|
|
|
|
|
+ // Should only have the original test@example.com directory, not the hidden ones
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.Count, Is.EqualTo(1));
|
|
|
|
|
+ Assert.That(_processor.Metadata.Items.First().DirectoryName, Is.EqualTo("test@example.com"));
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+
|
|
|
|
|
+ #region Dispose Tests
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Dispose_DisposesResourcesProperly()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act
|
|
|
|
|
+ _processor.Dispose();
|
|
|
|
|
+
|
|
|
|
|
+ // Assert - Should not throw
|
|
|
|
|
+ Assert.Pass("Dispose completed without exceptions");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ [Test]
|
|
|
|
|
+ public void Dispose_CalledMultipleTimes_DoesNotThrow()
|
|
|
|
|
+ {
|
|
|
|
|
+ // Arrange
|
|
|
|
|
+ _processor = CreateProcessor();
|
|
|
|
|
+
|
|
|
|
|
+ // Act & Assert
|
|
|
|
|
+ Assert.DoesNotThrow(() =>
|
|
|
|
|
+ {
|
|
|
|
|
+ _processor.Dispose();
|
|
|
|
|
+ _processor.Dispose();
|
|
|
|
|
+ _processor.Dispose();
|
|
|
|
|
+ });
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ #endregion
|
|
|
|
|
+ }
|
|
|
|
|
+}
|