using DocumentService.Application.Common.DTOs;
using FluentAssertions;
namespace DocumentService.Tests.Live.API;
///
/// Live tests for PDF attachment endpoints (check + extract).
/// Uses PdfAttachmentClient — routes managed by the client.
/// Target: http://172.24.12.39:9393/
///
[Collection("Live API")]
public class PdfAttachmentLiveTests : LiveApiTestBase
{
// ──────────────────────────────────────────────────────────────
// CheckAsync(Stream) → multipart → /api/pdf/attachments/check
// ──────────────────────────────────────────────────────────────
[Fact]
public async Task Check_Stream_PdfWithoutAttachments_ReturnsHasAttachmentsFalse()
{
using var stream = LoadPdfStream("valid.pdf");
var result = await AttachmentClient.CheckAsync(stream);
result.Should().NotBeNull();
result.HasAttachments.Should().BeFalse();
result.AttachmentCount.Should().Be(0);
result.Attachments.Should().BeEmpty();
}
[Fact]
public async Task Check_Stream_PdfWith6Attachments_ReturnsAllAttachmentsWithMetadata()
{
using var stream = LoadPdfStream("pdfWithMoreThanOneAttachment.pdf");
var result = await AttachmentClient.CheckAsync(stream);
result.HasAttachments.Should().BeTrue();
result.AttachmentCount.Should().Be(6, "PDF has exactly 6 attachments");
result.Attachments.Should().HaveCount(6);
foreach (var attachment in result.Attachments)
{
attachment.FileName.Should().NotBeNullOrEmpty();
attachment.MimeType.Should().NotBeNullOrEmpty();
attachment.Size.Should().BeGreaterThan(0);
}
}
[Fact]
public async Task Check_Stream_PdfWithSwissQrCode_HasNoAttachments()
{
// QR code PDF has no embedded file attachments
using var stream = LoadPdfStream("pdfWithSwissQRCode.pdf");
var result = await AttachmentClient.CheckAsync(stream);
result.HasAttachments.Should().BeFalse();
result.AttachmentCount.Should().Be(0);
}
[Fact]
public async Task Check_Stream_EmptyStream_Throws()
{
using var stream = new MemoryStream(Array.Empty());
var act = async () => await AttachmentClient.CheckAsync(stream);
await act.Should().ThrowAsync();
}
[Fact]
public async Task Check_Stream_CorruptedContent_Throws()
{
byte[] garbage = "This is not a valid PDF content"u8.ToArray();
using var stream = new MemoryStream(garbage);
var act = async () => await AttachmentClient.CheckAsync(stream);
await act.Should().ThrowAsync(
"corrupted PDF should return 500 → client throws");
}
// ──────────────────────────────────────────────────────────────
// CheckAsync(byte[]) → Base64 JSON → /api/pdf/attachments/check
// ──────────────────────────────────────────────────────────────
[Fact]
public async Task Check_Bytes_PdfWithoutAttachments_ReturnsHasAttachmentsFalse()
{
byte[] bytes = LoadPdfBytes("valid.pdf");
var result = await AttachmentClient.CheckAsync(bytes);
result.HasAttachments.Should().BeFalse();
result.AttachmentCount.Should().Be(0);
}
[Fact]
public async Task Check_Bytes_PdfWith6Attachments_ReturnsAllAttachmentsWithMetadata()
{
byte[] bytes = LoadPdfBytes("pdfWithMoreThanOneAttachment.pdf");
var result = await AttachmentClient.CheckAsync(bytes);
result.HasAttachments.Should().BeTrue();
result.AttachmentCount.Should().Be(6);
result.Attachments.Should().HaveCount(6);
foreach (var attachment in result.Attachments)
{
attachment.FileName.Should().NotBeNullOrEmpty();
attachment.MimeType.Should().NotBeNullOrEmpty();
attachment.Size.Should().BeGreaterThan(0);
}
}
[Fact]
public async Task Check_Bytes_EmptyArray_Throws()
{
var act = async () => await AttachmentClient.CheckAsync(Array.Empty());
await act.Should().ThrowAsync();
}
// ──────────────────────────────────────────────────────────────
// ExtractAsync(Stream) → multipart → /api/pdf/attachments/extract
// ──────────────────────────────────────────────────────────────
[Fact]
public async Task Extract_Stream_PdfWith6Attachments_Returns6NamedStreams()
{
using var stream = LoadPdfStream("pdfWithMoreThanOneAttachment.pdf");
var attachments = await AttachmentClient.ExtractAsync(stream);
attachments.Should().HaveCount(6, "PDF contains 6 attachments");
foreach (var (name, content) in attachments)
{
name.Should().NotBeNullOrEmpty();
content.Length.Should().BeGreaterThan(0);
content.Dispose();
}
}
[Fact]
public async Task Extract_Stream_AttachmentNamesAreUnique()
{
using var stream = LoadPdfStream("pdfWithMoreThanOneAttachment.pdf");
var attachments = await AttachmentClient.ExtractAsync(stream);
try
{
attachments.Keys.Should().OnlyHaveUniqueItems(
"ZIP entry names must be unique within the archive");
}
finally
{
foreach (var s in attachments.Values) s.Dispose();
}
}
[Fact]
public async Task Extract_Stream_EmptyStream_Throws()
{
using var stream = new MemoryStream(Array.Empty());
var act = async () => await AttachmentClient.ExtractAsync(stream);
await act.Should().ThrowAsync();
}
[Fact]
public async Task Extract_Stream_CorruptedContent_Throws()
{
byte[] garbage = "This is not a valid PDF content"u8.ToArray();
using var stream = new MemoryStream(garbage);
var act = async () => await AttachmentClient.ExtractAsync(stream);
await act.Should().ThrowAsync();
}
// ──────────────────────────────────────────────────────────────
// ExtractAsync(byte[]) → Base64 JSON → /api/pdf/attachments/extract
// ──────────────────────────────────────────────────────────────
[Fact]
public async Task Extract_Bytes_PdfWith6Attachments_Returns6NamedStreams()
{
byte[] bytes = LoadPdfBytes("pdfWithMoreThanOneAttachment.pdf");
var attachments = await AttachmentClient.ExtractAsync(bytes);
attachments.Should().HaveCount(6);
foreach (var (name, content) in attachments)
{
name.Should().NotBeNullOrEmpty();
content.Length.Should().BeGreaterThan(0);
content.Dispose();
}
}
[Fact]
public async Task Extract_Bytes_AttachmentContentsAreNonEmpty()
{
byte[] bytes = LoadPdfBytes("pdfWithMoreThanOneAttachment.pdf");
var attachments = await AttachmentClient.ExtractAsync(bytes);
try
{
foreach (var (_, content) in attachments)
{
((MemoryStream)content).ToArray().Should().NotBeEmpty(
"every extracted attachment must have content");
}
}
finally
{
foreach (var s in attachments.Values) s.Dispose();
}
}
[Fact]
public async Task Extract_Bytes_EmptyArray_Throws()
{
var act = async () => await AttachmentClient.ExtractAsync(Array.Empty());
await act.Should().ThrowAsync();
}
}