End-to-end tests that run against a real, deployed API instance to verify that
all controllers respond correctly over HTTP - bypassing WebApplicationFactory.
Project setup:
- Targets net8.0; xUnit 2.9.3 + FluentAssertions 7.0.0
- References DocumentService.Client, Application and Domain as project refs
- Embeds 4 test PDFs as EmbeddedResources:
valid.pdf, pdfWithSwissQRCode.pdf, pdfWithMoreThanOneAttachment.pdf,
encryptedPdf.pdf
LiveApiTestBase:
- Base URL defaults to http://172.24.12.39:9393, overridable via
LIVE_API_BASE_URL env var
- Constructs typed clients (PdfValidationClient, PdfAttachmentClient,
PdfOperationsClient, SwissQrCodeClient) backed by a single HttpClient
- LoadPdfBytes / LoadPdfStream helpers for EmbeddedResource access
Test coverage per file:
- PdfValidationLiveTests : Stream + byte[] overloads for validate and
validate-pdfa; empty/corrupted inputs assert 4xx/5xx
- PdfAttachmentLiveTests : check + extract endpoints; stream and byte[] variants
- PdfOperationsLiveTests : merge and stamp endpoints
- SwissQrCodeLiveTests : extract Swiss QR bill; invalid content asserts error
Tests are intentionally skipped in CI (no live server); run manually with
the LIVE_API_BASE_URL variable pointing to a running instance.
197 lines
7.6 KiB
C#
197 lines
7.6 KiB
C#
using DocumentService.Application.Common.DTOs;
|
|
using FluentAssertions;
|
|
|
|
namespace DocumentService.Tests.Live.API;
|
|
|
|
/// <summary>
|
|
/// Live tests for PDF validation endpoints.
|
|
/// Uses PdfValidationClient — routes are managed by the client, not the test.
|
|
/// Target: http://172.24.12.39:9393/
|
|
/// </summary>
|
|
[Collection("Live API")]
|
|
public class PdfValidationLiveTests : LiveApiTestBase
|
|
{
|
|
// ──────────────────────────────────────────────────────────────
|
|
// ValidateAsync(Stream) → multipart/form-data → /api/pdf/validation/validate
|
|
// ──────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task Validate_Stream_ValidPdf_ReturnsMetadata()
|
|
{
|
|
using var stream = LoadPdfStream("valid.pdf");
|
|
|
|
var result = await ValidationClient.ValidateAsync(stream);
|
|
|
|
result.Should().NotBeNull();
|
|
result.PageCount.Should().BeGreaterThan(0);
|
|
result.FileSizeBytes.Should().BeGreaterThan(0);
|
|
result.PdfVersion.Should().NotBeNullOrEmpty();
|
|
result.HasAttachments.Should().BeFalse();
|
|
result.AttachmentCount.Should().Be(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Stream_PdfWithAttachments_ReturnsHasAttachmentsTrue()
|
|
{
|
|
using var stream = LoadPdfStream("pdfWithMoreThanOneAttachment.pdf");
|
|
|
|
var result = await ValidationClient.ValidateAsync(stream);
|
|
|
|
result.HasAttachments.Should().BeTrue();
|
|
result.AttachmentCount.Should().BeGreaterThan(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Stream_PdfWithSwissQrCode_ReturnsValidResult()
|
|
{
|
|
using var stream = LoadPdfStream("pdfWithSwissQRCode.pdf");
|
|
|
|
var result = await ValidationClient.ValidateAsync(stream);
|
|
|
|
result.Should().NotBeNull();
|
|
result.PageCount.Should().BeGreaterThan(0);
|
|
result.FileSizeBytes.Should().BeGreaterThan(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Stream_EmptyStream_Throws()
|
|
{
|
|
using var stream = new MemoryStream(Array.Empty<byte>());
|
|
|
|
var act = async () => await ValidationClient.ValidateAsync(stream);
|
|
|
|
await act.Should().ThrowAsync<HttpRequestException>(
|
|
"empty file should be rejected with 400");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Stream_CorruptedContent_Throws()
|
|
{
|
|
// Valid stream shape, but not a PDF
|
|
byte[] garbage = "THIS IS NOT A PDF FILE"u8.ToArray();
|
|
using var stream = new MemoryStream(garbage);
|
|
|
|
var act = async () => await ValidationClient.ValidateAsync(stream);
|
|
|
|
await act.Should().ThrowAsync<HttpRequestException>(
|
|
"corrupted content should be rejected (500)");
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// ValidateAsync(byte[]) → Base64 JSON → /api/pdf/validation/validate
|
|
// ──────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task Validate_Bytes_ValidPdf_ReturnsMetadata()
|
|
{
|
|
byte[] bytes = LoadPdfBytes("valid.pdf");
|
|
|
|
var result = await ValidationClient.ValidateAsync(bytes);
|
|
|
|
result.Should().NotBeNull();
|
|
result.PageCount.Should().BeGreaterThan(0);
|
|
result.FileSizeBytes.Should().BeGreaterThan(0);
|
|
result.PdfVersion.Should().NotBeNullOrEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Bytes_PdfWithAttachments_ReturnsHasAttachmentsTrue()
|
|
{
|
|
byte[] bytes = LoadPdfBytes("pdfWithMoreThanOneAttachment.pdf");
|
|
|
|
var result = await ValidationClient.ValidateAsync(bytes);
|
|
|
|
result.HasAttachments.Should().BeTrue();
|
|
result.AttachmentCount.Should().BeGreaterThan(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task Validate_Bytes_EmptyArray_Throws()
|
|
{
|
|
var act = async () => await ValidationClient.ValidateAsync(Array.Empty<byte>());
|
|
|
|
await act.Should().ThrowAsync<HttpRequestException>(
|
|
"empty byte array encodes to empty Base64, which should be rejected");
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// ValidatePdfAAsync(Stream) → multipart → /api/pdf/validation/validate-pdfa
|
|
// ──────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Stream_ValidPdf_ReturnsComplianceFields()
|
|
{
|
|
using var stream = LoadPdfStream("valid.pdf");
|
|
|
|
var result = await ValidationClient.ValidatePdfAAsync(stream);
|
|
|
|
result.Should().NotBeNull();
|
|
result.IsValid.Should().BeTrue();
|
|
result.PageCount.Should().BeGreaterThan(0);
|
|
result.PdfVersion.Should().NotBeNullOrEmpty();
|
|
result.FileSize.Should().BeGreaterThan(0);
|
|
result.Encrypted.Should().BeFalse();
|
|
// valid.pdf is a regular PDF, not PDF/A
|
|
result.PdfACompliant.Should().BeFalse();
|
|
result.PdfAVersion.Should().BeNullOrEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Stream_EncryptedPdf_ReturnsEncryptedTrue_NotCompliant()
|
|
{
|
|
using var stream = LoadPdfStream("encryptedPdf.pdf");
|
|
|
|
var result = await ValidationClient.ValidatePdfAAsync(stream);
|
|
|
|
result.Should().NotBeNull();
|
|
result.Encrypted.Should().BeTrue();
|
|
result.PdfACompliant.Should().BeFalse("encrypted PDFs cannot be PDF/A compliant");
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Stream_EmptyStream_Throws()
|
|
{
|
|
using var stream = new MemoryStream(Array.Empty<byte>());
|
|
|
|
var act = async () => await ValidationClient.ValidatePdfAAsync(stream);
|
|
|
|
await act.Should().ThrowAsync<HttpRequestException>();
|
|
}
|
|
|
|
// ──────────────────────────────────────────────────────────────
|
|
// ValidatePdfAAsync(byte[]) → Base64 JSON → /api/pdf/validation/validate-pdfa
|
|
// ──────────────────────────────────────────────────────────────
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Bytes_ValidPdf_ReturnsComplianceFields()
|
|
{
|
|
byte[] bytes = LoadPdfBytes("valid.pdf");
|
|
|
|
var result = await ValidationClient.ValidatePdfAAsync(bytes);
|
|
|
|
result.Should().NotBeNull();
|
|
result.IsValid.Should().BeTrue();
|
|
result.PdfACompliant.Should().BeFalse();
|
|
result.PageCount.Should().BeGreaterThan(0);
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Bytes_EncryptedPdf_ReturnsEncryptedTrue()
|
|
{
|
|
byte[] bytes = LoadPdfBytes("encryptedPdf.pdf");
|
|
|
|
var result = await ValidationClient.ValidatePdfAAsync(bytes);
|
|
|
|
result.Encrypted.Should().BeTrue();
|
|
result.PdfACompliant.Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public async Task ValidatePdfA_Bytes_EmptyArray_Throws()
|
|
{
|
|
var act = async () => await ValidationClient.ValidatePdfAAsync(Array.Empty<byte>());
|
|
|
|
await act.Should().ThrowAsync<HttpRequestException>();
|
|
}
|
|
}
|