Compare commits

...

13 Commits

Author SHA1 Message Date
OlgunR
64be11f7ad Add tests for DevExpressPdfProcessor and roadmap updates
Updated ROADMAP.md to reflect progress on the TDD process for
DevExpressPdfProcessor, including the creation of unit tests
(6 tests in the Red Phase). Updated progress to 4/7 mini-steps
completed and outlined the next step (Green Phase).

Added `DevExpressPdfProcessorTests.cs` with unit tests to
validate PDF files and extract metadata. Tests cover valid
PDFs, null/empty inputs, corrupted PDFs, and file size
calculations. Used `FluentAssertions` for assertions.

Cleaned up `DocumentOperator.Tests.csproj` by removing an
unused folder reference. No functional changes to the project
structure.
2026-06-19 14:38:12 +02:00
OlgunR
b88f011701 It seems your list of code changes is empty. Could you provide the descriptions of the changes made to the files? Once you do, I can help craft a concise and comprehensive commit message for you! 2026-06-19 12:54:15 +02:00
OlgunR
b8c9e1b6a6 Update ROADMAP and test structure for DevExpressPdfProcessor
Updated ROADMAP.md to reflect progress on Phase 3, Step 3.2:
- Created test folder structure under Unit/Infrastructure/Services/PdfProcessing.
- Updated progress status to 2/7 mini-steps completed.
- Documented next step (adding a test PDF file) and marked DevExpressPdfProcessor.cs implementation as "IN PROGRESS."
- Highlighted availability of the DevExpress Universal License.

Modified DocumentOperator.Tests.csproj:
- Added the new test folder structure to the project file.
2026-06-19 12:44:29 +02:00
OlgunR
09cc64eff0 Refactor: Remove ProcessDocument and update roadmap
Removed the obsolete `ProcessDocument` folder and its files
(`ProcessDocumentCommand.cs`, `ProcessDocumentHandler.cs`,
`ProcessDocumentValidator.cs`) as part of Application Layer
cleanup. Updated `ROADMAP.md` to reflect progress, including
the start of `DevExpressPdfProcessor` implementation (Step 3.2)
and actionable steps for creating a test folder structure.
Documented the availability of the `DevExpress Universal License`.
2026-06-19 11:36:16 +02:00
OlgunR
867e0b2655 Remove unused UnitTest1 class and empty test method
The `UnitTest1` class in the `DocumentOperator.Tests` namespace was removed, including the `Test1` method, which was an empty test marked with the `[Fact]` attribute. This cleanup suggests the test class is no longer needed or has been replaced by other tests.
2026-06-19 11:08:53 +02:00
OlgunR
fc79665241 Update ROADMAP and add STATUS_UPDATE for project status
Extensively updated `ROADMAP.md` to reflect the current
project status, including documentation of the DevExpress
Universal License, completion of Phases 1 and 2, and
progress in Phase 3. Clarified discrepancies in the
Application Layer and identified gaps in the Tests Layer
and Infrastructure Services.

Created `STATUS_UPDATE_17_01_2025.md` to summarize the
current status, key learnings, and next steps. Outlined
a TDD-driven approach for implementing the
`DevExpressPdfProcessor` and cleaning up the Application
Layer. Confirmed build success and updated documentation
to align with the latest roadmap.
2026-06-19 11:08:36 +02:00
OlgunR
196f6d9cfb Update test project dependencies and add project references
Updated `Microsoft.NET.Test.Sdk` to version 17.11.1 and `xunit` to version 2.9.3. Updated `xunit.runner.visualstudio` to version 2.8.2 with additional metadata for asset inclusion and private asset behavior.

Added new dependencies: `FluentAssertions` (7.0.0) and `Moq` (4.20.72).

Introduced project references to `DocumentOperator.Domain`, `DocumentOperator.Application`, and `DocumentOperator.Infrastructure` to the test project.

