ソースを参照

add EmailStorageProcessorTests

Dalibor Votruba 1 年間 前
コミット
65c194a850

+ 1 - 1
Test/EmailItemTests.cs

@@ -1,7 +1,7 @@
 using qdr.app.studiou.orders2printpack.EmailStorage;
 using System.IO.Abstractions.TestingHelpers;
 
-namespace Test
+namespace qdr.app.studiou.orders2printpack.Test
 {
    [TestFixture]
     public class EmailItemTests

+ 592 - 0
Test/EmailStorageProcessorTests.cs

@@ -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
+    }
+}

+ 1 - 0
Test/qdr.app.studiou.orders2printpack.Test.csproj

@@ -11,6 +11,7 @@
   <ItemGroup>
     <PackageReference Include="coverlet.collector" Version="6.0.2" />
     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
+    <PackageReference Include="Moq" Version="4.20.72" />
     <PackageReference Include="NUnit" Version="4.2.2" />
     <PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
     <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />