소스 검색

sdd UT for EmailItem

Dalibor Votruba 1 년 전
부모
커밋
545a520574
4개의 변경된 파일681개의 추가작업 그리고 23개의 파일을 삭제
  1. 78 6
      App/EmailStorage/EmailItem.cs
  2. 597 0
      Test/EmailItemTests.cs
  3. 0 16
      Test/UnitTest1.cs
  4. 6 1
      Test/qdr.app.studiou.orders2printpack.Test.csproj

+ 78 - 6
App/EmailStorage/EmailItem.cs

@@ -1,3 +1,5 @@
+using System.IO.Abstractions;
+
 namespace qdr.app.studiou.orders2printpack.EmailStorage
 {
     /// <summary>
@@ -19,6 +21,11 @@ namespace qdr.app.studiou.orders2printpack.EmailStorage
         /// </summary>
         private readonly string _basePath;
 
+        /// <summary>
+        /// File system abstraction for testable file operations
+        /// </summary>
+        private readonly IFileSystem _fileSystem;
+
         #endregion
 
         #region Constructor
@@ -28,11 +35,13 @@ namespace qdr.app.studiou.orders2printpack.EmailStorage
         /// </summary>
         /// <param name="metadata">The metadata item containing email information</param>
         /// <param name="basePath">Base directory path for file operations</param>
-        /// <exception cref="ArgumentNullException">Thrown when metadata or basePath is null</exception>
-        public EmailItem(MetadataItem metadata, string basePath)
+        /// <param name="fileSystem">File system abstraction for file operations</param>
+        /// <exception cref="ArgumentNullException">Thrown when metadata, basePath, or fileSystem is null</exception>
+        public EmailItem(MetadataItem metadata, string basePath, IFileSystem fileSystem)
         {
             _metadata = metadata ?? throw new ArgumentNullException(nameof(metadata));
             _basePath = basePath ?? throw new ArgumentNullException(nameof(basePath));
+            _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
         }
 
         #endregion
@@ -48,19 +57,19 @@ namespace qdr.app.studiou.orders2printpack.EmailStorage
         /// Gets the full directory path for this email item
         /// Combines the base path with the directory name from metadata
         /// </summary>
-        public string DirectoryPath => Path.Combine(_basePath, _metadata.DirectoryName);
+        public string DirectoryPath => _fileSystem.Path.Combine(_basePath, _metadata.DirectoryName);
         
         /// <summary>
         /// Gets a value indicating whether the directory exists on the file system
         /// </summary>
-        public bool DirectoryExists => Directory.Exists(DirectoryPath);
+        public bool DirectoryExists => _fileSystem.Directory.Exists(DirectoryPath);
         
         /// <summary>
         /// Gets all JPG files in the email item's directory
         /// Returns an empty enumerable if the directory doesn't exist
         /// </summary>
         public IEnumerable<string> JpgFiles => 
-            DirectoryExists ? Directory.GetFiles(DirectoryPath, "*.jpg") : Enumerable.Empty<string>();
+            DirectoryExists ? _fileSystem.Directory.GetFiles(DirectoryPath, "*.jpg") : Enumerable.Empty<string>();
         
         /// <summary>
         /// Gets the count of JPG files in the directory
@@ -70,7 +79,7 @@ namespace qdr.app.studiou.orders2printpack.EmailStorage
         /// <summary>
         /// Gets the total size in bytes of all JPG files in the directory
         /// </summary>
-        public long TotalFileSize => JpgFiles.Sum(f => new FileInfo(f).Length);
+        public long TotalFileSize => JpgFiles.Sum(f => _fileSystem.FileInfo.New(f).Length);
         
         /// <summary>
         /// Gets a value indicating whether this item is ready for processing
@@ -142,6 +151,69 @@ namespace qdr.app.studiou.orders2printpack.EmailStorage
         public string GetSummary() =>
             $"{_metadata.EmailAddress} - {FileCount} files, {_metadata.State}, Orders: {_metadata.Orders}";
 
+        /// <summary>
+        /// Gets detailed file information for all JPG files in the directory
+        /// </summary>
+        /// <returns>Collection of file information objects</returns>
+        public IEnumerable<IFileInfo> GetFileDetails() =>
+            JpgFiles.Select(f => _fileSystem.FileInfo.New(f));
+
+        /// <summary>
+        /// Checks if a specific file exists in the email item's directory
+        /// </summary>
+        /// <param name="fileName">Name of the file to check</param>
+        /// <returns>True if the file exists, false otherwise</returns>
+        public bool FileExists(string fileName)
+        {
+            if (string.IsNullOrEmpty(fileName))
+                return false;
+
+            var filePath = _fileSystem.Path.Combine(DirectoryPath, fileName);
+            return _fileSystem.File.Exists(filePath);
+        }
+
+        /// <summary>
+        /// Gets the full path for a file within the email item's directory
+        /// </summary>
+        /// <param name="fileName">Name of the file</param>
+        /// <returns>Full path to the file</returns>
+        public string GetFilePath(string fileName) =>
+            _fileSystem.Path.Combine(DirectoryPath, fileName);
+
+        /// <summary>
+        /// Creates the email item's directory if it doesn't exist
+        /// </summary>
+        /// <returns>True if directory was created or already exists, false if creation failed</returns>
+        public bool EnsureDirectoryExists()
+        {
+            try
+            {
+                if (!DirectoryExists)
+                {
+                    _fileSystem.Directory.CreateDirectory(DirectoryPath);
+                }
+                return true;
+            }
+            catch
+            {
+                return false;
+            }
+        }
+
+        /// <summary>
+        /// Gets file information for a specific file in the directory
+        /// </summary>
+        /// <param name="fileName">Name of the file</param>
+        /// <returns>File information object, or null if file doesn't exist</returns>
+        public IFileInfo? GetFileInfo(string fileName)
+        {
+            if (string.IsNullOrEmpty(fileName))
+                return null;
+
+            var filePath = GetFilePath(fileName);
+            return _fileSystem.File.Exists(filePath) ? _fileSystem.FileInfo.New(filePath) : null;
+        }
+
         #endregion
     }
 }

