mirror of
https://github.com/Sea-Haven-Industries/shoc-backend.git
synced 2026-10-04 05:33:19 +00:00
feat(work-orders): cap media uploads at 50MB
This commit is contained in:
parent
47022c7761
commit
b6b8d2e58f
2 changed files with 86 additions and 2 deletions
|
|
@ -0,0 +1,75 @@
|
||||||
|
using Api.SeaHavenIndustries.Controllers;
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Moq;
|
||||||
|
using SeaHaven.Services.DTOs;
|
||||||
|
using SeaHaven.Services.Interfaces;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Api.SeaHavenIndustries.Tests;
|
||||||
|
|
||||||
|
public class WorkOrderMediaControllerTests
|
||||||
|
{
|
||||||
|
private static WorkOrderMediaController CreateController(
|
||||||
|
Mock<IWorkOrderMediaService>? service = null,
|
||||||
|
Mock<IFileStoragePort>? storage = null)
|
||||||
|
{
|
||||||
|
var controller = new WorkOrderMediaController(
|
||||||
|
(service ?? new Mock<IWorkOrderMediaService>()).Object,
|
||||||
|
(storage ?? new Mock<IFileStoragePort>()).Object);
|
||||||
|
controller.ControllerContext = new ControllerContext
|
||||||
|
{
|
||||||
|
HttpContext = new DefaultHttpContext()
|
||||||
|
};
|
||||||
|
return controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddMedia_HasFiftyMegabyteRequestLimit()
|
||||||
|
{
|
||||||
|
var method = typeof(WorkOrderMediaController).GetMethod(nameof(WorkOrderMediaController.AddMedia));
|
||||||
|
var attribute = Assert.Single(
|
||||||
|
method!.CustomAttributes,
|
||||||
|
candidate => candidate.AttributeType == typeof(RequestSizeLimitAttribute));
|
||||||
|
var bytes = Assert.Single(attribute.ConstructorArguments);
|
||||||
|
|
||||||
|
Assert.Equal(50_000_000L, bytes.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMedia_FileOverLimit_ReturnsStableUnprocessableEntity()
|
||||||
|
{
|
||||||
|
var file = new Mock<IFormFile>();
|
||||||
|
file.SetupGet(candidate => candidate.Length).Returns(50_000_001);
|
||||||
|
var storage = new Mock<IFileStoragePort>(MockBehavior.Strict);
|
||||||
|
var controller = CreateController(storage: storage);
|
||||||
|
|
||||||
|
var result = await controller.AddMedia(1, null, file.Object, CancellationToken.None);
|
||||||
|
|
||||||
|
var response = Assert.IsType<UnprocessableEntityObjectResult>(result);
|
||||||
|
var error = Assert.IsType<WorkOrderBoardValidationErrorDto>(response.Value);
|
||||||
|
Assert.Equal("FileTooLarge", error.Code);
|
||||||
|
Assert.Equal("The uploaded file must not exceed 50 MB.", error.Message);
|
||||||
|
storage.VerifyNoOtherCalls();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task AddMedia_UnsupportedType_ReturnsUnprocessableEntity()
|
||||||
|
{
|
||||||
|
var bytes = new byte[] { 1, 2, 3 };
|
||||||
|
var file = new FormFile(new MemoryStream(bytes), 0, bytes.Length, "file", "payload.exe")
|
||||||
|
{
|
||||||
|
Headers = new HeaderDictionary(),
|
||||||
|
ContentType = "application/octet-stream"
|
||||||
|
};
|
||||||
|
var storage = new Mock<IFileStoragePort>(MockBehavior.Strict);
|
||||||
|
var controller = CreateController(storage: storage);
|
||||||
|
|
||||||
|
var result = await controller.AddMedia(1, null, file, CancellationToken.None);
|
||||||
|
|
||||||
|
var response = Assert.IsType<UnprocessableEntityObjectResult>(result);
|
||||||
|
var error = Assert.IsType<WorkOrderBoardValidationErrorDto>(response.Value);
|
||||||
|
Assert.Equal("UnsupportedMediaType", error.Code);
|
||||||
|
storage.VerifyNoOtherCalls();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -17,6 +17,8 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
[Route("api/workorders")]
|
[Route("api/workorders")]
|
||||||
public class WorkOrderMediaController : Controller
|
public class WorkOrderMediaController : Controller
|
||||||
{
|
{
|
||||||
|
public const long MaxUploadBytes = 50_000_000;
|
||||||
|
|
||||||
private readonly IWorkOrderMediaService _workOrderMediaService;
|
private readonly IWorkOrderMediaService _workOrderMediaService;
|
||||||
private readonly IFileStoragePort _fileStorage;
|
private readonly IFileStoragePort _fileStorage;
|
||||||
|
|
||||||
|
|
@ -51,7 +53,7 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("{id:int}/media")]
|
[HttpPost("{id:int}/media")]
|
||||||
[RequestSizeLimit(30_000_000)]
|
[RequestSizeLimit(MaxUploadBytes)]
|
||||||
public async Task<IActionResult> AddMedia(
|
public async Task<IActionResult> AddMedia(
|
||||||
int id,
|
int id,
|
||||||
[FromForm] WorkOrderMediaCategory? category,
|
[FromForm] WorkOrderMediaCategory? category,
|
||||||
|
|
@ -64,7 +66,14 @@ namespace Api.SeaHavenIndustries.Controllers
|
||||||
string? fileUrl = null;
|
string? fileUrl = null;
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
WorkOrderMediaFileRules.EnsureAllowed(file);
|
if (file.Length > MaxUploadBytes)
|
||||||
|
{
|
||||||
|
throw new WorkOrderBoardValidationException(
|
||||||
|
"FileTooLarge",
|
||||||
|
"The uploaded file must not exceed 50 MB.");
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkOrderMediaFileRules.EnsureAllowed(file, category);
|
||||||
var actorId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
var actorId = User.FindFirstValue(ClaimTypes.NameIdentifier);
|
||||||
await _workOrderMediaService.EnsureCanMutateMediaAsync(id, User, actorId, cancellationToken, category);
|
await _workOrderMediaService.EnsureCanMutateMediaAsync(id, User, actorId, cancellationToken, category);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue