Files
DocumentService/DocumentService.Tests/Unit/Client/PdfOperationsClientTests.cs
TekH 2c98558131 Refactor DTOs and namespaces for maintainability
Consolidated DTOs under `DocumentService.Application.Common.DTOs.Requests`
to centralize and align them with the application layer. Introduced
`PdfBase64RequestBase` to encapsulate shared properties, reducing
redundancy across request DTOs. Updated controllers, clients, and tests
to use the new DTO structure. Removed redundant DTOs and cleaned up
unused namespaces and comments. Improved documentation and ensured
consistent naming conventions across the codebase.
2026-08-31 01:19:52 +02:00

238 lines
8.6 KiB
C#
Raw Permalink Blame History

using DocumentService.Client.Clients;
using DocumentService.Client.Interfaces;
using DocumentService.Application.Common.DTOs.Requests;
using DocumentService.Client.Models.ValueObjects;
using DocumentService.Domain.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"/> <20> 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();
}
}