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:
@@ -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
|
||||
}
|
||||
Reference in New Issue
Block a user