Refactor: Rename DocumentOperator to DocumentService

Updated project and solution files to rename and restructure
`DocumentOperator` to `DocumentService`. Modified project
references in `DocumentService.API.csproj`, `DocumentService.Application.csproj`,
and `DocumentService.Infrastructure.csproj` to reflect the new
naming convention. Updated `DocumentService.sln` to remove
old project references, add new ones, and adjust configuration
and nested project mappings accordingly. This change aligns
the project structure with the new naming convention.
This commit is contained in:
2026-08-30 21:33:25 +02:00
parent 050cb55bf9
commit 207c778f3a
108 changed files with 37 additions and 37 deletions

View File

@@ -0,0 +1,383 @@
using DocumentService.Application.Common.DTOs;
using DocumentService.Client.Models.Requests;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
using System.Net;
using System.Net.Http.Json;
using Xunit;
namespace DocumentService.Tests.Integration.API;
/// <summary>
/// Integration tests for PdfAttachmentController.
/// Tests /api/pdf/attachments/check endpoint with both multipart and Base64 input.
/// </summary>
public class PdfAttachmentControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly WebApplicationFactory<Program> _factory;
private readonly HttpClient _client;
public PdfAttachmentControllerTests(WebApplicationFactory<Program> factory)
{
_factory = factory;
_client = _factory.CreateClient();
}
#region Helper Methods
/// <summary>
/// Loads a test PDF from embedded resources.
/// </summary>
private static async Task<byte[]> LoadTestPdfAsync(string filename)
{
var assembly = typeof(PdfAttachmentControllerTests).Assembly;
var resourceName = $"DocumentService.Tests.TestData.Pdfs.{filename}";
using var stream = assembly.GetManifestResourceStream(resourceName);
if (stream == null)
{
throw new InvalidOperationException($"Test resource '{resourceName}' not found");
}
using var ms = new MemoryStream();
await stream.CopyToAsync(ms);
return ms.ToArray();
}
#endregion
#region Base64 JSON Tests
[Fact]
public async Task POST_CheckAttachments_Base64_PdfWithoutAttachments_Returns200()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
string base64Pdf = Convert.ToBase64String(pdfBytes);
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
result.Should().NotBeNull();
result!.HasAttachments.Should().BeFalse();
result.AttachmentCount.Should().Be(0);
result.Attachments.Should().BeEmpty();
}
[Fact]
public async Task POST_CheckAttachments_Base64_PdfWithMultipleAttachments_Returns200()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
string base64Pdf = Convert.ToBase64String(pdfBytes);
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
result.Should().NotBeNull();
result!.HasAttachments.Should().BeTrue();
result.AttachmentCount.Should().Be(6, "PDF has exactly 6 attachments");
result.Attachments.Should().HaveCount(6);
// Verify each attachment has required properties
foreach (var attachment in result.Attachments)
{
attachment.FileName.Should().NotBeNullOrEmpty();
attachment.MimeType.Should().NotBeNullOrEmpty();
attachment.Size.Should().BeGreaterThan(0);
}
}
[Fact]
public async Task POST_CheckAttachments_Base64_InvalidBase64_Returns400()
{
// Arrange
var request = new CheckPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var problemDetails = await response.Content.ReadAsStringAsync();
// FormatException message contains "Base-64" (with hyphen)
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
}
[Fact]
public async Task POST_CheckAttachments_Base64_EmptyPdf_Returns400()
{
// Arrange
var request = new CheckPdfAttachmentsRequest { Base64Pdf = string.Empty };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var problemDetails = await response.Content.ReadAsStringAsync();
// Empty Base64 causes FormatException or empty stream error
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
}
#endregion
#region Multipart/Form-Data Tests
[Fact]
public async Task POST_CheckAttachments_Multipart_PdfWithoutAttachments_Returns200()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(pdfBytes);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "pdfWithSwissQRCode.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
result.Should().NotBeNull();
result!.HasAttachments.Should().BeFalse();
result.AttachmentCount.Should().Be(0);
result.Attachments.Should().BeEmpty();
}
[Fact]
public async Task POST_CheckAttachments_Multipart_PdfWithMultipleAttachments_Returns200()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(pdfBytes);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "pdfWithMoreThanOneAttachment.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
result.Should().NotBeNull();
result!.HasAttachments.Should().BeTrue();
result.AttachmentCount.Should().Be(6);
result.Attachments.Should().HaveCount(6);
// Verify first attachment details
var firstAttachment = result.Attachments.First();
firstAttachment.FileName.Should().NotBeNullOrEmpty();
firstAttachment.MimeType.Should().NotBeNullOrEmpty();
firstAttachment.Size.Should().BeGreaterThan(0);
}
[Fact]
public async Task POST_CheckAttachments_Multipart_MissingFile_Returns400()
{
// Arrange
using var content = new MultipartFormDataContent(); // No file added
// Act
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task POST_CheckAttachments_Multipart_CorruptedPdf_Returns500()
{
// Arrange
byte[] corruptedBytes = "This is not a valid PDF content"u8.ToArray();
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(corruptedBytes);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "corrupted.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/check", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
var problemDetails = await response.Content.ReadAsStringAsync();
// Note: Generic error message for security reasons (doesn't expose internal details)
problemDetails.Should().Contain("error");
}
#endregion
#region Edge Cases
[Fact]
public async Task POST_CheckAttachments_Base64_PdfWithSwissQrCode_Returns200()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithSwissQRCode.pdf");
string base64Pdf = Convert.ToBase64String(pdfBytes);
var request = new CheckPdfAttachmentsRequest { Base64Pdf = base64Pdf };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/check", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
var result = await response.Content.ReadFromJsonAsync<AttachmentCheckResult>();
result.Should().NotBeNull();
result!.HasAttachments.Should().BeFalse("Swiss QR PDF has no attachments");
result.AttachmentCount.Should().Be(0);
}
#endregion
#region Extract Attachments Tests (Multipart)
[Fact]
public async Task POST_ExtractAttachments_Multipart_ValidPdfWithAttachments_Returns200WithZip()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(pdfBytes);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "test.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");
byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
zipBytes.Should().NotBeEmpty("ZIP should contain data");
// Verify ZIP structure
using var zipStream = new MemoryStream(zipBytes);
using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
}
[Fact]
public async Task POST_ExtractAttachments_Multipart_EmptyPdf_Returns400()
{
// Arrange
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(Array.Empty<byte>());
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "empty.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task POST_ExtractAttachments_Multipart_CorruptedPdf_Returns500()
{
// Arrange
byte[] corruptedBytes = "This is not a valid PDF content"u8.ToArray();
using var content = new MultipartFormDataContent();
var fileContent = new ByteArrayContent(corruptedBytes);
fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/pdf");
content.Add(fileContent, "file", "corrupted.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/attachments/extract", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
}
#endregion
#region Extract Attachments Tests (Base64)
[Fact]
public async Task POST_ExtractAttachments_Base64_ValidPdfWithAttachments_Returns200WithZip()
{
// Arrange
byte[] pdfBytes = await LoadTestPdfAsync("pdfWithMoreThanOneAttachment.pdf");
string base64Pdf = Convert.ToBase64String(pdfBytes);
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = base64Pdf };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("application/zip");
response.Content.Headers.ContentDisposition?.FileName.Should().Be("attachments.zip");
byte[] zipBytes = await response.Content.ReadAsByteArrayAsync();
zipBytes.Should().NotBeEmpty("ZIP should contain data");
// Verify ZIP structure
using var zipStream = new MemoryStream(zipBytes);
using var zipArchive = new System.IO.Compression.ZipArchive(zipStream, System.IO.Compression.ZipArchiveMode.Read);
zipArchive.Entries.Should().HaveCount(6, "PDF contains 6 attachments");
}
[Fact]
public async Task POST_ExtractAttachments_Base64_InvalidBase64_Returns400()
{
// Arrange
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = "invalid-base64!!!" };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var problemDetails = await response.Content.ReadAsStringAsync();
// FormatException message contains "Base-64" (with hyphen)
problemDetails.Should().MatchRegex("(?i)base.?64", "should contain Base64 validation error");
}
[Fact]
public async Task POST_ExtractAttachments_Base64_EmptyPdf_Returns400()
{
// Arrange
var request = new ExtractPdfAttachmentsRequest { Base64Pdf = string.Empty };
// Act
var response = await _client.PostAsJsonAsync("/api/pdf/attachments/extract", request);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
var problemDetails = await response.Content.ReadAsStringAsync();
problemDetails.Should().MatchRegex("(Base64|empty|stream)", "should contain validation error message");
}
#endregion
}