✨ 接入账单 OSS 存储并区分生产测试目录

This commit is contained in:
caojia
2026-10-16 16:44:51 +08:00
parent 9824e05919
commit b0719d2ae9
9 changed files with 406 additions and 36 deletions
@@ -0,0 +1,65 @@
package com.bms.ar;
import java.nio.file.Files;
import java.nio.file.Path;
import com.bms.ar.domain.ArBillExport;
import com.bms.ar.mapper.ArBillExportMapper;
import com.bms.ar.service.impl.ArBillServiceImpl;
import com.bms.common.config.BmsConfig;
import com.bms.common.utils.file.OssStorage;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.util.ReflectionTestUtils;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class ArBillExportDownloadTest {
@TempDir
Path tempDir;
@Test
void routesOssDownloadThroughStorage() throws Exception {
OssStorage storage = mock(OssStorage.class);
ArBillServiceImpl service = service("oss://lionqueen/bms/ar/bill/test.xlsx", storage);
MockHttpServletResponse response = new MockHttpServletResponse();
service.downloadExport(1L, response);
verify(storage).download(eq("oss://lionqueen/bms/ar/bill/test.xlsx"), same(response.getOutputStream()));
assertTrue(response.getHeader("Content-Disposition").contains("bill.xlsx"));
}
@Test
void historicalLocalBillStillDownloadsWithoutOss() throws Exception {
String previousProfile = BmsConfig.getProfile();
try {
new BmsConfig().setProfile(tempDir.toString());
Path file = tempDir.resolve("download/ar/test.xlsx");
Files.createDirectories(file.getParent());
Files.write(file, new byte[] {4, 5, 6});
OssStorage storage = mock(OssStorage.class);
MockHttpServletResponse response = new MockHttpServletResponse();
service("/profile/download/ar/test.xlsx", storage).downloadExport(1L, response);
assertArrayEquals(new byte[] {4, 5, 6}, response.getContentAsByteArray());
verifyNoInteractions(storage);
} finally {
new BmsConfig().setProfile(previousProfile);
}
}
private ArBillServiceImpl service(String storageKey, OssStorage storage) {
ArBillExport export = new ArBillExport();
export.setExportId(1L);
export.setExportStatus("SUCCESS");
export.setStorageKey(storageKey);
export.setFileName("bill.xlsx");
ArBillExportMapper mapper = mock(ArBillExportMapper.class);
when(mapper.selectById(1L)).thenReturn(export);
ArBillServiceImpl service = new ArBillServiceImpl();
ReflectionTestUtils.setField(service, "exportMapper", mapper);
ReflectionTestUtils.setField(service, "ossStorage", storage);
return service;
}
}
@@ -0,0 +1,107 @@
package com.bms.common.utils.file;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.nio.file.Files;
import java.nio.file.Path;
import com.aliyun.oss.ClientBuilderConfiguration;
import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder;
import com.aliyun.oss.model.OSSObject;
import com.aliyun.oss.model.PutObjectRequest;
import com.bms.common.config.OssProperties;
import com.bms.common.exception.ServiceException;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.io.TempDir;
import org.mockito.ArgumentCaptor;
import org.mockito.MockedConstruction;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.*;
import static org.mockito.Mockito.*;
class OssStorageTest {
@TempDir
Path tempDir;
private OssProperties properties() {
OssProperties properties = new OssProperties();
properties.setEndpoint("https://oss.example.test");
properties.setBucket("test-bucket");
properties.setAccessKeyId("test-id");
properties.setAccessKeySecret("test-secret");
return properties;
}
@Test
void uploadsPrivateObjectAndClosesClient() throws Exception {
OSS client = mock(OSS.class);
Path file = Files.writeString(tempDir.resolve("bill.xlsx"), "bill");
try (MockedConstruction<OSSClientBuilder> ignored = builder(client)) {
assertEquals("oss://test-bucket/bms/test/ar/bill.xlsx",
new OssStorage(properties()).upload(file, "ar/bill.xlsx", "application/test"));
ArgumentCaptor<PutObjectRequest> request = ArgumentCaptor.forClass(PutObjectRequest.class);
verify(client).putObject(request.capture());
assertEquals("test-bucket", request.getValue().getBucketName());
assertEquals("bms/test/ar/bill.xlsx", request.getValue().getKey());
assertEquals(4, request.getValue().getMetadata().getContentLength());
assertEquals("private", request.getValue().getMetadata().getRawMetadata().get("x-oss-object-acl"));
verify(client).shutdown();
}
}
@Test
void productionUploadUsesConfiguredPrefix() throws Exception {
OSS client = mock(OSS.class);
Path file = Files.writeString(tempDir.resolve("bill.xlsx"), "bill");
OssProperties properties = properties();
properties.setPathPrefix("bms/prod");
try (MockedConstruction<OSSClientBuilder> ignored = builder(client)) {
assertEquals("oss://test-bucket/bms/prod/ar/bill.xlsx",
new OssStorage(properties).upload(file, "ar/bill.xlsx", "application/test"));
verify(client).putObject(argThat((PutObjectRequest request) ->
"bms/prod/ar/bill.xlsx".equals(request.getKey())));
}
}
@Test
void uploadFailureIsPropagatedAndClientClosed() throws Exception {
OSS client = mock(OSS.class);
when(client.putObject(any(PutObjectRequest.class))).thenThrow(new IllegalStateException("upload failed"));
Path file = Files.writeString(tempDir.resolve("bill.xlsx"), "bill");
try (MockedConstruction<OSSClientBuilder> ignored = builder(client)) {
assertThrows(IllegalStateException.class,
() -> new OssStorage(properties()).upload(file, "ar/bill.xlsx", "application/test"));
verify(client).shutdown();
}
}
@Test
void downloadsStoredObjectThroughStream() throws Exception {
OSS client = mock(OSS.class);
OSSObject object = new OSSObject();
object.setObjectContent(new ByteArrayInputStream(new byte[] {1, 2, 3}));
when(client.getObject("test-bucket", "ar/bill.xlsx")).thenReturn(object);
try (MockedConstruction<OSSClientBuilder> ignored = builder(client)) {
ByteArrayOutputStream output = new ByteArrayOutputStream();
new OssStorage(properties()).download("oss://test-bucket/ar/bill.xlsx", output);
assertArrayEquals(new byte[] {1, 2, 3}, output.toByteArray());
verify(client).shutdown();
}
}
@Test
void rejectsMissingCredentialsAndForeignBucket() {
OssStorage storage = new OssStorage(new OssProperties());
assertThrows(ServiceException.class, () -> storage.upload(tempDir.resolve("bill.xlsx"), "ar/bill.xlsx", "application/test"));
assertThrows(ServiceException.class,
() -> new OssStorage(properties()).download("oss://other-bucket/ar/bill.xlsx", new ByteArrayOutputStream()));
}
private MockedConstruction<OSSClientBuilder> builder(OSS client) {
return mockConstruction(OSSClientBuilder.class, (mock, context) ->
when(mock.build(anyString(), anyString(), anyString(), any(ClientBuilderConfiguration.class)))
.thenReturn(client));
}
}