新需求更新 同步更新
This commit is contained in:
+53
@@ -0,0 +1,53 @@
|
||||
package com.nanri.aiimage.modules.filetemplate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
class ModuleTemplateControllerTest {
|
||||
|
||||
@Test
|
||||
void returnsXlsxWithUtf8AttachmentFilename() throws Exception {
|
||||
ModuleTemplateService service = mock(ModuleTemplateService.class);
|
||||
byte[] bytes = {1, 2, 3, 4};
|
||||
when(service.download("publish")).thenReturn(new ModuleTemplateService.TemplateDownload(
|
||||
"上架 文档格式.xlsx",
|
||||
ModuleTemplateService.XLSX_CONTENT_TYPE,
|
||||
bytes));
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new ModuleTemplateController(service)).build();
|
||||
|
||||
mockMvc.perform(get("/api/module-templates/publish/download").param("user_id", "1"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(content().bytes(bytes))
|
||||
.andExpect(content().contentType(ModuleTemplateService.XLSX_CONTENT_TYPE))
|
||||
.andExpect(header().longValue(HttpHeaders.CONTENT_LENGTH, bytes.length))
|
||||
.andExpect(header().string(HttpHeaders.CONTENT_DISPOSITION,
|
||||
"attachment; filename=\"download.xlsx\"; "
|
||||
+ "filename*=UTF-8''%E4%B8%8A%E6%9E%B6%20%E6%96%87%E6%A1%A3%E6%A0%BC%E5%BC%8F.xlsx"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void propagatesUnknownModuleAndStorageStatusCodes() throws Exception {
|
||||
ModuleTemplateService service = mock(ModuleTemplateService.class);
|
||||
when(service.download("missing"))
|
||||
.thenThrow(new ResponseStatusException(HttpStatus.NOT_FOUND, "模板不存在"));
|
||||
when(service.download("publish"))
|
||||
.thenThrow(new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "模板存储暂不可用"));
|
||||
MockMvc mockMvc = MockMvcBuilders.standaloneSetup(new ModuleTemplateController(service)).build();
|
||||
|
||||
mockMvc.perform(get("/api/module-templates/missing/download"))
|
||||
.andExpect(status().isNotFound());
|
||||
mockMvc.perform(get("/api/module-templates/publish/download"))
|
||||
.andExpect(status().isServiceUnavailable());
|
||||
}
|
||||
}
|
||||
+51
@@ -0,0 +1,51 @@
|
||||
package com.nanri.aiimage.modules.filetemplate;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
class ModuleTemplateRegistryTest {
|
||||
|
||||
@Test
|
||||
void exposesSixFixedModulesWithPackagedXlsxResources() throws Exception {
|
||||
ModuleTemplateRegistry registry = new ModuleTemplateRegistry();
|
||||
|
||||
assertEquals(Set.of(
|
||||
"publish",
|
||||
"delete-brand",
|
||||
"appearance-patent",
|
||||
"price-track",
|
||||
"collect-data",
|
||||
"similar-asin"),
|
||||
registry.templates().stream()
|
||||
.map(ModuleTemplateRegistry.ModuleTemplate::moduleCode)
|
||||
.collect(Collectors.toSet()));
|
||||
|
||||
for (ModuleTemplateRegistry.ModuleTemplate template : registry.templates()) {
|
||||
assertTrue(template.resourcePath().matches("templates/module-input/[a-z-]+\\.xlsx"));
|
||||
assertEquals("input/" + template.resourcePath().substring(template.resourcePath().lastIndexOf('/') + 1),
|
||||
template.objectKey());
|
||||
ClassPathResource resource = new ClassPathResource(template.resourcePath());
|
||||
assertTrue(resource.exists(), template.resourcePath());
|
||||
try (InputStream input = resource.getInputStream()) {
|
||||
assertArrayEquals(new byte[]{'P', 'K'}, input.readNBytes(2), template.resourcePath());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void lookupNormalizesCaseAndWhitespaceWithoutAcceptingUnknownCodes() {
|
||||
ModuleTemplateRegistry registry = new ModuleTemplateRegistry();
|
||||
|
||||
assertEquals("上架 文档格式.xlsx", registry.find(" PUBLISH ").orElseThrow().downloadFilename());
|
||||
assertTrue(registry.find("../../publish").isEmpty());
|
||||
assertTrue(registry.find(null).isEmpty());
|
||||
}
|
||||
}
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
package com.nanri.aiimage.modules.filetemplate;
|
||||
|
||||
import com.nanri.aiimage.config.OssProperties;
|
||||
import com.nanri.aiimage.modules.file.service.oss.OssStorageService;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.server.ResponseStatusException;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.verifyNoInteractions;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class ModuleTemplateServiceTest {
|
||||
|
||||
private static final String BUCKET = "aiimage-templates";
|
||||
|
||||
private OssStorageService ossStorageService;
|
||||
private ModuleTemplateService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
OssProperties properties = new OssProperties();
|
||||
properties.setTemplateBucket(BUCKET);
|
||||
ossStorageService = mock(OssStorageService.class);
|
||||
service = new ModuleTemplateService(new ModuleTemplateRegistry(), properties, ossStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void downloadsExistingObjectWithoutUploadingItAgain() {
|
||||
byte[] stored = {1, 2, 3};
|
||||
when(ossStorageService.objectExists(BUCKET, "input/publish.xlsx")).thenReturn(true);
|
||||
when(ossStorageService.readObjectBytes(BUCKET, "input/publish.xlsx")).thenReturn(stored);
|
||||
|
||||
ModuleTemplateService.TemplateDownload download = service.download("publish");
|
||||
|
||||
assertEquals("上架 文档格式.xlsx", download.filename());
|
||||
assertEquals(ModuleTemplateService.XLSX_CONTENT_TYPE, download.contentType());
|
||||
assertArrayEquals(stored, download.content());
|
||||
verify(ossStorageService).ensureBucketExists(BUCKET);
|
||||
verify(ossStorageService, never()).uploadBytes(
|
||||
eq(BUCKET), eq("input/publish.xlsx"), org.mockito.ArgumentMatchers.any(), anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
void lazilyUploadsMissingObjectBeforeReadingItFromStorage() {
|
||||
byte[] stored = {4, 5, 6};
|
||||
when(ossStorageService.objectExists(BUCKET, "input/publish.xlsx")).thenReturn(false);
|
||||
when(ossStorageService.readObjectBytes(BUCKET, "input/publish.xlsx")).thenReturn(stored);
|
||||
|
||||
ModuleTemplateService.TemplateDownload download = service.download("publish");
|
||||
|
||||
assertArrayEquals(stored, download.content());
|
||||
ArgumentCaptor<byte[]> uploaded = ArgumentCaptor.forClass(byte[].class);
|
||||
verify(ossStorageService).uploadBytes(
|
||||
eq(BUCKET), eq("input/publish.xlsx"), uploaded.capture(),
|
||||
eq(ModuleTemplateService.XLSX_CONTENT_TYPE));
|
||||
assertTrue(uploaded.getValue().length > 2);
|
||||
assertArrayEquals(new byte[]{'P', 'K'}, new byte[]{uploaded.getValue()[0], uploaded.getValue()[1]});
|
||||
verify(ossStorageService).readObjectBytes(BUCKET, "input/publish.xlsx");
|
||||
}
|
||||
|
||||
@Test
|
||||
void startupSynchronizationUploadsOnlyMissingTemplates() {
|
||||
when(ossStorageService.objectExists(BUCKET, "input/publish.xlsx")).thenReturn(true);
|
||||
|
||||
assertEquals(5, service.synchronizeAll());
|
||||
|
||||
verify(ossStorageService).ensureBucketExists(BUCKET);
|
||||
verify(ossStorageService, times(6)).objectExists(eq(BUCKET), anyString());
|
||||
verify(ossStorageService, never()).uploadBytes(
|
||||
eq(BUCKET), eq("input/publish.xlsx"), org.mockito.ArgumentMatchers.any(), anyString());
|
||||
verify(ossStorageService, times(5)).uploadBytes(
|
||||
eq(BUCKET), anyString(), org.mockito.ArgumentMatchers.any(),
|
||||
eq(ModuleTemplateService.XLSX_CONTENT_TYPE));
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownModuleReturnsNotFoundWithoutTouchingStorage() {
|
||||
ResponseStatusException ex = assertThrows(ResponseStatusException.class,
|
||||
() -> service.download("not-a-module"));
|
||||
|
||||
assertEquals(HttpStatus.NOT_FOUND, ex.getStatusCode());
|
||||
verifyNoInteractions(ossStorageService);
|
||||
}
|
||||
|
||||
@Test
|
||||
void storageFailureReturnsServiceUnavailable() {
|
||||
doThrow(new IllegalStateException("MinIO unavailable"))
|
||||
.when(ossStorageService).ensureBucketExists(BUCKET);
|
||||
|
||||
ResponseStatusException ex = assertThrows(ResponseStatusException.class,
|
||||
() -> service.download("publish"));
|
||||
|
||||
assertEquals(HttpStatus.SERVICE_UNAVAILABLE, ex.getStatusCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializerLeavesApplicationRunningWhenStartupStorageIsUnavailable() {
|
||||
ModuleTemplateService unavailableService = mock(ModuleTemplateService.class);
|
||||
doThrow(new ResponseStatusException(HttpStatus.SERVICE_UNAVAILABLE, "unavailable"))
|
||||
.when(unavailableService).synchronizeAll();
|
||||
ModuleTemplateStorageInitializer initializer = new ModuleTemplateStorageInitializer(unavailableService);
|
||||
|
||||
assertDoesNotThrow(initializer::synchronize);
|
||||
verify(unavailableService).synchronizeAll();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user