Files
baiyue-firstleg/file/src/test/java/com/baiyue/file/service/InternalBusinessFileServiceTest.java
T

210 lines
13 KiB
Java
Raw Normal View History

package com.baiyue.file.service;
import com.baiyue.common.core.exception.BizException;
import com.baiyue.file.entity.FileObjectEntity;
import com.baiyue.file.mapper.FileObjectMapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.*;
import org.springframework.mock.web.MockMultipartFile;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class InternalBusinessFileServiceTest {
@Mock FileObjectMapper files;
@Mock BusinessFileStorage storage;
@InjectMocks InternalBusinessFileService service;
@BeforeEach void setUp() { MockitoAnnotations.openMocks(this); }
@Test void customsDocumentUploadPersistsExactBusinessOwnership() {
MockMultipartFile file = new MockMultipartFile("file", "invoice.txt", "text/plain", "invoice".getBytes());
when(storage.store(10001L, 41L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false)).thenReturn(new BusinessFileStorage.Stored(
"business-files/10001/41/key.txt", "text/plain", 7, "a".repeat(64), "invoice.txt"));
when(files.insert(any(FileObjectEntity.class))).thenAnswer(invocation -> {
((FileObjectEntity) invocation.getArgument(0)).setId(71L); return 1;
});
var result = service.upload(10001L, "CUSTOMS_DOCUMENT", "41", 7L, file);
assertThat(result.fileId()).isEqualTo(71L);
assertThat(result.businessType()).isEqualTo("CUSTOMS_DOCUMENT");
assertThat(result.businessId()).isEqualTo("41");
assertThat(result.contentType()).isEqualTo("text/plain");
}
2026-08-13 09:49:55 +08:00
@Test void platformContainerAndLoadingDocumentsSupportZeroTenantScope() {
MockMultipartFile file = new MockMultipartFile("file", "customs.pdf", "application/pdf", "%PDF-1.4".getBytes());
when(storage.store(eq(0L), anyLong(), same(file), eq(BusinessFileCategory.STANDARD_MAX_SIZE), eq(false))).thenAnswer(invocation ->
2026-08-13 09:49:55 +08:00
new BusinessFileStorage.Stored("business-files/0/" + invocation.getArgument(1) + "/customs.pdf",
"application/pdf", file.getSize(), "b".repeat(64), "customs.pdf"));
when(storage.store(11L, 51L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false)).thenReturn(new BusinessFileStorage.Stored(
2026-08-20 15:25:40 +08:00
"business-files/11/51/unload.jpg", "image/jpeg", file.getSize(), "c".repeat(64), "unload.jpg"));
2026-08-13 09:49:55 +08:00
when(files.insert(any(FileObjectEntity.class))).thenAnswer(invocation -> {
((FileObjectEntity) invocation.getArgument(0)).setId(81L); return 1;
});
var clearance = service.upload(0L, "CONTAINER_CLEARANCE_DOCUMENT", "41", 7L, file);
2026-08-26 13:58:33 +08:00
var customs = service.upload(0L, "LOADING_CUSTOMS_DECLARATION", "51", 7L, file);
2026-08-13 11:57:33 +08:00
var deliveryOrder = service.upload(0L, "CONTAINER_DELIVERY_ORDER", "41", 7L, file);
var pickupOrder = service.upload(0L, "CONTAINER_PICKUP_ORDER", "41", 7L, file);
2026-08-20 15:25:40 +08:00
var unloadPhoto = service.upload(11L, "OVERSEAS_UNLOAD_PHOTO", "51", 7L, file);
2026-08-13 09:49:55 +08:00
assertThat(clearance).extracting(InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
InternalBusinessFileService.Metadata::businessId).containsExactly(0L, "CONTAINER_CLEARANCE_DOCUMENT", "41");
assertThat(customs).extracting(InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
2026-08-26 13:58:33 +08:00
InternalBusinessFileService.Metadata::businessId).containsExactly(0L, "LOADING_CUSTOMS_DECLARATION", "51");
2026-08-13 11:57:33 +08:00
assertThat(deliveryOrder.businessType()).isEqualTo("CONTAINER_DELIVERY_ORDER");
assertThat(pickupOrder.businessType()).isEqualTo("CONTAINER_PICKUP_ORDER");
2026-08-20 15:25:40 +08:00
assertThat(unloadPhoto).extracting(InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
InternalBusinessFileService.Metadata::businessId)
.containsExactly(11L, "OVERSEAS_UNLOAD_PHOTO", "51");
verify(storage, times(3)).store(0L, 41L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false);
verify(storage).store(0L, 51L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false);
verify(storage).store(11L, 51L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false);
2026-08-13 09:49:55 +08:00
}
@Test void oceanBookingDocumentUsesCustomerAndBookingOwnership() {
MockMultipartFile file = new MockMultipartFile("file", "booking.pdf", "application/pdf",
"%PDF-1.4".getBytes());
when(storage.store(10001L, 61L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false))
.thenReturn(new BusinessFileStorage.Stored("business-files/10001/61/booking.pdf",
"application/pdf", file.getSize(), "f".repeat(64), "booking.pdf"));
when(files.insert(any(FileObjectEntity.class))).thenAnswer(invocation -> {
((FileObjectEntity) invocation.getArgument(0)).setId(111L); return 1;
});
var metadata = service.upload(10001L, "OCEAN_BOOKING_DOCUMENT", "61", 7L, file);
assertThat(metadata).extracting(InternalBusinessFileService.Metadata::ownerScope,
InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
InternalBusinessFileService.Metadata::businessId)
.containsExactly("CUSTOMER", 10001L, "OCEAN_BOOKING_DOCUMENT", "61");
assertThatThrownBy(() -> service.upload(0L, "OCEAN_BOOKING_DOCUMENT", "61", 7L, file))
.isInstanceOf(BizException.class).hasMessageContaining("明确客户");
}
@Test void oceanBookingDocumentRejectsWrongCustomerBookingAndTruckOwnership() {
FileObjectEntity value = new FileObjectEntity(); value.setId(111L); value.setTenantId(10001L);
value.setBusinessType("OCEAN_BOOKING_DOCUMENT"); value.setBusinessId("61");
value.setOriginalName("booking.pdf"); value.setObjectKey("business-files/10001/61/booking.pdf");
value.setContentType("application/pdf"); value.setSha256("f".repeat(64)); value.setSizeBytes(8L);
when(files.selectOne(any())).thenReturn(value, null, null, null);
assertThat(service.metadata(10001L, "OCEAN_BOOKING_DOCUMENT", "61", 111L).ownerScope())
.isEqualTo("CUSTOMER");
assertThatThrownBy(() -> service.metadata(10002L, "OCEAN_BOOKING_DOCUMENT", "61", 111L))
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
assertThatThrownBy(() -> service.metadata(10001L, "OCEAN_BOOKING_DOCUMENT", "62", 111L))
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
assertThatThrownBy(() -> service.metadata(10001L, "TRUCK_APPOINTMENT_ATTACHMENT", "61", 111L))
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
}
2026-08-13 09:49:55 +08:00
@Test void rejectsNegativeTenantAndInvalidOwnershipInputs() {
MockMultipartFile file = new MockMultipartFile("file", "a.txt", "text/plain", "x".getBytes());
assertThatThrownBy(() -> service.upload(-1L, "CONTAINER_CLEARANCE_DOCUMENT", "41", 7L, file))
.isInstanceOf(BizException.class).hasMessageContaining("归属");
assertThatThrownBy(() -> service.upload(0L, "CONTAINER_CLEARANCE_DOCUMENT", "0", 7L, file))
.isInstanceOf(BizException.class).hasMessageContaining("业务ID");
assertThatThrownBy(() -> service.upload(0L, "CONTAINER_CLEARANCE_DOCUMENT", "41", 0L, file))
.isInstanceOf(BizException.class).hasMessageContaining("操作人");
verifyNoInteractions(files, storage);
}
@Test void overseasRelabelPhotoPersistsExactTaskOwnership() {
MockMultipartFile file = new MockMultipartFile("file", "relabel.jpg", "image/jpeg", "photo".getBytes());
when(storage.store(100L, 81L, file, BusinessFileCategory.STANDARD_MAX_SIZE, false)).thenReturn(new BusinessFileStorage.Stored(
"business-files/100/81/relabel.jpg", "image/jpeg", file.getSize(), "d".repeat(64), "relabel.jpg"));
when(files.insert(any(FileObjectEntity.class))).thenAnswer(invocation -> {
((FileObjectEntity) invocation.getArgument(0)).setId(91L); return 1;
});
var result = service.upload(100L, "OVERSEAS_RELABEL_PHOTO", "81", 7L, file);
assertThat(result).extracting(InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
InternalBusinessFileService.Metadata::businessId)
.containsExactly(100L, "OVERSEAS_RELABEL_PHOTO", "81");
}
@Test void loadingCustomsSourceAndResultAreCustomerOwnedArbitraryFormatObjects() {
MockMultipartFile file = new MockMultipartFile("file", "资料包.zip", "application/zip", "zip".getBytes());
when(storage.store(10001L, 51L, file, BusinessFileCategory.CUSTOMS_MAX_SIZE, true))
.thenReturn(new BusinessFileStorage.Stored("business-files/10001/51/key", "application/zip",
file.getSize(), "e".repeat(64), "资料包.zip"));
when(files.insert(any(FileObjectEntity.class))).thenAnswer(invocation -> {
((FileObjectEntity) invocation.getArgument(0)).setId(101L); return 1;
});
var source = service.upload(10001L, "LOADING_LIST_CUSTOMS_SOURCE", "51", 7L, file);
var result = service.upload(10001L, "LOADING_LIST_CUSTOMS_RESULT", "51", 7L, file);
assertThat(source).extracting(InternalBusinessFileService.Metadata::tenantId,
InternalBusinessFileService.Metadata::businessType,
InternalBusinessFileService.Metadata::businessId)
.containsExactly(10001L, "LOADING_LIST_CUSTOMS_SOURCE", "51");
assertThat(result.businessType()).isEqualTo("LOADING_LIST_CUSTOMS_RESULT");
verify(storage, times(2)).store(10001L, 51L, file, BusinessFileCategory.CUSTOMS_MAX_SIZE, true);
}
@Test void loadingCustomsCategoriesRejectPlatformOwnerScope() {
var file = new MockMultipartFile("file", "source.bin", "application/octet-stream", new byte[]{1});
assertThatThrownBy(() -> service.upload(0L, "LOADING_LIST_CUSTOMS_SOURCE", "51", 7L, file))
.isInstanceOf(BizException.class).hasMessageContaining("明确客户");
assertThatThrownBy(() -> service.metadata(0L, "LOADING_LIST_CUSTOMS_RESULT", "51", 91L))
.isInstanceOf(BizException.class).hasMessageContaining("明确客户");
verifyNoInteractions(files, storage);
}
@Test void unsupportedBusinessTypeFailsClosed() {
assertThatThrownBy(() -> service.upload(10001L, "ARBITRARY", "41", 7L,
new MockMultipartFile("file", "a.txt", "text/plain", "x".getBytes())))
.isInstanceOf(BizException.class).hasMessageContaining("不支持");
verifyNoInteractions(files, storage);
}
@Test void downloadUsesValidatedMetadataObjectKey() {
FileObjectEntity value = new FileObjectEntity(); value.setId(71L); value.setTenantId(10001L);
value.setBusinessType("CUSTOMS_RECEIPT"); value.setBusinessId("61"); value.setOriginalName("release.txt");
value.setObjectKey("business-files/10001/61/release.txt"); value.setContentType("text/plain");
when(files.selectOne(any())).thenReturn(value);
when(storage.read(value.getObjectKey())).thenReturn("released".getBytes());
var result = service.download(10001L, "CUSTOMS_RECEIPT", "61", 71L);
assertThat(result.fileName()).isEqualTo("release.txt");
assertThat(result.content()).isEqualTo("released".getBytes());
verify(storage).read(value.getObjectKey());
}
2026-08-13 09:49:55 +08:00
@Test void platformMetadataAndDownloadRequireExactCompositeOwnership() {
FileObjectEntity value = new FileObjectEntity(); value.setId(91L); value.setTenantId(0L);
2026-08-26 13:58:33 +08:00
value.setBusinessType("LOADING_CUSTOMS_DECLARATION"); value.setBusinessId("51");
2026-08-13 09:49:55 +08:00
value.setOriginalName("declaration.pdf"); value.setObjectKey("business-files/0/51/declaration.pdf");
value.setContentType("application/pdf"); value.setSha256("c".repeat(64)); value.setSizeBytes(8L);
when(files.selectOne(any())).thenReturn(value, value, null, null, null);
when(storage.read(value.getObjectKey())).thenReturn("document".getBytes());
2026-08-26 13:58:33 +08:00
assertThat(service.metadata(0L, "LOADING_CUSTOMS_DECLARATION", "51", 91L).fileName())
2026-08-13 09:49:55 +08:00
.isEqualTo("declaration.pdf");
2026-08-26 13:58:33 +08:00
assertThat(service.download(0L, "LOADING_CUSTOMS_DECLARATION", "51", 91L).content())
2026-08-13 09:49:55 +08:00
.isEqualTo("document".getBytes());
assertThatThrownBy(() -> service.metadata(0L, "CONTAINER_CLEARANCE_DOCUMENT", "51", 91L))
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
2026-08-26 13:58:33 +08:00
assertThatThrownBy(() -> service.metadata(0L, "LOADING_CUSTOMS_DECLARATION", "52", 91L))
2026-08-13 09:49:55 +08:00
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
2026-08-26 13:58:33 +08:00
assertThatThrownBy(() -> service.metadata(0L, "LOADING_CUSTOMS_DECLARATION", "51", 92L))
2026-08-13 09:49:55 +08:00
.isInstanceOf(BizException.class).hasMessageContaining("归属不匹配");
verify(storage).read(value.getObjectKey());
}
2026-08-26 13:58:33 +08:00
}