No changes were made to the `coverlet.collector` dependency or the `<Using Include="Xunit" />` directive.
2026-06-18 16:35:56 +02:00
OlgunR
498b6758bf Add DocumentOperator.Tests project for unit testing
Added a new test project, `DocumentOperator.Tests`, to the solution targeting .NET 8.0. Configured the project as a non-packable test project with support for nullable reference types and implicit usings. Included dependencies for `xunit`, `Microsoft.NET.Test.Sdk`, and `coverlet.collector` for testing and code coverage.

Added a placeholder test class, `UnitTest1`, with a single test method, `Test1`, marked with the `[Fact]` attribute.
2026-06-18 16:23:41 +02:00
OlgunR
49d1f43822 Add IPdfProcessor interface and update ROADMAP.md
The `IPdfProcessor` interface was added to the `Application/Common/Interfaces/` directory. It includes the `ValidateAsync` method for validating PDFs and extracting metadata, with proper XML documentation and dependency on domain value objects.

Updated `ROADMAP.md` to mark Step 3.1 as completed, detailing the creation of the `IPdfProcessor` interface and its implementation status.

Removed the `<Folder Include="Common\Interfaces\" />` entry from `DocumentOperator.Application.csproj` to reflect the transition from a placeholder folder structure to actual implementation.
2026-06-18 16:02:32 +02:00
OlgunR
3a87ace144 Complete Phase 2: Domain Layer implementation
Updated ROADMAP.md to mark Phase 2 as completed and added detailed descriptions of completed tasks. Introduced three new value objects (`Base64String`, `TenantId`, and `PdfMetadata`) in the `DocumentOperator.Domain.Models.ValueObjects` namespace. These classes ensure type safety, immutability, and encapsulated validation.

- `Base64String`: Handles Base64 string creation, validation, and conversion.
- `TenantId`: Represents a tenant identifier with validation and normalization.
- `PdfMetadata`: Represents PDF metadata with computed properties.

Updated `DocumentOperator.Domain.csproj` to reflect the addition of these value objects. The project is now ready to begin Phase 3 (Infrastructure Layer).
2026-06-18 14:32:06 +02:00
OlgunR
cdb942210c Complete Step 2.2: Add enums for business concepts
Updated ROADMAP.md to mark Step 2.2 ("Enums erstellen") as completed, documenting the creation of `DocumentOperationType` and `ProcessingStatus` enums.

Added `DocumentOperationType` and `ProcessingStatus` enums under the `DocumentOperator.Domain.Models.Enums` namespace to represent document operations and processing statuses, respectively.

Modified `DocumentOperator.Domain.csproj` to remove the `Models\Enums\` folder from the `<ItemGroup>` section, reflecting changes in the inclusion strategy for enums.
2026-06-17 16:43:03 +02:00
OlgunR
9512913866 Refactor ROADMAP.md for pragmatic TDD approach
The roadmap document was restructured to reflect a pragmatic, iterative, and test-driven development (TDD) approach. Key updates include:

- Title updated to "Pragmatic Edition" with revised last updated date.
- Table of contents reorganized with new sections (e.g., "Development Philosophy," "Testing Strategy").
- Expanded "Core Features" and "Business Workflow" sections with additional details and updated flow diagrams.
- Revised "Architecture & Design Decisions" to emphasize minimal domain layers, dependency rules, and vertical slice architecture.
- Updated "CQRS with MediatR" and "Minimal APIs" sections with examples and best practices.
- Added "Development Philosophy," "Testing Strategy," and "Key Learnings & Decisions" sections.
- Updated "Technology Stack" to include testing libraries and remove unused dependencies.
- Reflected new folder structure, including a `Tests` project for unit and integration tests.
- Rewrote "Development Roadmap" with detailed steps for each phase, focusing on TDD and outside-in development.
- Updated "References & Best Practices" and "Update Log" to align with the new approach.

These changes aim to improve clarity, maintainability, and alignment with modern .NET practices.
2026-06-17 16:23:13 +02:00
OlgunR
758d32d8e0 Update ROADMAP.md with detailed project roadmap
- Added "Last Updated" timestamp, current status, and phase.
- Introduced a "TABLE OF CONTENTS" for easier navigation.
- Expanded "PROJECT OVERVIEW" with vision, purpose, and workflow.
- Detailed "ARCHITECTURE & DESIGN DECISIONS" with key patterns.
- Listed frameworks, libraries, and components in "TECH STACK."
- Provided a breakdown of the solution's folder structure.
- Outlined development phases in "DEVELOPMENT ROADMAP."
- Documented progress in "CURRENT STATUS" and added "UPDATE LOG."
- Included "LEARNING NOTES" and references to best practices.
- Improved formatting for clarity and readability.
2026-06-17 15:12:44 +02:00
18 changed files with 2073 additions and 1679 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,218 @@
# ?? Status Update - DocumentOperator (17.01.2025)
## ? Was wurde aktualisiert?
Die **ROADMAP.md** wurde vollständig mit dem **tatsächlichen Projektstand** abgeglichen und aktualisiert.
---
## ?? Haupterkenntnisse
### 1. **DevExpress Universal License** ?
**Wichtig:** Das Projekt verfügt über eine **DevExpress Universal License**!
**Das bedeutet:**
- ? Vollzugriff auf **ALLE** DevExpress Bibliotheken
- ? Nicht nur `DevExpress.Pdf.Core` - wir können **jedes** DevExpress Paket nutzen
- ? Falls künftig Word/Excel-Verarbeitung benötigt wird ? einfach hinzufügen!
**Aktuell verwendet:**
- `DevExpress.Pdf.Core` v25.2.8
**Bei Bedarf verfügbar:**
- `DevExpress.Office.Core` (Word, Excel)
- `DevExpress.Document.Processor` (erweiterte Dokumenten-Verarbeitung)
- `DevExpress.Blazor` (falls UI später benötigt wird)
- Alle weiteren DevExpress Produkte
---
### 2. **Aktueller Projektstand**
#### ? Abgeschlossen (Completed)
**Phase 1: Foundation**
- ? Solution Structure (4 Projekte + Tests)
- ? Dependencies (Clean Architecture Rules)
- ? NuGet Packages installiert
- ? Folder Structure erstellt
- ? Configuration (appsettings.json, Options Pattern)
- ? Serilog Setup
- ? Program.cs Setup
**Phase 2: Domain Layer (Minimal)**
- ? **4 Domain Exceptions:**
- `DomainException.cs` (Basis)
- `DomainValidationException.cs` (Value Object Validierung)
- `NotFoundException.cs` (Resource nicht gefunden)
- `PdfProcessingException.cs` (PDF-spezifische Fehler)
- ? **2 Enums:**
- `DocumentOperationType` (Validate, ExtractAttachments, Concatenate, ApplyStamp, EmbedCertificate)
- `ProcessingStatus` (Pending, Processing, Success, Failed)
- ? **3 Value Objects:**
- `Base64String` (typsicher, selbst-validierend, mit Factory Methods)
- `TenantId` (normalisiert, validiert)
- `PdfMetadata` (PageCount, FileSizeBytes, PdfVersion, HasAttachments, etc.)
**Phase 3: Infrastructure Layer**
- ? **IPdfProcessor Interface** erstellt
- `Task<PdfMetadata> ValidateAsync(byte[] pdfBytes)`
- Mit XML Comments dokumentiert
---
#### ?? In Arbeit (In Progress)
**Phase 3: Infrastructure Layer**
- **NEXT:** Step 3.2 - `DevExpressPdfProcessor` implementieren (mit TDD!)
- Ordner `Services/PdfProcessing/` existiert bereits
- **Aber:** Noch leer - muss implementiert werden
---
#### ?? Wichtige Hinweise
**1. Application Layer - ProcessDocument vs ValidatePdf**
- **Problem:**
- Ordner: `Features/Documents/ProcessDocument/`
- Dateien: `ProcessDocumentCommand.cs`, `ProcessDocumentHandler.cs`, `ProcessDocumentValidator.cs`
- **Alle leer!**
- **Roadmap sagt:**
- Wir sollten mit `ValidatePdf` Feature starten (nicht ProcessDocument)
- **Action Required:**
- Entweder ProcessDocument-Dateien löschen
- Oder umbenennen zu ValidatePdf
- Oder erst später nutzen (wenn wir ein generisches ProcessDocument Command brauchen)
**2. Tests Layer**
- **Problem:** Nur `UnitTest1.cs` (Dummy-Test)
- **Action Required:**
- Ordnerstruktur erstellen:
```
Tests/
??? Unit/
? ??? Application/
? ??? Infrastructure/
? ??? Domain/
??? Integration/
??? API/
```
- `UnitTest1.cs` löschen
**3. Infrastructure Services**
- **Problem:** Ordner existieren, aber leer
- `Services/PdfProcessing/` ? DevExpressPdfProcessor.cs fehlt
- `Services/FileStorage/` ? leer
- `Services/DocumentValidation/` ? leer
- **Action Required:**
- Step 3.2 durchführen: DevExpressPdfProcessor implementieren
---
## ?? Nächste Schritte (Roadmap)
### Schritt 1: DevExpressPdfProcessor implementieren (Phase 3, Step 3.2)
**TDD-Flow:**
1. **Test schreiben** (Red)
- `Tests/Unit/Infrastructure/Services/PdfProcessing/DevExpressPdfProcessorTests.cs`
- Test: `ValidateAsync_ValidPdf_ReturnsMetadata()`
2. **Code schreiben** (Green)
- `Infrastructure/Services/PdfProcessing/DevExpressPdfProcessor.cs`
- `IPdfProcessor` Interface implementieren
- DevExpress PDF API nutzen
3. **Test grün machen**
4. **Refactoring** (falls nötig)
---
### Schritt 2: Application Layer aufräumen
**Option A: ProcessDocument löschen**
```powershell
Remove-Item "DocumentOperator.Application\Features\Documents\ProcessDocument" -Recurse
```
**Option B: Zu ValidatePdf umbenennen**
```powershell
Rename-Item "ProcessDocument" "ValidatePdf"
# Dann Dateien umbenennen + Namespaces anpassen
```
**Option C: Behalten und später nutzen**
- Erst ValidatePdf neu erstellen
- ProcessDocument später für generisches Command nutzen
---
### Schritt 3: MediatR Setup (Phase 4, Step 4.1)
**Erstellen:**
1. `Application/DependencyInjection.cs`
- MediatR registrieren
- FluentValidation registrieren
- ValidationBehavior registrieren
2. `Application/Common/Behaviors/ValidationBehavior.cs`
- Pipeline Behavior für FluentValidation
---
### Schritt 4: ValidatePdf Feature (Phase 4, Step 4.2)
**Erstellen:**
1. `Application/Features/Documents/ValidatePdf/ValidatePdfQuery.cs`
2. `Application/Features/Documents/ValidatePdf/ValidatePdfHandler.cs`
3. `Application/Features/Documents/ValidatePdf/ValidatePdfValidator.cs`
**Mit TDD:**
- `Tests/Unit/Application/Features/ValidatePdf/ValidatePdfHandlerTests.cs`
---
## ?? Empfehlung
**Nächster Sprint:**
1. ? ROADMAP.md ist aktuell
2. **Jetzt:** DevExpressPdfProcessor implementieren (mit TDD)
3. **Dann:** Application Layer aufräumen (ProcessDocument ? ValidatePdf)
4. **Dann:** MediatR Setup + ValidationBehavior
5. **Dann:** ValidatePdf Feature komplett durchziehen
**Vorteil dieses Ansatzes:**
- Wir sehen **echten** Code (DevExpress Integration)
- Wir wissen welche Exceptions geworfen werden
- Application Layer kann darauf aufbauen
- Schneller Feedback-Loop
---
## ?? Build Status
? **Build erfolgreich!** (17.01.2025)
Alle Projekte kompilieren ohne Fehler.
---
## ?? Dokumentation
- ? **ROADMAP.md** vollständig aktualisiert
- ? **STATUS_UPDATE_17_01_2025.md** erstellt (diese Datei)
- ? DevExpress Universal License dokumentiert
- ? Aktueller Projektstand dokumentiert
- ? Nächste Schritte klar definiert
---
**Last Updated:** 17.01.2025
**Status:** Ready für Phase 3, Step 3.2 (DevExpressPdfProcessor)

View File

@@ -0,0 +1,16 @@
using DocumentOperator.Domain.Models.ValueObjects;
namespace DocumentOperator.Application.Common.Interfaces;
public interface IPdfProcessor
{
/// <summary>
/// Validates a PDF and extracts metadata.
/// </summary>
/// <param name="pdfBytes">PDF content as byte array</param>
/// <returns>PDF metadata (page count, size, version, attachments)</returns>
/// <exception cref="Domain.Common.Exceptions.PdfProcessingException">
/// Thrown when PDF is corrupted or cannot be processed
/// </exception>
Task<PdfMetadata> ValidateAsync(byte[] pdfBytes);
}

View File

@@ -17,7 +17,6 @@
</ItemGroup>
<ItemGroup>
<Folder Include="Common\Interfaces\" />
<Folder Include="Common\Behaviors\" />
<Folder Include="Common\DTOs\" />
<Folder Include="Common\Mappings\" />

View File

@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DocumentOperator.Application.Features.Documents.ProcessDocument
{
internal class ProcessDocumentCommand
{
}
}

View File

@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DocumentOperator.Application.Features.Documents.ProcessDocument
{
internal class ProcessDocumentHandler
{
}
}

View File

@@ -1,12 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace DocumentOperator.Application.Features.Documents.ProcessDocument
{
internal class ProcessDocumentValidator
{
}
}

View File

@@ -9,8 +9,6 @@
<ItemGroup>
<Folder Include="Common\Results\" />
<Folder Include="Constants\" />
<Folder Include="Models\Enums\" />
<Folder Include="Models\ValueObjects\" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,10 @@
namespace DocumentOperator.Domain.Models.Enums;
public enum DocumentOperationType
{
Validate,
ExtractAttachments,
Concatenate,
ApplyStamp,
EmbedCertificate
}

View File

@@ -0,0 +1,9 @@
namespace DocumentOperator.Domain.Models.Enums;
public enum ProcessingStatus
{
Pending,
Processing,
Success,
Failed
}

View File

@@ -0,0 +1,59 @@
namespace DocumentOperator.Domain.Models.ValueObjects;
public sealed class Base64String
{
public string Value { get; }
private Base64String(string value)
{
Value = value;
}
public static Base64String Create(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new Common.Exceptions.DomainValidationException("Base64 string cannot be empty.");
// Validierung: Ist es gültiges Base64?
try
{
Convert.FromBase64String(value);
}
catch (FormatException)
{
throw new Common.Exceptions.DomainValidationException("Invalid Base64 format.");
}
return new Base64String(value);
}
public static Base64String FromByteArray(byte[] bytes)
{
if (bytes == null || bytes.Length == 0)
throw new Common.Exceptions.DomainValidationException("Byte array cannot be null or empty.");
var base64 = Convert.ToBase64String(bytes);
return new Base64String(base64);
}
public byte[] ToByteArray()
{
return Convert.FromBase64String(Value);
}
public override string ToString() => Value;
// Equality (wichtig für Value Objects!)
public override bool Equals(object? obj)
{
if (obj is not Base64String other)
return false;
return Value == other.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -0,0 +1,32 @@
namespace DocumentOperator.Domain.Models.ValueObjects;
public sealed class PdfMetadata
{
public int PageCount { get; }
public long FileSizeBytes { get; }
public string PdfVersion { get; }
public bool HasAttachments { get; }
public int AttachmentCount { get; }
// Computed Property (berechnet aus FileSizeBytes)
public double FileSizeMB => FileSizeBytes / 1024.0 / 1024.0;
public PdfMetadata(
int pageCount,
long fileSizeBytes,
string pdfVersion,
bool hasAttachments,
int attachmentCount)
{
PageCount = pageCount;
FileSizeBytes = fileSizeBytes;
PdfVersion = pdfVersion;
HasAttachments = hasAttachments;
AttachmentCount = attachmentCount;
}
public override string ToString()
{
return $"PDF: {PageCount} pages, {FileSizeMB:F2} MB, Version {PdfVersion}, Attachments: {AttachmentCount}";
}
}

View File

@@ -0,0 +1,41 @@
namespace DocumentOperator.Domain.Models.ValueObjects;
public sealed class TenantId
{
public string Value { get; }
private TenantId(string value)
{
Value = value;
}
public static TenantId Create(string value)
{
if (string.IsNullOrWhiteSpace(value))
throw new Common.Exceptions.DomainValidationException("TenantId cannot be empty.");
if (value.Length > 100)
throw new Common.Exceptions.DomainValidationException("TenantId cannot exceed 100 characters.");
// Normalisierung: Lowercase
var normalized = value.Trim().ToLowerInvariant();
return new TenantId(normalized);
}
public override string ToString() => Value;
// Equality
public override bool Equals(object? obj)
{
if (obj is not TenantId other)
return false;
return Value == other.Value;
}
public override int GetHashCode()
{
return Value.GetHashCode();
}
}

View File

@@ -0,0 +1,42 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>
<ItemGroup>
<None Remove="TestData\Pdfs\valid.pdf" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="TestData\Pdfs\valid.pdf" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.0" />
<PackageReference Include="FluentAssertions" Version="7.0.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.1" />
<PackageReference Include="Moq" Version="4.20.72" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\DocumentOperator.Domain\DocumentOperator.Domain.csproj" />
<ProjectReference Include="..\DocumentOperator.Application\DocumentOperator.Application.csproj" />
<ProjectReference Include="..\DocumentOperator.Infrastructure\DocumentOperator.Infrastructure.csproj" />
</ItemGroup>
</Project>

Binary file not shown.

View File

@@ -0,0 +1,146 @@
using System.Reflection;
using DocumentOperator.Application.Common.Interfaces;
using DocumentOperator.Domain.Common.Exceptions;
using DocumentOperator.Domain.Models.ValueObjects;
using DocumentOperator.Infrastructure.Services.PdfProcessing;
using FluentAssertions;
namespace DocumentOperator.Tests.Unit.Infrastructure.Services.PdfProcessing;
/// <summary>
/// Unit tests for DevExpressPdfProcessor.
/// Tests PDF validation and metadata extraction.
/// </summary>
public class DevExpressPdfProcessorTests
{
private readonly IPdfProcessor _sut; // SUT = System Under Test
public DevExpressPdfProcessorTests()
{
// Arrange: Create instance (wird später implementiert)
_sut = new DevExpressPdfProcessor();
}
#region Helper Methods
/// <summary>
/// Loads a test PDF from embedded resources.
/// </summary>
/// <param name="filename">Name of the PDF file (e.g., "valid.pdf")</param>
/// <returns>PDF content as byte array</returns>
private static byte[] LoadTestPdf(string filename)
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = $"DocumentOperator.Tests.TestData.Pdfs.{filename}";
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
throw new FileNotFoundException(
$"Embedded resource '{resourceName}' not found. " +
$"Available resources: {string.Join(", ", assembly.GetManifestResourceNames())}");
}
using var memoryStream = new MemoryStream();
stream.CopyTo(memoryStream);
return memoryStream.ToArray();
}
#endregion
#region ValidateAsync Tests
[Fact]
public async Task ValidateAsync_ValidPdf_ReturnsPdfMetadata()
{
// Arrange
byte[] pdfBytes = LoadTestPdf("valid.pdf");
// Act
var metadata = await _sut.ValidateAsync(pdfBytes);
// Assert
metadata.Should().NotBeNull("a valid PDF should return metadata");
metadata.PageCount.Should().BeGreaterThan(0, "PDF must have at least one page");
metadata.FileSizeBytes.Should().Be(pdfBytes.Length, "file size should match input");
metadata.PdfVersion.Should().NotBeNullOrEmpty("PDF version should be detected");
}
[Fact]
public async Task ValidateAsync_ValidPdf_ReturnsCorrectPageCount()
{
// Arrange
byte[] pdfBytes = LoadTestPdf("valid.pdf");
// Act
var metadata = await _sut.ValidateAsync(pdfBytes);
// Assert
// Deine valid.pdf hat wahrscheinlich 1-5 Seiten - passe an!
metadata.PageCount.Should().BeInRange(1, 100, "test PDF should have reasonable page count");
}
[Fact]
public async Task ValidateAsync_NullBytes_ThrowsPdfProcessingException()
{
// Arrange
byte[]? pdfBytes = null;
// Act
Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes!);
// Assert
await act.Should().ThrowAsync<PdfProcessingException>()
.WithMessage("*null*", "null input should be rejected");
}
[Fact]
public async Task ValidateAsync_EmptyBytes_ThrowsPdfProcessingException()
{
// Arrange
byte[] pdfBytes = Array.Empty<byte>();
// Act
Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes);
// Assert
await act.Should().ThrowAsync<PdfProcessingException>()
.WithMessage("*empty*", "empty input should be rejected");
}
[Fact]
public async Task ValidateAsync_CorruptedPdf_ThrowsPdfProcessingException()
{
// Arrange
byte[] pdfBytes = "This is not a valid PDF content"u8.ToArray();
// Act
Func<Task> act = async () => await _sut.ValidateAsync(pdfBytes);
// Assert
await act.Should().ThrowAsync<PdfProcessingException>()
.WithMessage("*invalid*", "corrupted PDF should throw exception");
}
#endregion
#region Metadata Tests
[Fact]
public async Task ValidateAsync_ValidPdf_FileSizeMBCalculatedCorrectly()
{
// Arrange
byte[] pdfBytes = LoadTestPdf("valid.pdf");
// Act
var metadata = await _sut.ValidateAsync(pdfBytes);
// Assert
double expectedSizeMB = pdfBytes.Length / 1024.0 / 1024.0;
metadata.FileSizeMB.Should().BeApproximately(expectedSizeMB, 0.01,
"FileSizeMB should be calculated correctly from bytes");
}
#endregion
}

View File

@@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentOperator.Infrastruc
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentOperator.Domain", "DocumentOperator.Domain\DocumentOperator.Domain.csproj", "{B4C1C3ED-D3E8-4272-8704-2E73CEA6A0DD}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DocumentOperator.Tests", "DocumentOperator.Tests\DocumentOperator.Tests.csproj", "{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -33,6 +35,10 @@ Global
{B4C1C3ED-D3E8-4272-8704-2E73CEA6A0DD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B4C1C3ED-D3E8-4272-8704-2E73CEA6A0DD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B4C1C3ED-D3E8-4272-8704-2E73CEA6A0DD}.Release|Any CPU.Build.0 = Release|Any CPU
{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D2E997-3DA7-4061-8A50-DBB34BBC3E5A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

1640
ROADMAP.md

File diff suppressed because it is too large Load Diff