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.
238 lines
8.6 KiB
C#
238 lines
8.6 KiB
C#
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();
|
||
}
|
||
}
|