Refactor: Rename DocumentOperator to DocumentService
Updated project and solution files to rename and restructure `DocumentOperator` to `DocumentService`. Modified project references in `DocumentService.API.csproj`, `DocumentService.Application.csproj`, and `DocumentService.Infrastructure.csproj` to reflect the new naming convention. Updated `DocumentService.sln` to remove old project references, add new ones, and adjust configuration and nested project mappings accordingly. This change aligns the project structure with the new naming convention.
This commit is contained in:
221
DocumentService.Tests/Unit/Client/PdfAttachmentClientTests.cs
Normal file
221
DocumentService.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>();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user