+ 597 - 0
Test/EmailItemTests.cs

@@ -0,0 +1,597 @@
+using qdr.app.studiou.orders2printpack.EmailStorage;
+using System.IO.Abstractions.TestingHelpers;
+
+namespace Test
+{
+   [TestFixture]
+    public class EmailItemTests
+    {
+        private MockFileSystem _mockFileSystem;
+        private string _basePath;
+        private MetadataItem _testMetadata;
+
+        [SetUp]
+        public void SetUp()
+        {
+            _mockFileSystem = new MockFileSystem();
+            _basePath = @"C:\TestBase";
+            
+            _testMetadata = new MetadataItem
+            {
+                DirectoryName = "test_user_at_example_dot_com",
+                EmailAddress = "test.user@example.com",
+                State = EmailStateEnum.Ready,
+                Subject = "Test Subject",
+                Body = "Test Body",
+                FileNameAttachment = "test_attachment.zip",
+                SizeAttachment = 1024,
+                InternalFilesCount = 3,
+                Orders = "1001,1002,1003"
+            };
+
+            // Create base directory
+            _mockFileSystem.AddDirectory(_basePath);
+        }
+
+        #region Constructor Tests
+
+        [Test]
+        public void Constructor_WithValidParameters_ShouldInitializeCorrectly()
+        {
+            // Act
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Assert
+            Assert.That(emailItem.Metadata, Is.EqualTo(_testMetadata));
+            Assert.That(emailItem.DirectoryPath, Is.EqualTo(Path.Combine(_basePath, _testMetadata.DirectoryName)));
+        }
+
+        [Test]
+        public void Constructor_WithNullMetadata_ShouldThrowArgumentNullException()
+        {
+            // Act & Assert
+            var ex = Assert.Throws<ArgumentNullException>(() => new EmailItem(null, _basePath, _mockFileSystem));
+            Assert.That(ex.ParamName, Is.EqualTo("metadata"));
+        }
+
+        [Test]
+        public void Constructor_WithNullBasePath_ShouldThrowArgumentNullException()
+        {
+            // Act & Assert
+            var ex = Assert.Throws<ArgumentNullException>(() => new EmailItem(_testMetadata, null, _mockFileSystem));
+            Assert.That(ex.ParamName, Is.EqualTo("basePath"));
+        }
+
+        #endregion
+
+        #region Property Tests
+
+        [Test]
+        public void DirectoryPath_ShouldCombineBasePathWithDirectoryName()
+        {
+            // Arrange
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+            var expectedPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+
+            // Act & Assert
+            Assert.That(emailItem.DirectoryPath, Is.EqualTo(expectedPath));
+        }
+
+        [Test]
+        public void DirectoryExists_WhenDirectoryExists_ShouldReturnTrue()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.DirectoryExists, Is.True);
+        }
+
+        [Test]
+        public void DirectoryExists_WhenDirectoryDoesNotExist_ShouldReturnFalse()
+        {
+            // Arrange
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.DirectoryExists, Is.False);
+        }
+
+        [Test]
+        public void JpgFiles_WhenDirectoryExists_ShouldReturnJpgFiles()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image2.jpg"), new MockFileData("image2"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "document.txt"), new MockFileData("text"));
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var jpgFiles = emailItem.JpgFiles.ToList();
+
+            // Assert
+            Assert.That(jpgFiles.Count, Is.EqualTo(2));
+            Assert.That(jpgFiles.Any(f => f.EndsWith("image1.jpg")), Is.True);
+            Assert.That(jpgFiles.Any(f => f.EndsWith("image2.jpg")), Is.True);
+            Assert.That(jpgFiles.Any(f => f.EndsWith("document.txt")), Is.False);
+        }
+
+        [Test]
+        public void JpgFiles_WhenDirectoryDoesNotExist_ShouldReturnEmptyEnumerable()
+        {
+            // Arrange
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var jpgFiles = emailItem.JpgFiles.ToList();
+
+            // Assert
+            Assert.That(jpgFiles, Is.Empty);
+        }
+
+        [Test]
+        public void FileCount_WhenDirectoryHasJpgFiles_ShouldReturnCorrectCount()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image2.jpg"), new MockFileData("image2"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image3.jpg"), new MockFileData("image3"));
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.FileCount, Is.EqualTo(3));
+        }
+
+        [Test]
+        public void FileCount_WhenDirectoryDoesNotExist_ShouldReturnZero()
+        {
+            // Arrange
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.FileCount, Is.EqualTo(0));
+        }
+
+        [Test]
+        public void TotalFileSize_WhenDirectoryHasJpgFiles_ShouldReturnCorrectTotalSize()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData(new byte[100])); // 100 bytes
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image2.jpg"), new MockFileData(new byte[200])); // 200 bytes
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image3.jpg"), new MockFileData(new byte[300])); // 300 bytes
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.TotalFileSize, Is.EqualTo(600));
+        }
+
+        [Test]
+        public void TotalFileSize_WhenDirectoryDoesNotExist_ShouldReturnZero()
+        {
+            // Arrange
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.TotalFileSize, Is.EqualTo(0));
+        }
+
+        [Test]
+        public void IsReadyForProcessing_WhenStateIsReadyAndDirectoryExistsWithFiles_ShouldReturnTrue()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            
+            _testMetadata.State = EmailStateEnum.Ready;
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.IsReadyForProcessing, Is.True);
+        }
+
+        [Test]
+        public void IsReadyForProcessing_WhenStateIsNotReady_ShouldReturnFalse()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            
+            _testMetadata.State = EmailStateEnum.Processing;
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.IsReadyForProcessing, Is.False);
+        }
+
+        [Test]
+        public void IsReadyForProcessing_WhenDirectoryDoesNotExist_ShouldReturnFalse()
+        {
+            // Arrange
+            _testMetadata.State = EmailStateEnum.Ready;
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.IsReadyForProcessing, Is.False);
+        }
+
+        [Test]
+        public void IsReadyForProcessing_WhenDirectoryExistsButNoFiles_ShouldReturnFalse()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            
+            _testMetadata.State = EmailStateEnum.Ready;
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.IsReadyForProcessing, Is.False);
+        }
+
+        [Test]
+        public void CanBeReset_WhenStateIsNotProcessingOrUploading_ShouldReturnTrue()
+        {
+            // Arrange
+            var testCases = new[]
+            {
+                EmailStateEnum.Ready,
+                EmailStateEnum.Sending,
+                EmailStateEnum.Sent,
+                EmailStateEnum.Error
+            };
+
+            foreach (var state in testCases)
+            {
+                _testMetadata.State = state;
+                var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+                // Act & Assert
+                Assert.That(emailItem.CanBeReset, Is.True, $"Failed for state: {state}");
+            }
+        }
+
+        [Test]
+        public void CanBeReset_WhenStateIsProcessingOrUploading_ShouldReturnFalse()
+        {
+            // Arrange
+            var testCases = new[]
+            {
+                EmailStateEnum.Processing,
+                EmailStateEnum.Uploading
+            };
+
+            foreach (var state in testCases)
+            {
+                _testMetadata.State = state;
+                var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+                // Act & Assert
+                Assert.That(emailItem.CanBeReset, Is.False, $"Failed for state: {state}");
+            }
+        }
+
+        [Test]
+        public void NormalizedRecipient_ShouldReplaceSpecialCharacters()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user@example.com";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.NormalizedRecipient, Is.EqualTo("test_dot_user_at_example_dot_com"));
+        }
+
+        [Test]
+        public void NormalizedRecipient_WithComplexEmail_ShouldHandleMultipleSpecialCharacters()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user.name@sub.domain.example.com";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.NormalizedRecipient, Is.EqualTo("test_dot_user_dot_name_at_sub_dot_domain_dot_example_dot_com"));
+        }
+
+        #endregion
+
+        #region Method Tests
+
+        [Test]
+        public void GenerateZipFileName_WithDefaultTimestamp_ShouldIncludeCurrentTime()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user@example.com";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var fileName = emailItem.GenerateZipFileName();
+
+            // Assert
+            Assert.That(fileName, Does.StartWith("studioufoto_test_dot_user_at_example_dot_com_"));
+            Assert.That(fileName, Does.EndWith(".zip"));
+            
+            // Check that it contains a timestamp pattern (YYYYMMDD_HHMMSS)
+            var timestampPattern = @"\d{8}_\d{6}";
+            Assert.That(fileName, Does.Match($@"studioufoto_test_dot_user_at_example_dot_com_{timestampPattern}\.zip"));
+        }
+
+        [Test]
+        public void GenerateZipFileName_WithSpecificTimestamp_ShouldUseProvidedTimestamp()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user@example.com";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+            var timestamp = new DateTime(2024, 5, 30, 14, 30, 45);
+
+            // Act
+            var fileName = emailItem.GenerateZipFileName(timestamp);
+
+            // Assert
+            Assert.That(fileName, Is.EqualTo("studioufoto_test_dot_user_at_example_dot_com_20240530_143045.zip"));
+        }
+
+        [Test]
+        public void Validate_WithAllValidData_ShouldReturnValid()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.Subject = "Valid Subject";
+            _testMetadata.Body = "Valid Body";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.True);
+            Assert.That(issues, Is.Empty);
+        }
+
+        [Test]
+        public void Validate_WithMissingEmailAddress_ShouldReturnInvalid()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item("Email address is required"));
+        }
+
+        [Test]
+        public void Validate_WithNullEmailAddress_ShouldReturnInvalid()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = null;
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item("Email address is required"));
+        }
+
+        [Test]
+        public void Validate_WithNonExistentDirectory_ShouldReturnInvalid()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.Subject = "Valid Subject";
+            _testMetadata.Body = "Valid Body";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item($"Directory does not exist: {emailItem.DirectoryPath}"));
+        }
+
+        [Test]
+        public void Validate_WithDirectoryButNoJpgFiles_ShouldReturnInvalid()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.Subject = "Valid Subject";
+            _testMetadata.Body = "Valid Body";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item("No JPG files found in directory"));
+        }
+
+        [Test]
+        public void Validate_WithMissingSubject_ShouldReturnInvalid()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.Subject = "";
+            _testMetadata.Body = "Valid Body";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item("Email subject is required"));
+        }
+
+        [Test]
+        public void Validate_WithMissingBody_ShouldReturnInvalid()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.Subject = "Valid Subject";
+            _testMetadata.Body = "";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues, Contains.Item("Email body is required"));
+        }
+
+        [Test]
+        public void Validate_WithMultipleIssues_ShouldReturnAllIssues()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "";
+            _testMetadata.Subject = "";
+            _testMetadata.Body = "";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var (isValid, issues) = emailItem.Validate();
+
+            // Assert
+            Assert.That(isValid, Is.False);
+            Assert.That(issues.Count, Is.EqualTo(4)); // Email, Directory, Subject, Body
+            Assert.That(issues, Contains.Item("Email address is required"));
+            Assert.That(issues, Contains.Item($"Directory does not exist: {emailItem.DirectoryPath}"));
+            Assert.That(issues, Contains.Item("Email subject is required"));
+            Assert.That(issues, Contains.Item("Email body is required"));
+        }
+
+        [Test]
+        public void GetSummary_ShouldReturnFormattedSummary()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image2.jpg"), new MockFileData("image2"));
+            
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.State = EmailStateEnum.Ready;
+            _testMetadata.Orders = "1001,1002";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var summary = emailItem.GetSummary();
+
+            // Assert
+            Assert.That(summary, Is.EqualTo("test.user@example.com - 2 files, Ready, Orders: 1001,1002"));
+        }
+
+        [Test]
+        public void GetSummary_WithNoFiles_ShouldShowZeroFiles()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "test.user@example.com";
+            _testMetadata.State = EmailStateEnum.Error;
+            _testMetadata.Orders = "1001";
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var summary = emailItem.GetSummary();
+
+            // Assert
+            Assert.That(summary, Is.EqualTo("test.user@example.com - 0 files, Error, Orders: 1001"));
+        }
+
+        #endregion
+
+        #region Edge Cases and Boundary Tests
+
+        [Test]
+        public void NormalizedRecipient_WithEmptyEmail_ShouldReturnEmpty()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act & Assert
+            Assert.That(emailItem.NormalizedRecipient, Is.EqualTo(""));
+        }
+
+        [Test]
+        public void GenerateZipFileName_WithEmptyEmail_ShouldStillGenerateFileName()
+        {
+            // Arrange
+            _testMetadata.EmailAddress = "";
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var fileName = emailItem.GenerateZipFileName();
+
+            // Assert
+            Assert.That(fileName, Does.StartWith("studioufoto__"));
+            Assert.That(fileName, Does.EndWith(".zip"));
+        }
+
+        [Test]
+        public void JpgFiles_WithMixedCaseExtensions_ShouldReturnJpgFilesOnly()
+        {
+            // Arrange
+            var directoryPath = Path.Combine(_basePath, _testMetadata.DirectoryName);
+            _mockFileSystem.AddDirectory(directoryPath);
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image1.jpg"), new MockFileData("image1"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image2.JPG"), new MockFileData("image2"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "image3.jpeg"), new MockFileData("image3"));
+            _mockFileSystem.AddFile(Path.Combine(directoryPath, "document.txt"), new MockFileData("text"));
+            
+            var emailItem = new EmailItem(_testMetadata, _basePath, _mockFileSystem);
+
+            // Act
+            var jpgFiles = emailItem.JpgFiles.ToList();
+
+            // Assert
+            // Note: MockFileSystem's GetFiles with "*.jpg" pattern is case-sensitive
+            // This test verifies the current behavior - adjust if case-insensitive matching is needed
+            Assert.That(jpgFiles.Count, Is.EqualTo(2)); // Only lowercase .jpg
+            Assert.That(jpgFiles.Any(f => f.EndsWith("image1.jpg")), Is.True);
+        }
+
+        #endregion
+    }
+}

+ 0 - 16
Test/UnitTest1.cs

@@ -1,16 +0,0 @@
-namespace Test
-{
-    public class Tests
-    {
-        [SetUp]
-        public void Setup()
-        {
-        }
-
-        [Test]
-        public void Test1()
-        {
-            Assert.Pass();
-        }
-    }
-}

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

@@ -1,7 +1,7 @@
 <Project Sdk="Microsoft.NET.Sdk">
 
   <PropertyGroup>
-    <TargetFramework>net9.0</TargetFramework>
+    <TargetFramework>net9.0-windows</TargetFramework>
     <LangVersion>latest</LangVersion>
     <ImplicitUsings>enable</ImplicitUsings>
     <Nullable>enable</Nullable>
@@ -14,6 +14,11 @@
     <PackageReference Include="NUnit" Version="4.2.2" />
     <PackageReference Include="NUnit.Analyzers" Version="4.4.0" />
     <PackageReference Include="NUnit3TestAdapter" Version="4.6.0" />
+    <PackageReference Include="System.IO.Abstractions.TestingHelpers" Version="22.0.14" />
+  </ItemGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\App\qdr.app.studiou.orders2printpack.csproj" />
   </ItemGroup>
 
   <ItemGroup>