using DocumentService.Application.Common.DTOs; using FluentAssertions; namespace DocumentService.Tests.Live.API; /// /// Live tests for Swiss QR Code extraction endpoint. /// Uses SwissQrCodeClient — routes managed by the client. /// Target: http://172.24.12.39:9393/ /// [Collection("Live API")] public class SwissQrCodeLiveTests : LiveApiTestBase { // ────────────────────────────────────────────────────────────── // ExtractAsync(Stream) → multipart → /api/pdf/qr-code/extract-swiss // ────────────────────────────────────────────────────────────── [Fact] public async Task Extract_Stream_PdfWithQrCode_ReturnsBillDetails() { using var stream = LoadPdfStream("pdfWithSwissQRCode.pdf"); var result = await QrCodeClient.ExtractAsync(stream); result.Should().NotBeNull(); result.Bill.Should().NotBeNull("bill data must be extracted"); result.RawLines.Should().NotBeEmpty("raw QR payload lines must be present"); // Swiss QR Bill Standard 2.0 payload always starts with "SPC" result.RawLines.Should().Contain(line => line == "SPC", "first SPC line is mandated by Swiss QR Bill Standard 2.0"); } [Fact] public async Task Extract_Stream_PdfWithQrCode_BillHasMandatoryFields() { using var stream = LoadPdfStream("pdfWithSwissQRCode.pdf"); var result = await QrCodeClient.ExtractAsync(stream); var bill = result.Bill; bill.Should().NotBeNull(); bill!.Account.Should().NotBeNullOrEmpty("IBAN/QR-IBAN is mandatory per standard"); bill.Creditor.Should().NotBeNull("Creditor is mandatory"); bill.Creditor!.Name.Should().NotBeNullOrEmpty("Creditor name is mandatory"); } [Fact] public async Task Extract_Stream_PdfWithoutQrCode_Throws404() { // valid.pdf has no QR code using var stream = LoadPdfStream("valid.pdf"); var act = async () => await QrCodeClient.ExtractAsync(stream); // Client calls EnsureSuccessStatusCode → HttpRequestException on 404 await act.Should().ThrowAsync( "PDF without QR code must return 404"); } [Fact] public async Task Extract_Stream_EmptyStream_Throws() { using var stream = new MemoryStream(Array.Empty()); var act = async () => await QrCodeClient.ExtractAsync(stream); await act.Should().ThrowAsync(); } [Fact] public async Task Extract_Stream_WithRawFlag_ReturnsRawLines() { using var stream = LoadPdfStream("pdfWithSwissQRCode.pdf"); // raw=true → API returns full unprocessed payload lines var result = await QrCodeClient.ExtractAsync(stream, raw: true); result.Should().NotBeNull(); result.RawLines.Should().NotBeEmpty(); } // ────────────────────────────────────────────────────────────── // ExtractAsync(byte[]) → Base64 JSON → /api/pdf/qr-code/extract-swiss // ────────────────────────────────────────────────────────────── [Fact] public async Task Extract_Bytes_PdfWithQrCode_ReturnsBillDetails() { byte[] bytes = LoadPdfBytes("pdfWithSwissQRCode.pdf"); var result = await QrCodeClient.ExtractAsync(bytes); result.Should().NotBeNull(); result.Bill.Should().NotBeNull(); result.RawLines.Should().NotBeEmpty(); } [Fact] public async Task Extract_Bytes_PdfWithQrCode_BillHasMandatoryFields() { byte[] bytes = LoadPdfBytes("pdfWithSwissQRCode.pdf"); var result = await QrCodeClient.ExtractAsync(bytes); result.Bill!.Account.Should().NotBeNullOrEmpty(); result.Bill.Creditor.Should().NotBeNull(); result.Bill.Creditor!.Name.Should().NotBeNullOrEmpty(); } [Fact] public async Task Extract_Bytes_PdfWithoutQrCode_Throws404() { byte[] bytes = LoadPdfBytes("valid.pdf"); var act = async () => await QrCodeClient.ExtractAsync(bytes); await act.Should().ThrowAsync( "PDF without QR code must return 404"); } [Fact] public async Task Extract_Bytes_EmptyArray_Throws() { var act = async () => await QrCodeClient.ExtractAsync(Array.Empty()); await act.Should().ThrowAsync(); } }