Files
DocumentService/DocumentService.Tests/Integration/API/PdfOperationsControllerTests.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

501 lines
17 KiB
C#

using System.Net;
using System.Net.Http.Headers;
using System.Reflection;
using System.Text;
using System.Text.Json;
using DocumentService.Application.Common.DTOs.Requests;
using DocumentService.Domain.Models.ValueObjects;
using FluentAssertions;
using Microsoft.AspNetCore.Mvc.Testing;
namespace DocumentService.Tests.Integration.API;
public class PdfOperationsControllerTests : IClassFixture<WebApplicationFactory<Program>>
{
private readonly HttpClient _client;
public PdfOperationsControllerTests(WebApplicationFactory<Program> factory)
{
_client = factory.CreateClient();
}
private static Stream LoadTestPdfAsStream(string fileName)
{
var assembly = Assembly.GetExecutingAssembly();
var resourceName = $"DocumentService.Tests.TestData.Pdfs.{fileName}";
return assembly.GetManifestResourceStream(resourceName)
?? throw new FileNotFoundException($"Embedded resource not found: {resourceName}");
}
private static string LoadTestPdfAsBase64(string fileName)
{
using var stream = LoadTestPdfAsStream(fileName);
using var ms = new MemoryStream();
stream.CopyTo(ms);
return Convert.ToBase64String(ms.ToArray());
}
#region Merge Endpoint Tests (existing - keeping for reference)
[Fact]
public async Task MergeFromFiles_ValidPdfs_ReturnsMergedPdf()
{
// Arrange
using var content = new MultipartFormDataContent();
using var pdf1Stream = LoadTestPdfAsStream("valid.pdf");
using var pdf2Stream = LoadTestPdfAsStream("valid.pdf");
var pdf1Content = new StreamContent(pdf1Stream);
var pdf2Content = new StreamContent(pdf2Stream);
pdf1Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
pdf2Content.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(pdf1Content, "files", "file1.pdf");
content.Add(pdf2Content, "files", "file2.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("application/pdf");
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
mergedPdf.Should().NotBeEmpty();
mergedPdf.Length.Should().BeGreaterThan(100); // Sanity check
}
[Fact]
public async Task MergeFromBase64_ValidPdfs_ReturnsMergedPdf()
{
// Arrange
string base64Pdf1 = LoadTestPdfAsBase64("valid.pdf");
string base64Pdf2 = LoadTestPdfAsBase64("valid.pdf");
var request = new MergePdfsBase64Request
{
Base64Pdfs = new List<string> { base64Pdf1, base64Pdf2 },
PageRanges = null
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
mergedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task MergeFromBase64_WithPageRanges_ReturnsMergedPdf()
{
// Arrange
string base64Pdf1 = LoadTestPdfAsBase64("valid.pdf");
string base64Pdf2 = LoadTestPdfAsBase64("valid.pdf");
var request = new MergePdfsBase64Request
{
Base64Pdfs = new List<string> { base64Pdf1, base64Pdf2 },
PageRanges = new List<string?> { "1", "1" } // Only first page from each
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] mergedPdf = await response.Content.ReadAsByteArrayAsync();
mergedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task MergeFromFiles_OnePdf_Returns400()
{
// Arrange
using var content = new MultipartFormDataContent();
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
var pdfContent = new StreamContent(pdfStream);
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(pdfContent, "files", "file1.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task MergeFromBase64_InvalidBase64_Returns400()
{
// Arrange
var request = new MergePdfsBase64Request
{
Base64Pdfs = new List<string> { "INVALID_BASE64!!!", "ANOTHER_INVALID" }
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task MergeFromBase64_InvalidPageRange_Returns400()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new MergePdfsBase64Request
{
Base64Pdfs = new List<string> { base64Pdf, base64Pdf },
PageRanges = new List<string?> { "999-1000", null } // Exceeds page count
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task MergeFromFiles_CorruptedPdf_Returns500()
{
// Arrange
using var content = new MultipartFormDataContent();
byte[] corruptedData = "NOT A PDF FILE"u8.ToArray();
var corruptedContent = new ByteArrayContent(corruptedData);
corruptedContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
using var validPdfStream = LoadTestPdfAsStream("valid.pdf");
var validContent = new StreamContent(validPdfStream);
validContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(corruptedContent, "files", "corrupted.pdf");
content.Add(validContent, "files", "valid.pdf");
// Act
var response = await _client.PostAsync("/api/pdf/operations/merge", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.InternalServerError);
}
#endregion
#region Annotate Endpoint Tests
[Fact]
public async Task AnnotateFromFile_TextMarkupHighlight_ReturnsAnnotatedPdf()
{
// Arrange
using var content = new MultipartFormDataContent();
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
var pdfContent = new StreamContent(pdfStream);
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(pdfContent, "file", "test.pdf");
content.Add(new StringContent(AnnotationType.TextMarkup.ToString()), "annotationType");
content.Add(new StringContent("1"), "pageNumber");
content.Add(new StringContent("100"), "x1");
content.Add(new StringContent("100"), "y1");
content.Add(new StringContent("200"), "x2");
content.Add(new StringContent("120"), "y2");
content.Add(new StringContent("Important text"), "content");
content.Add(new StringContent("Test Author"), "author");
content.Add(new StringContent("FFFF00"), "color");
content.Add(new StringContent(TextMarkupStyle.Highlight.ToString()), "textMarkupStyle");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
response.Content.Headers.ContentType?.MediaType.Should().Be("application/pdf");
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
annotatedPdf.Should().NotBeEmpty();
annotatedPdf.Length.Should().BeGreaterThan(100);
}
[Fact]
public async Task AnnotateFromBase64_FreeText_ReturnsAnnotatedPdf()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.FreeText,
PageNumber = 1,
X1 = 50,
Y1 = 50,
X2 = 150,
Y2 = 100,
Content = "Free text annotation",
Author = "John Doe",
Color = "FF0000"
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
annotatedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task AnnotateFromBase64_StickyNote_ReturnsAnnotatedPdf()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.StickyNote,
PageNumber = 1,
X1 = 300,
Y1 = 300,
X2 = 320,
Y2 = 320,
Content = "Please review this section",
Author = "Reviewer"
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
annotatedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task AnnotateFromFile_Circle_ReturnsAnnotatedPdf()
{
// Arrange
using var content = new MultipartFormDataContent();
using var pdfStream = LoadTestPdfAsStream("valid.pdf");
var pdfContent = new StreamContent(pdfStream);
pdfContent.Headers.ContentType = new MediaTypeHeaderValue("application/pdf");
content.Add(pdfContent, "file", "test.pdf");
content.Add(new StringContent(AnnotationType.Circle.ToString()), "annotationType");
content.Add(new StringContent("1"), "pageNumber");
content.Add(new StringContent("100"), "x1");
content.Add(new StringContent("200"), "y1");
content.Add(new StringContent("200"), "x2");
content.Add(new StringContent("300"), "y2");
content.Add(new StringContent("Circle annotation"), "content");
content.Add(new StringContent("00FF00"), "color");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
annotatedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task AnnotateFromBase64_Square_ReturnsAnnotatedPdf()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.Square,
PageNumber = 1,
X1 = 250,
Y1 = 250,
X2 = 350,
Y2 = 350,
Color = "0000FF"
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.OK);
byte[] annotatedPdf = await response.Content.ReadAsByteArrayAsync();
annotatedPdf.Should().NotBeEmpty();
}
[Fact]
public async Task AnnotateFromBase64_InvalidBase64_Returns400()
{
// Arrange
var request = new AddAnnotationBase64Request
{
Base64Pdf = "INVALID_BASE64!!!",
AnnotationType = AnnotationType.Circle,
PageNumber = 1,
X1 = 0,
Y1 = 0,
X2 = 100,
Y2 = 100
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task AnnotateFromBase64_InvalidPageNumber_Returns400()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.FreeText,
PageNumber = 999, // Exceeds page count
X1 = 0,
Y1 = 0,
X2 = 100,
Y2 = 100,
Content = "Test"
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task AnnotateFromBase64_FreeTextWithoutContent_Returns400()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.FreeText,
PageNumber = 1,
X1 = 0,
Y1 = 0,
X2 = 100,
Y2 = 100
// Missing required Content
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task AnnotateFromBase64_TextMarkupWithoutStyle_Returns400()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.TextMarkup,
PageNumber = 1,
X1 = 0,
Y1 = 0,
X2 = 100,
Y2 = 100
// Missing required TextMarkupStyle
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
[Fact]
public async Task AnnotateFromBase64_InvalidColorFormat_Returns400()
{
// Arrange
string base64Pdf = LoadTestPdfAsBase64("valid.pdf");
var request = new AddAnnotationBase64Request
{
Base64Pdf = base64Pdf,
AnnotationType = AnnotationType.Circle,
PageNumber = 1,
X1 = 0,
Y1 = 0,
X2 = 100,
Y2 = 100,
Color = "INVALID" // Invalid hex format
};
var json = JsonSerializer.Serialize(request);
var content = new StringContent(json, Encoding.UTF8, "application/json");
// Act
var response = await _client.PostAsync("/api/pdf/operations/annotate", content);
// Assert
response.StatusCode.Should().Be(HttpStatusCode.BadRequest);
}
#endregion
}