package com.sinata.appointment.service; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Mockito.mock; import com.sinata.appointment.config.LocalFileUploadProperties; import com.sinata.appointment.mapper.MpStaffGroupMapper; import com.sinata.appointment.mapper.MpStaffGroupMemberMapper; import com.sinata.appointment.mapper.MpUserMapper; import com.sinata.appointment.mapper.SysDeptMapper; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; import org.junit.jupiter.api.Test; import org.springframework.mock.web.MockHttpServletRequest; import org.springframework.test.util.ReflectionTestUtils; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import org.springframework.web.multipart.MultipartFile; class UploadStreamCloseTest { @Test void localImageUploadClosesMultipartInputStream() throws Exception { LocalFileUploadProperties properties = new LocalFileUploadProperties(); ReflectionTestUtils.setField(properties, "fileUploadPath", Files.createTempDirectory("image-upload").toString()); ReflectionTestUtils.setField(properties, "pictureServerAddress", "https://example.test/resources"); LocalImageUploadService service = new LocalImageUploadService(properties); CloseTrackingMultipartFile file = new CloseTrackingMultipartFile("photo.png", "image/png"); service.uploadImage(file); assertThat(file.lastStreamClosed()).isTrue(); } @Test void staffAvatarUploadClosesMultipartInputStream() throws Exception { String previousUserDir = System.getProperty("user.dir"); System.setProperty("user.dir", Files.createTempDirectory("staff-avatar-upload").toString()); RequestContextHolder.setRequestAttributes(new ServletRequestAttributes(new MockHttpServletRequest())); try { StaffManageApplicationService service = new StaffManageApplicationService( mock(MpUserMapper.class), mock(SysDeptMapper.class), mock(MpStaffGroupMemberMapper.class), mock(MpStaffGroupMapper.class)); CloseTrackingMultipartFile file = new CloseTrackingMultipartFile("avatar.jpg", "image/jpeg"); service.uploadAvatar(file); assertThat(file.lastStreamClosed()).isTrue(); } finally { RequestContextHolder.resetRequestAttributes(); System.setProperty("user.dir", previousUserDir); } } private static final class CloseTrackingMultipartFile implements MultipartFile { private final String originalFilename; private final String contentType; private CloseTrackingInputStream lastStream; private CloseTrackingMultipartFile(String originalFilename, String contentType) { this.originalFilename = originalFilename; this.contentType = contentType; } boolean lastStreamClosed() { return lastStream != null && lastStream.closed; } @Override public String getName() { return "file"; } @Override public String getOriginalFilename() { return originalFilename; } @Override public String getContentType() { return contentType; } @Override public boolean isEmpty() { return false; } @Override public long getSize() { return 4; } @Override public byte[] getBytes() { return new byte[] {1, 2, 3, 4}; } @Override public InputStream getInputStream() { lastStream = new CloseTrackingInputStream(getBytes()); return lastStream; } @Override public void transferTo(java.io.File dest) throws IOException, IllegalStateException { Files.write(dest.toPath(), getBytes()); } } private static final class CloseTrackingInputStream extends ByteArrayInputStream { private boolean closed; private CloseTrackingInputStream(byte[] buf) { super(buf); } @Override public void close() throws IOException { closed = true; super.close(); } } }