优化
This commit is contained in:
+23
@@ -0,0 +1,23 @@
|
||||
package com.baiyue.file.controller;
|
||||
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.file.dto.ExportTaskDtos.CreateExportTaskRequest;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
|
||||
class PlatformExportTaskControllerContractTest {
|
||||
@Test
|
||||
void exposesThePlatformFileRouteAndProtectsListingAndDownloads() throws Exception {
|
||||
RequestMapping mapping = PlatformExportTaskController.class.getAnnotation(RequestMapping.class);
|
||||
assertEquals("/api/platform/v1/files/export-tasks", mapping.value()[0]);
|
||||
assertEquals("isAuthenticated()", PlatformExportTaskController.class
|
||||
.getDeclaredMethod("create", AuthenticatedUser.class, CreateExportTaskRequest.class)
|
||||
.getAnnotation(PreAuthorize.class).value());
|
||||
assertEquals("hasAuthority('platform:download:list')", PlatformExportTaskController.class
|
||||
.getDeclaredMethod("download", AuthenticatedUser.class, long.class)
|
||||
.getAnnotation(PreAuthorize.class).value());
|
||||
}
|
||||
}
|
||||
@@ -159,12 +159,33 @@ class QualificationFileContractTest {
|
||||
when(iamClient.submit(eq("internal-token"), any())).thenThrow(new RuntimeException("timeout"));
|
||||
|
||||
var exception = assertThrows(BizException.class,
|
||||
() -> controller.upload(merchantUser(), file, null));
|
||||
() -> controller.upload(merchantUser(), file, "BUSINESS_LICENSE", null, null, false));
|
||||
|
||||
assertEquals(ResultCode.SERVICE_UNAVAILABLE, exception.getResultCode());
|
||||
verify(storage, never()).delete(stored);
|
||||
}
|
||||
|
||||
@Test
|
||||
void merchantUploadForwardsItsQualificationTypeAndCleansTheReplacedFile() {
|
||||
var storage = mock(LocalQualificationStorage.class);
|
||||
var iamClient = mock(IamQualificationClient.class);
|
||||
var controller = merchantController(storage, iamClient);
|
||||
var file = new MockMultipartFile("file", "legal-person.png", "image/png", new byte[] {1, 2, 3});
|
||||
var stored = storedFile();
|
||||
var oldUrl = "/api/merchant/v1/files/qualifications/22222222-2222-2222-2222-222222222222.png";
|
||||
when(storage.store(10001L, file)).thenReturn(stored);
|
||||
when(iamClient.submit(eq("internal-token"), any())).thenReturn(ApiResponse.success(
|
||||
new IamQualificationClient.QualificationResponse(31L, null, "PROFILE_REQUIRED", oldUrl)));
|
||||
|
||||
controller.upload(merchantUser(), file, "LEGAL_PERSON_ID", null, LocalDate.of(2030, 1, 1), true);
|
||||
|
||||
var requestCaptor = org.mockito.ArgumentCaptor.forClass(IamQualificationClient.QualificationRequest.class);
|
||||
verify(iamClient).submit(eq("internal-token"), requestCaptor.capture());
|
||||
verify(storage).deleteByDocumentUrl(10001L, oldUrl);
|
||||
assertEquals("LEGAL_PERSON_ID", requestCaptor.getValue().qualificationType());
|
||||
assertTrue(requestCaptor.getValue().replace());
|
||||
}
|
||||
|
||||
private static PlatformQualificationFileController platformController(
|
||||
LocalQualificationStorage storage, IamQualificationClient iamClient) {
|
||||
var controller = new PlatformQualificationFileController();
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
package com.baiyue.file.service;
|
||||
|
||||
import com.baiyue.common.core.api.ApiResponse;
|
||||
import com.baiyue.file.client.IamExportClient;
|
||||
import com.baiyue.file.entity.FileExportTask;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class CustomerExportTaskHandlerTest {
|
||||
@TempDir Path tempDir;
|
||||
|
||||
@Test
|
||||
void generatesARealXlsxFromIamBatchesAndReportsProgress() throws Exception {
|
||||
IamExportClient client = mock(IamExportClient.class);
|
||||
LocalExportStorage storage = new LocalExportStorage(tempDir.toString());
|
||||
storage.initialize();
|
||||
var row = new IamExportClient.CustomerExportRow(
|
||||
"101", "TEN101", "柏悦物流", "柏悦物流有限公司", "91440300MA5ABCDE12",
|
||||
"有限责任公司", null, null, "CNY", "深圳市", "张三", "13800138000",
|
||||
"ops@example.com", "baiyue", "李四", "基础版", 30, "ACTIVE", null, null);
|
||||
when(client.customers("internal-token", null, 500, "柏悦", "ACTIVE"))
|
||||
.thenReturn(ApiResponse.success(new IamExportClient.CustomerExportBatch(List.of(row), 101L, false)));
|
||||
CustomerExportTaskHandler handler = new CustomerExportTaskHandler(
|
||||
client, storage, new ObjectMapper().findAndRegisterModules(), "internal-token");
|
||||
FileExportTask task = new FileExportTask();
|
||||
task.setId(9L);
|
||||
task.setParametersJson("{\"keyword\":\"柏悦\",\"status\":\"ACTIVE\"}");
|
||||
AtomicLong progress = new AtomicLong();
|
||||
|
||||
var result = handler.export(task, progress::set);
|
||||
|
||||
assertEquals(1L, result.rows());
|
||||
assertEquals(1L, progress.get());
|
||||
assertTrue(result.fileName().endsWith(".xlsx"));
|
||||
Path workbookPath = tempDir.resolve(result.objectKey());
|
||||
assertTrue(Files.isRegularFile(workbookPath));
|
||||
try (XSSFWorkbook workbook = new XSSFWorkbook(Files.newInputStream(workbookPath))) {
|
||||
assertEquals("客户ID", workbook.getSheetAt(0).getRow(0).getCell(0).getStringCellValue());
|
||||
assertEquals("柏悦物流", workbook.getSheetAt(0).getRow(1).getCell(2).getStringCellValue());
|
||||
assertEquals("13800138000", workbook.getSheetAt(0).getRow(1).getCell(11).getStringCellValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.baiyue.file.service;
|
||||
|
||||
import com.baiyue.common.core.api.ResultCode;
|
||||
import com.baiyue.common.core.exception.BizException;
|
||||
import com.baiyue.common.security.AuthenticatedUser;
|
||||
import com.baiyue.file.dto.ExportTaskDtos.CreateExportTaskRequest;
|
||||
import com.baiyue.file.entity.FileExportTask;
|
||||
import com.baiyue.file.repository.ExportTaskRepository;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ExportTaskServiceTest {
|
||||
@Test
|
||||
void createsPersistedTaskOnlyWhenSourceExportPermissionIsPresent() {
|
||||
ExportTaskRepository repository = mock(ExportTaskRepository.class);
|
||||
ExportTaskHandler handler = mock(ExportTaskHandler.class);
|
||||
when(handler.exportType()).thenReturn("CUSTOMER_LIST");
|
||||
when(handler.requiredPermission()).thenReturn("platform:customer:export");
|
||||
ExportTaskService service = new ExportTaskService(repository, mock(LocalExportStorage.class),
|
||||
new ObjectMapper(), List.of(handler));
|
||||
doAnswer(invocation -> null).when(repository).insert(any());
|
||||
|
||||
var response = service.create(user(1L, Set.of("platform:customer:export")),
|
||||
new CreateExportTaskRequest("CUSTOMER_LIST", "XLSX", "客户列表", Map.of("status", "ACTIVE")));
|
||||
|
||||
assertEquals("PENDING", response.status());
|
||||
assertEquals("CUSTOMER_LIST", response.exportType());
|
||||
verify(repository).insert(any(FileExportTask.class));
|
||||
BizException denied = assertThrows(BizException.class, () -> service.create(user(1L, Set.of()),
|
||||
new CreateExportTaskRequest("CUSTOMER_LIST", "XLSX", "客户列表", Map.of())));
|
||||
assertEquals(ResultCode.FORBIDDEN, denied.getResultCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void refusesAnotherUsersTaskUnlessDownloadAllIsGranted() {
|
||||
ExportTaskRepository repository = mock(ExportTaskRepository.class);
|
||||
ExportTaskHandler handler = mock(ExportTaskHandler.class);
|
||||
when(handler.exportType()).thenReturn("CUSTOMER_LIST");
|
||||
ExportTaskService service = new ExportTaskService(repository, mock(LocalExportStorage.class),
|
||||
new ObjectMapper(), List.of(handler));
|
||||
FileExportTask task = new FileExportTask();
|
||||
task.setId(9L); task.setTenantId(0L); task.setAppCode("PLATFORM"); task.setRequestedBy(2L);
|
||||
when(repository.find(9L)).thenReturn(task);
|
||||
|
||||
BizException denied = assertThrows(BizException.class, () -> service.get(user(1L, Set.of()), 9L));
|
||||
assertEquals(ResultCode.FORBIDDEN, denied.getResultCode());
|
||||
assertEquals("9", service.get(user(1L, Set.of("platform:download:all")), 9L).id());
|
||||
}
|
||||
|
||||
private AuthenticatedUser user(long id, Set<String> permissions) {
|
||||
return new AuthenticatedUser(id, 0L, 0L, id, "user" + id, "PLATFORM", "WEB", permissions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.baiyue.file.service;
|
||||
|
||||
import com.baiyue.file.entity.FileExportTask;
|
||||
import com.baiyue.file.repository.ExportTaskRepository;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ExportTaskWorkerTest {
|
||||
@Test
|
||||
void onlyExecutesATaskAfterTheAtomicClaimSucceeds() {
|
||||
ExportTaskRepository repository = mock(ExportTaskRepository.class);
|
||||
ExportTaskService service = mock(ExportTaskService.class);
|
||||
LocalExportStorage storage = mock(LocalExportStorage.class);
|
||||
ExportTaskHandler handler = mock(ExportTaskHandler.class);
|
||||
FileExportTask task = new FileExportTask();
|
||||
task.setId(9L); task.setExportType("CUSTOMER_LIST");
|
||||
when(repository.firstPendingId()).thenReturn(9L);
|
||||
when(repository.claim(eq(9L), any(LocalDateTime.class))).thenReturn(true);
|
||||
when(repository.find(9L)).thenReturn(task);
|
||||
when(service.handler("CUSTOMER_LIST")).thenReturn(handler);
|
||||
when(handler.export(eq(task), any())).thenAnswer(invocation -> {
|
||||
ExportTaskHandler.ProgressReporter progress = invocation.getArgument(1);
|
||||
progress.update(12L);
|
||||
return new ExportTaskHandler.ExportResult("客户列表.xlsx", "exports/9/file.xlsx",
|
||||
CustomerExportTaskHandler.CONTENT_TYPE, 1024L, 12L);
|
||||
});
|
||||
when(repository.succeed(eq(9L), eq("客户列表.xlsx"), eq("exports/9/file.xlsx"),
|
||||
eq(CustomerExportTaskHandler.CONTENT_TYPE), eq(1024L), eq(12L),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class))).thenReturn(true);
|
||||
ExportTaskWorker worker = new ExportTaskWorker(repository, service, storage, 7, 30);
|
||||
|
||||
worker.processNext();
|
||||
|
||||
verify(repository).progress(9L, 12L);
|
||||
verify(repository).succeed(eq(9L), eq("客户列表.xlsx"), eq("exports/9/file.xlsx"),
|
||||
eq(CustomerExportTaskHandler.CONTENT_TYPE), eq(1024L), eq(12L),
|
||||
any(LocalDateTime.class), any(LocalDateTime.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void skipsAContendedTaskThatAnotherWorkerClaimed() {
|
||||
ExportTaskRepository repository = mock(ExportTaskRepository.class);
|
||||
when(repository.firstPendingId()).thenReturn(9L);
|
||||
when(repository.claim(eq(9L), any(LocalDateTime.class))).thenReturn(false);
|
||||
ExportTaskWorker worker = new ExportTaskWorker(repository, mock(ExportTaskService.class),
|
||||
mock(LocalExportStorage.class), 7, 30);
|
||||
|
||||
worker.processNext();
|
||||
|
||||
verify(repository, never()).find(9L);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user