test: Add unit tests for DocumentService.Client
This commit is contained in:
56
DocumentOperator.Tests/Unit/Client/MockHttpMessageHandler.cs
Normal file
56
DocumentOperator.Tests/Unit/Client/MockHttpMessageHandler.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Reusable fake <see cref="HttpMessageHandler"/> for unit-testing HTTP clients.
|
||||
/// Captures the outgoing request and returns the configured response.
|
||||
/// </summary>
|
||||
internal sealed class MockHttpMessageHandler : HttpMessageHandler
|
||||
{
|
||||
private readonly HttpResponseMessage _response;
|
||||
|
||||
/// <summary>The last request that was sent through this handler.</summary>
|
||||
public HttpRequestMessage? LastRequest { get; private set; }
|
||||
|
||||
public MockHttpMessageHandler(HttpResponseMessage response)
|
||||
{
|
||||
_response = response;
|
||||
}
|
||||
|
||||
// ?? convenience factories ????????????????????????????????????????????????
|
||||
|
||||
/// <summary>Creates a handler that returns 200 OK with a JSON-serialised body.</summary>
|
||||
public static MockHttpMessageHandler ReturningJson<T>(T body, HttpStatusCode status = HttpStatusCode.OK)
|
||||
{
|
||||
var json = JsonSerializer.Serialize(body, new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase });
|
||||
var response = new HttpResponseMessage(status)
|
||||
{
|
||||
Content = new StringContent(json, Encoding.UTF8, "application/json")
|
||||
};
|
||||
return new MockHttpMessageHandler(response);
|
||||
}
|
||||
|
||||
/// <summary>Creates a handler that returns 200 OK with raw bytes as the body.</summary>
|
||||
public static MockHttpMessageHandler ReturningBytes(byte[] bytes, string mediaType = "application/octet-stream", HttpStatusCode status = HttpStatusCode.OK)
|
||||
{
|
||||
var response = new HttpResponseMessage(status)
|
||||
{
|
||||
Content = new ByteArrayContent(bytes) { Headers = { ContentType = new(mediaType) } }
|
||||
};
|
||||
return new MockHttpMessageHandler(response);
|
||||
}
|
||||
|
||||
/// <summary>Creates a handler that returns the given status code with no body.</summary>
|
||||
public static MockHttpMessageHandler ReturningStatus(HttpStatusCode status)
|
||||
=> new(new HttpResponseMessage(status));
|
||||
|
||||
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
|
||||
{
|
||||
LastRequest = request;
|
||||
return Task.FromResult(_response);
|
||||
}
|
||||
}
|
||||
221
DocumentOperator.Tests/Unit/Client/PdfAttachmentClientTests.cs
Normal file
221
DocumentOperator.Tests/Unit/Client/PdfAttachmentClientTests.cs
Normal file
@@ -0,0 +1,221 @@
|
||||
using DocumentService.Client.Clients;
|
||||
using DocumentService.Client.Interfaces;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.IO.Compression;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="PdfAttachmentClient"/>.
|
||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
||||
/// </summary>
|
||||
public class PdfAttachmentClientTests
|
||||
{
|
||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||||
|
||||
private static (PdfAttachmentClient client, MockHttpMessageHandler handler) BuildJson<T>(T body)
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningJson(body);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static (PdfAttachmentClient client, MockHttpMessageHandler handler) BuildBytes(byte[] bytes, string mediaType = "application/zip")
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningBytes(bytes, mediaType);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||||
|
||||
/// <summary>Builds a minimal valid ZIP containing the given entries.</summary>
|
||||
private static byte[] BuildZip(Dictionary<string, string> entries)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
using (var archive = new ZipArchive(ms, ZipArchiveMode.Create, leaveOpen: true))
|
||||
{
|
||||
foreach (var (name, content) in entries)
|
||||
{
|
||||
var entry = archive.CreateEntry(name);
|
||||
using var writer = new StreamWriter(entry.Open());
|
||||
writer.Write(content);
|
||||
}
|
||||
}
|
||||
return ms.ToArray();
|
||||
}
|
||||
|
||||
// ?? CheckAttachmentsAsync (Stream) ???????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAttachmentsAsync_Stream_SendsMultipartPost()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new AttachmentCheckResult
|
||||
{
|
||||
HasAttachments = true,
|
||||
AttachmentCount = 1,
|
||||
Attachments = new List<AttachmentMetadata>
|
||||
{
|
||||
new() { FileName = "factur-x.xml", MimeType = "application/xml", Size = 512 }
|
||||
}
|
||||
};
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.CheckAttachmentsAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.HasAttachments.Should().BeTrue();
|
||||
result.AttachmentCount.Should().Be(1);
|
||||
result.Attachments[0].FileName.Should().Be("factur-x.xml");
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/attachments/check");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAttachmentsAsync_Stream_PdfWithNoAttachments_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new AttachmentCheckResult { HasAttachments = false, AttachmentCount = 0 };
|
||||
var (client, _) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.CheckAttachmentsAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.HasAttachments.Should().BeFalse();
|
||||
result.AttachmentCount.Should().Be(0);
|
||||
result.Attachments.Should().BeEmpty();
|
||||
}
|
||||
|
||||
// ?? CheckAttachmentsAsync (byte[]) ???????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAttachmentsAsync_Bytes_SendsJsonWithBase64()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new AttachmentCheckResult { HasAttachments = false };
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
await client.CheckAttachmentsAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
// ?? ExtractAttachmentsAsync (Stream) — ZIP unzip ?????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractAttachmentsAsync_Stream_UnzipsAndReturnsDictionary()
|
||||
{
|
||||
// Arrange
|
||||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||||
{
|
||||
["factur-x.xml"] = "<invoice>test</invoice>",
|
||||
["readme.txt"] = "Hello World"
|
||||
});
|
||||
var (client, handler) = BuildBytes(zipBytes);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractAttachmentsAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.Should().HaveCount(2);
|
||||
result.Should().ContainKey("factur-x.xml");
|
||||
result.Should().ContainKey("readme.txt");
|
||||
|
||||
using var xmlStream = result["factur-x.xml"];
|
||||
var xmlContent = await new StreamReader(xmlStream).ReadToEndAsync();
|
||||
xmlContent.Should().Be("<invoice>test</invoice>");
|
||||
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/attachments/extract");
|
||||
|
||||
// Cleanup
|
||||
foreach (var s in result.Values) s.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractAttachmentsAsync_Stream_WithSingleEntry_ReturnsOneItem()
|
||||
{
|
||||
// Arrange
|
||||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||||
{
|
||||
["data.xml"] = "<root/>"
|
||||
});
|
||||
var (client, _) = BuildBytes(zipBytes);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractAttachmentsAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.Should().HaveCount(1);
|
||||
result.Should().ContainKey("data.xml");
|
||||
|
||||
foreach (var s in result.Values) s.Dispose();
|
||||
}
|
||||
|
||||
// ?? ExtractAttachmentsAsync (byte[]) — ZIP unzip ?????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractAttachmentsAsync_Bytes_SendsJsonAndUnzips()
|
||||
{
|
||||
// Arrange
|
||||
var zipBytes = BuildZip(new Dictionary<string, string>
|
||||
{
|
||||
["invoice.xml"] = "<invoice/>"
|
||||
});
|
||||
var (client, handler) = BuildBytes(zipBytes);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractAttachmentsAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
result.Should().ContainKey("invoice.xml");
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
|
||||
foreach (var s in result.Values) s.Dispose();
|
||||
}
|
||||
|
||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task CheckAttachmentsAsync_WhenApiReturns404_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.NotFound);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.CheckAttachmentsAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractAttachmentsAsync_WhenApiReturns500_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.InternalServerError);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfAttachmentClient(httpClient, NullLogger<PdfAttachmentClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ExtractAttachmentsAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
}
|
||||
236
DocumentOperator.Tests/Unit/Client/PdfOperationsClientTests.cs
Normal file
236
DocumentOperator.Tests/Unit/Client/PdfOperationsClientTests.cs
Normal file
@@ -0,0 +1,236 @@
|
||||
using DocumentService.Client.Clients;
|
||||
using DocumentService.Client.Interfaces;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using DocumentService.Client.Models.ValueObjects;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="PdfOperationsClient"/>.
|
||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
||||
/// </summary>
|
||||
public class PdfOperationsClientTests
|
||||
{
|
||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||||
|
||||
private static (PdfOperationsClient client, MockHttpMessageHandler handler) BuildBytes(byte[] bytes = null!)
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningBytes(bytes ?? "merged-pdf"u8.ToArray(), "application/pdf");
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfOperationsClient(httpClient, NullLogger<PdfOperationsClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||||
|
||||
private static AddAnnotationBase64Request FakeAnnotationRequest() => new()
|
||||
{
|
||||
Base64Pdf = string.Empty,
|
||||
AnnotationType = AnnotationType.TextMarkup,
|
||||
PageNumber = 1,
|
||||
X1 = 10, Y1 = 20, Width = 100, Height = 30,
|
||||
Color = "FFFF00",
|
||||
TextMarkupStyle = TextMarkupStyle.Highlight,
|
||||
Origin = AnnotationOrigin.TopLeft
|
||||
};
|
||||
|
||||
private static AddStampBase64Request FakeStampRequest() => new()
|
||||
{
|
||||
Base64Pdf = string.Empty,
|
||||
StampType = StampType.Text,
|
||||
X = 100, Y = 50,
|
||||
Text = "CONFIDENTIAL",
|
||||
FontSize = 24,
|
||||
Color = "FF0000",
|
||||
Opacity = 0.5,
|
||||
Placement = StampPlacement.Foreground,
|
||||
Origin = AnnotationOrigin.BottomLeft
|
||||
};
|
||||
|
||||
// ?? MergeAsync (Streams) ?????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task MergeAsync_Streams_SendsMultipartWithAllFiles()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var streams = new List<Stream>
|
||||
{
|
||||
new MemoryStream(FakePdfBytes()),
|
||||
new MemoryStream(FakePdfBytes())
|
||||
};
|
||||
|
||||
// Act
|
||||
using var result = await client.MergeAsync(streams);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/operations/merge");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
|
||||
foreach (var s in streams) s.Dispose();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeAsync_Streams_ReturnsMergedPdfStream()
|
||||
{
|
||||
// Arrange
|
||||
var expectedBytes = "merged-content"u8.ToArray();
|
||||
var (client, _) = BuildBytes(expectedBytes);
|
||||
|
||||
// Act
|
||||
using var result = await client.MergeAsync(new[] { new MemoryStream(FakePdfBytes()), new MemoryStream(FakePdfBytes()) });
|
||||
var actualBytes = await result.ReadAllBytesAsync();
|
||||
|
||||
// Assert
|
||||
actualBytes.Should().BeEquivalentTo(expectedBytes);
|
||||
}
|
||||
|
||||
// ?? MergeAsync (byte[][]) ????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task MergeAsync_ByteArrays_SendsJsonWithBase64List()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
|
||||
// Act
|
||||
using var result = await client.MergeAsync(new[] { FakePdfBytes(), FakePdfBytes() });
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdfs").GetArrayLength().Should().Be(2);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task MergeAsync_ByteArrays_WithPageRanges_IncludesPageRangesInJson()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var pageRanges = new List<string?> { "1-2", null };
|
||||
|
||||
// Act
|
||||
await client.MergeAsync(new[] { FakePdfBytes(), FakePdfBytes() }, pageRanges);
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
var body = await handler.LastRequest!.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("pageRanges").GetArrayLength().Should().Be(2);
|
||||
}
|
||||
|
||||
// ?? AnnotateAsync (Stream) ????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateAsync_Stream_SendsMultipartWithAnnotationFields()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var request = FakeAnnotationRequest();
|
||||
|
||||
// Act
|
||||
using var result = await client.AnnotateAsync(new MemoryStream(FakePdfBytes()), request);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/operations/annotate");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
// ?? AnnotateAsync (byte[]) ????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task AnnotateAsync_Bytes_SendsJsonWithBase64Pdf()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var request = FakeAnnotationRequest();
|
||||
|
||||
// Act
|
||||
await client.AnnotateAsync(FakePdfBytes(), request);
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
// annotationType serializes as integer by default (TextMarkup = 0)
|
||||
doc.RootElement.GetProperty("annotationType").GetInt32().Should().Be((int)AnnotationType.TextMarkup);
|
||||
}
|
||||
|
||||
// ?? StampAsync (Stream) ???????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task StampAsync_Stream_SendsMultipartWithStampFields()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var request = FakeStampRequest();
|
||||
|
||||
// Act
|
||||
using var result = await client.StampAsync(new MemoryStream(FakePdfBytes()), request);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/operations/stamp");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
// ?? StampAsync (byte[]) ???????????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task StampAsync_Bytes_SendsJsonWithBase64Pdf()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes();
|
||||
var request = FakeStampRequest();
|
||||
|
||||
// Act
|
||||
await client.StampAsync(FakePdfBytes(), request);
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
// stampType serializes as integer by default (Text = 0)
|
||||
doc.RootElement.GetProperty("stampType").GetInt32().Should().Be((int)StampType.Text);
|
||||
}
|
||||
|
||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task MergeAsync_WhenApiReturns400_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.BadRequest);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfOperationsClient(httpClient, NullLogger<PdfOperationsClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.MergeAsync(new[] { new MemoryStream(FakePdfBytes()), new MemoryStream(FakePdfBytes()) }))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
}
|
||||
|
||||
// ?? local helper extension ???????????????????????????????????????????????????
|
||||
|
||||
file static class StreamHelper
|
||||
{
|
||||
public static async Task<byte[]> ReadAllBytesAsync(this Stream stream)
|
||||
{
|
||||
using var ms = new MemoryStream();
|
||||
await stream.CopyToAsync(ms);
|
||||
return ms.ToArray();
|
||||
}
|
||||
}
|
||||
168
DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs
Normal file
168
DocumentOperator.Tests/Unit/Client/PdfValidationClientTests.cs
Normal file
@@ -0,0 +1,168 @@
|
||||
using DocumentService.Client.Clients;
|
||||
using DocumentService.Client.Interfaces;
|
||||
using DocumentService.Client.Models.Requests;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="PdfValidationClient"/>.
|
||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
||||
/// </summary>
|
||||
public class PdfValidationClientTests
|
||||
{
|
||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||||
|
||||
private static (PdfValidationClient client, MockHttpMessageHandler handler) Build<T>(T responseBody)
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningJson(responseBody);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||||
|
||||
// ?? ValidatePdfAsync (Stream) ????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAsync_Stream_SendsMultipartPost()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PdfValidationResult { PageCount = 3, PdfVersion = "1.7", FileSizeBytes = 2048 };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ValidatePdfAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.PageCount.Should().Be(3);
|
||||
result.PdfVersion.Should().Be("1.7");
|
||||
handler.LastRequest!.Method.Should().Be(HttpMethod.Post);
|
||||
handler.LastRequest.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAsync_Stream_ThrowsWhenApiReturnsNull()
|
||||
{
|
||||
// Arrange — API returns JSON null
|
||||
var handler = MockHttpMessageHandler.ReturningJson<PdfValidationResult?>(null);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<InvalidOperationException>();
|
||||
}
|
||||
|
||||
// ?? ValidatePdfAsync (byte[]) ????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAsync_Bytes_SendsJsonWithBase64()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PdfValidationResult { PageCount = 1, IsEncrypted = false };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ValidatePdfAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
result.PageCount.Should().Be(1);
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
// ?? ValidatePdfAAsync (Stream) ???????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAAsync_Stream_SendsMultipartPost()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PdfAValidationResult { IsValid = true, PdfAVersion = "PDF/A-3b", PageCount = 2 };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeTrue();
|
||||
result.PdfAVersion.Should().Be("PDF/A-3b");
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAAsync_Stream_WithErrors_ReturnsErrors()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PdfAValidationResult
|
||||
{
|
||||
IsValid = false,
|
||||
Errors = new List<string> { "Missing embedded font", "Encryption not allowed" }
|
||||
};
|
||||
var (client, _) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ValidatePdfAAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.IsValid.Should().BeFalse();
|
||||
result.Errors.Should().HaveCount(2).And.Contain("Missing embedded font");
|
||||
}
|
||||
|
||||
// ?? ValidatePdfAAsync (byte[]) ???????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAAsync_Bytes_SendsJson()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new PdfAValidationResult { IsValid = true };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
await client.ValidatePdfAAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/validation/validate-pdfa");
|
||||
handler.LastRequest.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
}
|
||||
|
||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAsync_WhenApiReturns400_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.BadRequest);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ValidatePdfAsync_WhenApiReturns500_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.InternalServerError);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new PdfValidationClient(httpClient, NullLogger<PdfValidationClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ValidatePdfAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
}
|
||||
123
DocumentOperator.Tests/Unit/Client/SwissQrCodeClientTests.cs
Normal file
123
DocumentOperator.Tests/Unit/Client/SwissQrCodeClientTests.cs
Normal file
@@ -0,0 +1,123 @@
|
||||
using DocumentService.Client.Clients;
|
||||
using DocumentService.Client.Interfaces;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="SwissQrCodeClient"/>.
|
||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
||||
/// </summary>
|
||||
public class SwissQrCodeClientTests
|
||||
{
|
||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||||
|
||||
private static (SwissQrCodeClient client, MockHttpMessageHandler handler) Build<T>(T body)
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningJson(body);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new SwissQrCodeClient(httpClient, NullLogger<SwissQrCodeClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||||
|
||||
// ?? ExtractSwissQrCodeAsync (Stream) — parsed Bill ???????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_Stream_ParsedMode_SendsMultipartToCorrectEndpoint()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new SwissQrCodeExtractionResult { Bill = new { Iban = "CH93-0076-2011-6238-5295-7" } };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractSwissQrCodeAsync(new MemoryStream(FakePdfBytes()), raw: false);
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/qr-code/extract-swiss?raw=False");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_Stream_RawMode_SendsRawFlagInUrl()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new SwissQrCodeExtractionResult { RawLines = new List<string> { "SPC", "0200", "1" } };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractSwissQrCodeAsync(new MemoryStream(FakePdfBytes()), raw: true);
|
||||
|
||||
// Assert
|
||||
result.RawLines.Should().HaveCount(3).And.StartWith("SPC");
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/qr-code/extract-swiss?raw=True");
|
||||
}
|
||||
|
||||
// ?? ExtractSwissQrCodeAsync (byte[]) ?????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_Bytes_SendsJsonWithBase64()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new SwissQrCodeExtractionResult();
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
await client.ExtractSwissQrCodeAsync(FakePdfBytes(), raw: false);
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_Bytes_RawMode_IncludesRawFlagInUrl()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new SwissQrCodeExtractionResult { RawLines = new List<string> { "SPC" } };
|
||||
var (client, handler) = Build(expected);
|
||||
|
||||
// Act
|
||||
await client.ExtractSwissQrCodeAsync(FakePdfBytes(), raw: true);
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/qr-code/extract-swiss?raw=True");
|
||||
}
|
||||
|
||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_WhenApiReturns404_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.NotFound);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new SwissQrCodeClient(httpClient, NullLogger<SwissQrCodeClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ExtractSwissQrCodeAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractSwissQrCodeAsync_WhenApiReturns500_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.InternalServerError);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new SwissQrCodeClient(httpClient, NullLogger<SwissQrCodeClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.ExtractSwissQrCodeAsync(FakePdfBytes()))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
}
|
||||
199
DocumentOperator.Tests/Unit/Client/ZugferdClientTests.cs
Normal file
199
DocumentOperator.Tests/Unit/Client/ZugferdClientTests.cs
Normal file
@@ -0,0 +1,199 @@
|
||||
using DocumentService.Client.Clients;
|
||||
using DocumentService.Client.Interfaces;
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace DocumentService.Tests.Unit.Client;
|
||||
|
||||
/// <summary>
|
||||
/// Unit tests for <see cref="ZugferdClient"/>.
|
||||
/// All tests use a fake <see cref="MockHttpMessageHandler"/> — no real HTTP calls are made.
|
||||
/// </summary>
|
||||
public class ZugferdClientTests
|
||||
{
|
||||
// ?? helpers ?????????????????????????????????????????????????????????????
|
||||
|
||||
private static (ZugferdClient client, MockHttpMessageHandler handler) BuildJson<T>(T body)
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningJson(body);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new ZugferdClient(httpClient, NullLogger<ZugferdClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static (ZugferdClient client, MockHttpMessageHandler handler) BuildBytes(byte[] bytes, string mediaType = "application/xml")
|
||||
{
|
||||
var handler = MockHttpMessageHandler.ReturningBytes(bytes, mediaType);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new ZugferdClient(httpClient, NullLogger<ZugferdClient>.Instance);
|
||||
return (client, handler);
|
||||
}
|
||||
|
||||
private static byte[] FakePdfBytes() => "fake-pdf-content"u8.ToArray();
|
||||
private static byte[] FakeXmlBytes() => "<invoice>test</invoice>"u8.ToArray();
|
||||
|
||||
// ?? HasZugferdAsync (Stream) ?????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task HasZugferdAsync_Stream_WhenZugferdPresent_ReturnsTrue()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new ZugferdCheckResult { HasZugferd = true, Version = "2.1", Profile = "EN 16931" };
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.HasZugferdAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.HasZugferd.Should().BeTrue();
|
||||
result.Version.Should().Be("2.1");
|
||||
result.Profile.Should().Be("EN 16931");
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/zugferd/has-zugferd");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task HasZugferdAsync_Stream_WhenNoZugferd_ReturnsFalse()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new ZugferdCheckResult { HasZugferd = false };
|
||||
var (client, _) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.HasZugferdAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.HasZugferd.Should().BeFalse();
|
||||
result.Version.Should().BeNull();
|
||||
}
|
||||
|
||||
// ?? HasZugferdAsync (byte[]) ?????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task HasZugferdAsync_Bytes_SendsJsonWithBase64()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new ZugferdCheckResult { HasZugferd = true };
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
await client.HasZugferdAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
var body = await handler.LastRequest.Content!.ReadAsStringAsync();
|
||||
var doc = JsonDocument.Parse(body);
|
||||
doc.RootElement.GetProperty("base64Pdf").GetString().Should().NotBeNullOrEmpty();
|
||||
}
|
||||
|
||||
// ?? ExtractZugferdAsync (Stream) — raw XML stream ?????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractZugferdAsync_Stream_SendsMultipartWithAsFileTrue()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes(FakeXmlBytes());
|
||||
|
||||
// Act
|
||||
using var result = await client.ExtractZugferdAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.Should().NotBeNull();
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/zugferd/extract?asFile=true");
|
||||
handler.LastRequest.Content.Should().BeOfType<MultipartFormDataContent>();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractZugferdAsync_Stream_ReturnsXmlContent()
|
||||
{
|
||||
// Arrange
|
||||
var xmlBytes = "<root><invoice/></root>"u8.ToArray();
|
||||
var (client, _) = BuildBytes(xmlBytes);
|
||||
|
||||
// Act
|
||||
using var result = await client.ExtractZugferdAsync(new MemoryStream(FakePdfBytes()));
|
||||
var content = await new StreamReader(result).ReadToEndAsync();
|
||||
|
||||
// Assert
|
||||
content.Should().Be("<root><invoice/></root>");
|
||||
}
|
||||
|
||||
// ?? ExtractZugferdAsync (byte[]) ?????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractZugferdAsync_Bytes_SendsJsonWithFormatFile()
|
||||
{
|
||||
// Arrange
|
||||
var (client, handler) = BuildBytes(FakeXmlBytes());
|
||||
|
||||
// Act
|
||||
using var result = await client.ExtractZugferdAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/zugferd/extract?format=file");
|
||||
handler.LastRequest.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
}
|
||||
|
||||
// ?? ExtractZugferdAsResultAsync (Stream) ?????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractZugferdAsResultAsync_Stream_ReturnsStructuredResult()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new ZugferdExtractionResult
|
||||
{
|
||||
FileName = "factur-x.xml",
|
||||
XmlContent = "<invoice/>",
|
||||
Version = "2.1",
|
||||
Profile = "EN 16931"
|
||||
};
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
var result = await client.ExtractZugferdAsResultAsync(new MemoryStream(FakePdfBytes()));
|
||||
|
||||
// Assert
|
||||
result.FileName.Should().Be("factur-x.xml");
|
||||
result.XmlContent.Should().Be("<invoice/>");
|
||||
result.Version.Should().Be("2.1");
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/zugferd/extract?asFile=false");
|
||||
}
|
||||
|
||||
// ?? ExtractZugferdAsResultAsync (byte[]) ?????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task ExtractZugferdAsResultAsync_Bytes_SendsJsonWithFormatJson()
|
||||
{
|
||||
// Arrange
|
||||
var expected = new ZugferdExtractionResult { FileName = "zugferd.xml", XmlContent = "<x/>" };
|
||||
var (client, handler) = BuildJson(expected);
|
||||
|
||||
// Act
|
||||
await client.ExtractZugferdAsResultAsync(FakePdfBytes());
|
||||
|
||||
// Assert
|
||||
handler.LastRequest!.RequestUri!.PathAndQuery.Should().Be("/api/pdf/zugferd/extract?format=json");
|
||||
handler.LastRequest.Content.Should().NotBeNull();
|
||||
handler.LastRequest.Content!.Headers.ContentType!.MediaType.Should().Be("application/json");
|
||||
}
|
||||
|
||||
// ?? HTTP error propagation ???????????????????????????????????????????????
|
||||
|
||||
[Fact]
|
||||
public async Task HasZugferdAsync_WhenApiReturns400_ThrowsHttpRequestException()
|
||||
{
|
||||
// Arrange
|
||||
var handler = MockHttpMessageHandler.ReturningStatus(HttpStatusCode.BadRequest);
|
||||
var httpClient = new HttpClient(handler) { BaseAddress = new Uri("http://localhost") };
|
||||
var client = new ZugferdClient(httpClient, NullLogger<ZugferdClient>.Instance);
|
||||
|
||||
// Act & Assert
|
||||
await client.Invoking(c => c.HasZugferdAsync(new MemoryStream(FakePdfBytes())))
|
||||
.Should().ThrowAsync<HttpRequestException>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user