wip: media migration progress
This commit is contained in:
@@ -43,8 +43,8 @@ class PlayClerkUserInfoApiTest extends AbstractApiTest {
|
||||
private final List<String> clerkIdsToCleanup = new ArrayList<>();
|
||||
private int scenarioSequence = 0;
|
||||
private static final Comparator<ClerkScenario> BACKEND_ORDERING = Comparator
|
||||
.comparing(ClerkScenario::isOnline).reversed()
|
||||
.thenComparing(ClerkScenario::isPinned).reversed()
|
||||
.comparing(ClerkScenario::isOnline, Comparator.reverseOrder())
|
||||
.thenComparing(ClerkScenario::isPinned, Comparator.reverseOrder())
|
||||
.thenComparingLong(ClerkScenario::getLevelOrder)
|
||||
.thenComparingInt(ClerkScenario::getSequence)
|
||||
.thenComparing(ClerkScenario::getId);
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.starry.admin.modules.clerk.service.impl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkDataReviewInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.module.vo.PlayClerkDataReviewStateEditVo;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkUserInfoService;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import com.starry.common.enums.ClerkReviewState;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PlayClerkDataReviewInfoServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private IPlayClerkUserInfoService clerkUserInfoService;
|
||||
@Mock
|
||||
private IPlayClerkMediaAssetService clerkMediaAssetService;
|
||||
@Mock
|
||||
private IPlayMediaService mediaService;
|
||||
|
||||
private PlayClerkDataReviewInfoServiceImpl service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = spy(new PlayClerkDataReviewInfoServiceImpl());
|
||||
ReflectionTestUtils.setField(service, "playClerkUserInfoService", clerkUserInfoService);
|
||||
ReflectionTestUtils.setField(service, "clerkMediaAssetService", clerkMediaAssetService);
|
||||
ReflectionTestUtils.setField(service, "mediaService", mediaService);
|
||||
doReturn(true).when(service).update(any(PlayClerkDataReviewInfoEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateDataReviewStateShouldSynchronizeAlbumOnApproval() {
|
||||
PlayClerkDataReviewInfoEntity review = buildReview("review-1", "clerk-1", "2",
|
||||
Arrays.asList("media-existing", "media-new"));
|
||||
doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-1");
|
||||
|
||||
PlayClerkUserInfoEntity clerk = new PlayClerkUserInfoEntity();
|
||||
clerk.setId("clerk-1");
|
||||
clerk.setTenantId("tenant-1");
|
||||
when(clerkUserInfoService.getById("clerk-1")).thenReturn(clerk);
|
||||
|
||||
PlayMediaEntity existingMedia = new PlayMediaEntity();
|
||||
existingMedia.setId("media-existing");
|
||||
when(mediaService.getById("media-existing")).thenReturn(existingMedia);
|
||||
PlayMediaEntity newMedia = new PlayMediaEntity();
|
||||
newMedia.setId("media-new");
|
||||
when(mediaService.getById("media-new")).thenReturn(newMedia);
|
||||
|
||||
PlayClerkMediaAssetEntity assetStub = new PlayClerkMediaAssetEntity();
|
||||
assetStub.setId("asset-1");
|
||||
when(clerkMediaAssetService.linkDraftAsset(any(), any(), any(), any())).thenReturn(assetStub);
|
||||
|
||||
PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
|
||||
vo.setId("review-1");
|
||||
vo.setReviewState(ClerkReviewState.APPROVED);
|
||||
vo.setReviewCon("ok");
|
||||
|
||||
service.updateDataReviewState(vo);
|
||||
|
||||
verify(clerkMediaAssetService).linkDraftAsset(eq("tenant-1"), eq("clerk-1"), eq("media-existing"),
|
||||
eq(ClerkMediaUsage.PROFILE));
|
||||
verify(clerkMediaAssetService).linkDraftAsset(eq("tenant-1"), eq("clerk-1"), eq("media-new"), eq(ClerkMediaUsage.PROFILE));
|
||||
verify(clerkMediaAssetService).applyReviewDecision(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
|
||||
eq(Arrays.asList("media-existing", "media-new")), eq("review-1"), eq("ok"));
|
||||
verify(clerkUserInfoService).update(any(PlayClerkUserInfoEntity.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateDataReviewStateThrowsWhenClerkMissing() {
|
||||
PlayClerkDataReviewInfoEntity review = buildReview("review-2", "ghost", "2",
|
||||
Collections.singletonList("media"));
|
||||
doReturn(review).when(service).selectPlayClerkDataReviewInfoById("review-2");
|
||||
when(clerkUserInfoService.getById("ghost")).thenReturn(null);
|
||||
|
||||
PlayClerkDataReviewStateEditVo vo = new PlayClerkDataReviewStateEditVo();
|
||||
vo.setId("review-2");
|
||||
vo.setReviewState(ClerkReviewState.APPROVED);
|
||||
|
||||
assertThatThrownBy(() -> service.updateDataReviewState(vo))
|
||||
.isInstanceOf(CustomException.class)
|
||||
.hasMessageContaining("店员信息不存在");
|
||||
}
|
||||
|
||||
private PlayClerkDataReviewInfoEntity buildReview(String id, String clerkId, String dataType,
|
||||
java.util.List<String> payload) {
|
||||
PlayClerkDataReviewInfoEntity entity = new PlayClerkDataReviewInfoEntity();
|
||||
entity.setId(id);
|
||||
entity.setClerkId(clerkId);
|
||||
entity.setDataType(dataType);
|
||||
entity.setDataContent(payload);
|
||||
entity.setAddTime(LocalDateTime.now());
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package com.starry.admin.modules.clerk.service.impl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.test.util.ReflectionTestUtils;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class PlayClerkMediaAssetServiceImplTest {
|
||||
|
||||
@Mock
|
||||
private IPlayMediaService mediaService;
|
||||
|
||||
@Test
|
||||
void submitWithOrderShouldReindexAndFlagPending() {
|
||||
PlayClerkMediaAssetServiceImpl service = org.mockito.Mockito.spy(new PlayClerkMediaAssetServiceImpl());
|
||||
ReflectionTestUtils.setField(service, "mediaService", mediaService);
|
||||
List<PlayClerkMediaAssetEntity> assets = Arrays.asList(
|
||||
buildAsset("A", "media-a", 0),
|
||||
buildAsset("B", "media-b", 1),
|
||||
buildAsset("C", "media-c", 2));
|
||||
|
||||
doReturn(assets).when(service).listActiveByUsage("clerk-1", ClerkMediaUsage.PROFILE);
|
||||
|
||||
java.util.List<java.util.List<PlayClerkMediaAssetEntity>> batches = new java.util.ArrayList<>();
|
||||
org.mockito.Mockito.doAnswer(inv -> {
|
||||
java.util.Collection<PlayClerkMediaAssetEntity> collection = inv.getArgument(0);
|
||||
batches.add(new java.util.ArrayList<>(collection));
|
||||
return true;
|
||||
}).when(service).updateBatchById(any());
|
||||
|
||||
service.submitWithOrder("clerk-1", ClerkMediaUsage.PROFILE, Arrays.asList("media-b", "media-a"));
|
||||
|
||||
List<PlayClerkMediaAssetEntity> updates = flatten(batches);
|
||||
PlayClerkMediaAssetEntity b = find(updates, "B");
|
||||
PlayClerkMediaAssetEntity a = find(updates, "A");
|
||||
PlayClerkMediaAssetEntity c = find(updates, "C");
|
||||
|
||||
assertThat(b.getOrderIndex()).isEqualTo(0);
|
||||
assertThat(b.getReviewState()).isEqualTo(ClerkMediaReviewState.PENDING.getCode());
|
||||
assertThat(a.getOrderIndex()).isEqualTo(1);
|
||||
assertThat(a.getReviewState()).isEqualTo(ClerkMediaReviewState.PENDING.getCode());
|
||||
assertThat(c.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
|
||||
}
|
||||
|
||||
@Test
|
||||
void applyReviewDecisionShouldApproveAndReject() {
|
||||
PlayClerkMediaAssetServiceImpl service = org.mockito.Mockito.spy(new PlayClerkMediaAssetServiceImpl());
|
||||
ReflectionTestUtils.setField(service, "mediaService", mediaService);
|
||||
List<PlayClerkMediaAssetEntity> assets = Arrays.asList(
|
||||
buildAsset("A", "media-1", 0),
|
||||
buildAsset("B", "media-2", 1));
|
||||
|
||||
doReturn(assets).when(service).listActiveByUsage("clerk-1", ClerkMediaUsage.PROFILE);
|
||||
when(mediaService.listByIds(any())).thenReturn(Arrays.asList(
|
||||
buildMedia("media-1"),
|
||||
buildMedia("media-2")));
|
||||
|
||||
java.util.List<java.util.List<PlayClerkMediaAssetEntity>> batches = new java.util.ArrayList<>();
|
||||
org.mockito.Mockito.doAnswer(inv -> {
|
||||
java.util.Collection<PlayClerkMediaAssetEntity> collection = inv.getArgument(0);
|
||||
batches.add(new java.util.ArrayList<>(collection));
|
||||
return true;
|
||||
}).when(service).updateBatchById(any());
|
||||
|
||||
service.applyReviewDecision("clerk-1", ClerkMediaUsage.PROFILE, Arrays.asList("media-2"), "review-1", "ok");
|
||||
|
||||
List<PlayClerkMediaAssetEntity> updates = flatten(batches);
|
||||
PlayClerkMediaAssetEntity approved = find(updates, "B");
|
||||
PlayClerkMediaAssetEntity rejected = find(updates, "A");
|
||||
|
||||
assertThat(approved.getReviewState()).isEqualTo(ClerkMediaReviewState.APPROVED.getCode());
|
||||
assertThat(approved.getOrderIndex()).isEqualTo(0);
|
||||
assertThat(approved.getReviewRecordId()).isEqualTo("review-1");
|
||||
assertThat(rejected.getReviewState()).isEqualTo(ClerkMediaReviewState.REJECTED.getCode());
|
||||
}
|
||||
|
||||
private static PlayClerkMediaAssetEntity buildAsset(String id, String mediaId, int order) {
|
||||
PlayClerkMediaAssetEntity entity = new PlayClerkMediaAssetEntity();
|
||||
entity.setId(id);
|
||||
entity.setClerkId("clerk-1");
|
||||
entity.setTenantId("tenant-1");
|
||||
entity.setMediaId(mediaId);
|
||||
entity.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
entity.setReviewState(ClerkMediaReviewState.DRAFT.getCode());
|
||||
entity.setOrderIndex(order);
|
||||
entity.setSubmittedTime(LocalDateTime.now());
|
||||
return entity;
|
||||
}
|
||||
|
||||
private static PlayMediaEntity buildMedia(String id) {
|
||||
PlayMediaEntity media = new PlayMediaEntity();
|
||||
media.setId(id);
|
||||
media.setUrl("url-" + id);
|
||||
return media;
|
||||
}
|
||||
|
||||
private static PlayClerkMediaAssetEntity find(List<PlayClerkMediaAssetEntity> updates, String id) {
|
||||
return updates.stream()
|
||||
.filter(asset -> id.equals(asset.getId()))
|
||||
.findFirst()
|
||||
.orElseThrow();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<PlayClerkMediaAssetEntity> flatten(List<? extends List<PlayClerkMediaAssetEntity>> captured) {
|
||||
return captured.stream()
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toCollection(ArrayList::new));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.starry.admin.modules.clerk.service.impl;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
class PlayClerkUserInfoServiceImplTest {
|
||||
|
||||
@Test
|
||||
void mergeLegacyAlbumAppendsUniqueUrls() {
|
||||
List<String> legacy = Arrays.asList("https://oss/1.png", " ", "https://oss/2.png", "https://oss/1.png");
|
||||
List<MediaVo> destination = new ArrayList<>();
|
||||
MediaVo existing = new MediaVo();
|
||||
existing.setUrl("https://oss/2.png");
|
||||
destination.add(existing);
|
||||
|
||||
List<MediaVo> merged = PlayClerkUserInfoServiceImpl.mergeLegacyAlbum(legacy, destination);
|
||||
|
||||
assertThat(merged).hasSize(2);
|
||||
assertThat(merged.stream().map(MediaVo::getUrl))
|
||||
.containsExactlyInAnyOrder("https://oss/2.png", "https://oss/1.png");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
package com.starry.admin.modules.weichat.controller;
|
||||
|
||||
import static org.hamcrest.Matchers.hasSize;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
|
||||
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
|
||||
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.starry.admin.common.conf.ThreadLocalRequestDetail;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaReviewState;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.enums.MediaOwnerType;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import com.starry.admin.modules.weichat.entity.clerk.MediaOrderRequest;
|
||||
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
|
||||
import com.starry.admin.modules.weichat.service.MediaUploadService;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.test.web.servlet.MockMvc;
|
||||
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
public class WxClerkMediaControllerTest {
|
||||
|
||||
@Mock
|
||||
private MediaUploadService mediaUploadService;
|
||||
@Mock
|
||||
private IPlayMediaService mediaService;
|
||||
@Mock
|
||||
private IPlayClerkMediaAssetService clerkMediaAssetService;
|
||||
@InjectMocks
|
||||
private WxClerkMediaController controller;
|
||||
|
||||
private MockMvc mockMvc;
|
||||
private final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
private PlayClerkUserInfoEntity clerk;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
mockMvc = MockMvcBuilders.standaloneSetup(controller).build();
|
||||
clerk = new PlayClerkUserInfoEntity();
|
||||
clerk.setId("clerk-1");
|
||||
clerk.setTenantId("tenant-1");
|
||||
ThreadLocalRequestDetail.setRequestDetail(clerk);
|
||||
}
|
||||
|
||||
@AfterEach
|
||||
void tearDown() {
|
||||
ThreadLocalRequestDetail.remove();
|
||||
}
|
||||
|
||||
@Test
|
||||
void uploadShouldReturnMediaVo() throws Exception {
|
||||
MediaVo vo = new MediaVo();
|
||||
vo.setId("media-1");
|
||||
vo.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
when(mediaUploadService.upload(any(), eq(clerk), eq(ClerkMediaUsage.PROFILE))).thenReturn(vo);
|
||||
|
||||
MockMultipartFile file = new MockMultipartFile("file", "avatar.png", "image/png", new byte[] {1, 2});
|
||||
|
||||
mockMvc.perform(multipart("/wx/clerk/media/upload")
|
||||
.file(file)
|
||||
.param("usage", ClerkMediaUsage.PROFILE.getCode()))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data.id").value("media-1"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void updateOrderShouldDelegateToService() throws Exception {
|
||||
MediaOrderRequest request = new MediaOrderRequest();
|
||||
request.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
request.setMediaIds(Arrays.asList("m1", "m2"));
|
||||
|
||||
mockMvc.perform(put("/wx/clerk/media/order")
|
||||
.contentType(MediaType.APPLICATION_JSON)
|
||||
.content(objectMapper.writeValueAsString(request)))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
|
||||
verify(clerkMediaAssetService).submitWithOrder(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
|
||||
eq(Arrays.asList("m1", "m2")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void listApprovedShouldReturnAssemblerOutput() throws Exception {
|
||||
PlayClerkMediaAssetEntity asset = new PlayClerkMediaAssetEntity();
|
||||
asset.setId("asset-1");
|
||||
asset.setClerkId("clerk-1");
|
||||
asset.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
asset.setMediaId("media-1");
|
||||
asset.setReviewState(ClerkMediaReviewState.APPROVED.getCode());
|
||||
asset.setCreatedTime(java.sql.Timestamp.valueOf(LocalDateTime.now()));
|
||||
when(clerkMediaAssetService.listByState(eq("clerk-1"), eq(ClerkMediaUsage.PROFILE),
|
||||
eq(Collections.singletonList(ClerkMediaReviewState.APPROVED)))).thenReturn(
|
||||
Collections.singletonList(asset));
|
||||
|
||||
PlayMediaEntity media = new PlayMediaEntity();
|
||||
media.setId("media-1");
|
||||
media.setUrl("https://oss/mock.png");
|
||||
when(mediaService.listByIds(Collections.singletonList("media-1")))
|
||||
.thenReturn(Collections.singletonList(media));
|
||||
|
||||
mockMvc.perform(get("/wx/clerk/media/approved"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200))
|
||||
.andExpect(jsonPath("$.data", hasSize(1)))
|
||||
.andExpect(jsonPath("$.data[0].url").value("https://oss/mock.png"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void deleteShouldInvokeServices() throws Exception {
|
||||
mockMvc.perform(delete("/wx/clerk/media/{id}", "media-77"))
|
||||
.andExpect(status().isOk())
|
||||
.andExpect(jsonPath("$.code").value(200));
|
||||
|
||||
verify(clerkMediaAssetService).softDelete("clerk-1", "media-77");
|
||||
verify(mediaService).softDelete(MediaOwnerType.CLERK, "clerk-1", "media-77");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
package com.starry.admin.modules.weichat.service;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatThrownBy;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import com.starry.admin.common.exception.CustomException;
|
||||
import com.starry.admin.common.oss.service.IOssFileService;
|
||||
import com.starry.admin.modules.clerk.enums.ClerkMediaUsage;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkMediaAssetEntity;
|
||||
import com.starry.admin.modules.clerk.module.entity.PlayClerkUserInfoEntity;
|
||||
import com.starry.admin.modules.clerk.service.IPlayClerkMediaAssetService;
|
||||
import com.starry.admin.modules.media.entity.PlayMediaEntity;
|
||||
import com.starry.admin.modules.media.enums.MediaOwnerType;
|
||||
import com.starry.admin.modules.media.service.IPlayMediaService;
|
||||
import com.starry.admin.modules.weichat.entity.clerk.MediaVo;
|
||||
import java.awt.image.BufferedImage;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import javax.imageio.ImageIO;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.DisplayName;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.springframework.mock.web.MockMultipartFile;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class MediaUploadServiceTest {
|
||||
|
||||
@Mock
|
||||
private IOssFileService ossFileService;
|
||||
@Mock
|
||||
private IPlayMediaService mediaService;
|
||||
@Mock
|
||||
private IPlayClerkMediaAssetService clerkMediaAssetService;
|
||||
|
||||
private MediaUploadService service;
|
||||
|
||||
@BeforeEach
|
||||
void setUp() {
|
||||
service = new MediaUploadService(ossFileService, mediaService, clerkMediaAssetService);
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("上传图片时应创建媒资与草稿资产,并返回完整的 MediaVo")
|
||||
void uploadImageCreatesMediaAndAsset() throws Exception {
|
||||
PlayClerkUserInfoEntity clerk = buildClerk();
|
||||
MultipartFile file = buildImageFile("avatar.png", "image/png");
|
||||
|
||||
when(mediaService.normalizeAndSave(any())).thenAnswer(invocation -> invocation.getArgument(0));
|
||||
when(mediaService.updateById(any())).thenReturn(true);
|
||||
when(ossFileService.upload(any(), eq(clerk.getTenantId()), any()))
|
||||
.thenReturn("https://oss.mock/avatar.png");
|
||||
|
||||
PlayClerkMediaAssetEntity asset = new PlayClerkMediaAssetEntity();
|
||||
asset.setId("asset-1");
|
||||
asset.setUsage(ClerkMediaUsage.PROFILE.getCode());
|
||||
asset.setReviewState("draft");
|
||||
when(clerkMediaAssetService.linkDraftAsset(any(), any(), any(), any())).thenAnswer(invocation -> {
|
||||
asset.setMediaId(invocation.getArgument(2));
|
||||
return asset;
|
||||
});
|
||||
|
||||
MediaVo result = service.upload(file, clerk, null);
|
||||
|
||||
assertThat(result).isNotNull();
|
||||
assertThat(result.getUrl()).isEqualTo("https://oss.mock/avatar.png");
|
||||
assertThat(result.getUsage()).isEqualTo(ClerkMediaUsage.PROFILE.getCode());
|
||||
assertThat(result.getAssetId()).isEqualTo("asset-1");
|
||||
|
||||
ArgumentCaptor<PlayMediaEntity> mediaCaptor = ArgumentCaptor.forClass(PlayMediaEntity.class);
|
||||
verify(mediaService).normalizeAndSave(mediaCaptor.capture());
|
||||
PlayMediaEntity persisted = mediaCaptor.getValue();
|
||||
assertThat(persisted.getOwnerType()).isEqualTo(MediaOwnerType.CLERK);
|
||||
assertThat(persisted.getOwnerId()).isEqualTo(clerk.getId());
|
||||
assertThat(persisted.getMetadata()).containsKeys("originalFilename", "contentType", "uploadTraceId");
|
||||
|
||||
verify(clerkMediaAssetService).linkDraftAsset(
|
||||
eq(clerk.getTenantId()),
|
||||
eq(clerk.getId()),
|
||||
eq(persisted.getId()),
|
||||
eq(ClerkMediaUsage.PROFILE));
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("不支援的檔案格式應回報錯誤,且不呼叫外部服務")
|
||||
void uploadRejectsUnsupportedFormat() {
|
||||
PlayClerkUserInfoEntity clerk = buildClerk();
|
||||
MultipartFile invalidFile = new MockMultipartFile("file", "note.txt", "text/plain", "oops".getBytes());
|
||||
|
||||
assertThatThrownBy(() -> service.upload(invalidFile, clerk, ClerkMediaUsage.PROFILE))
|
||||
.isInstanceOf(CustomException.class)
|
||||
.hasMessage("不支持的文件格式");
|
||||
|
||||
verify(mediaService, never()).normalizeAndSave(any());
|
||||
verify(ossFileService, never()).upload(any(), any(), any());
|
||||
verify(clerkMediaAssetService, never()).linkDraftAsset(any(), any(), any(), any());
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("缺少檔案或店員資訊時應回報錯誤")
|
||||
void uploadRejectsMissingInputs() {
|
||||
PlayClerkUserInfoEntity clerk = buildClerk();
|
||||
MultipartFile emptyFile = new MockMultipartFile("file", new byte[0]);
|
||||
|
||||
assertThatThrownBy(() -> service.upload(emptyFile, clerk, ClerkMediaUsage.PROFILE))
|
||||
.isInstanceOf(CustomException.class)
|
||||
.hasMessage("请选择要上传的文件");
|
||||
|
||||
MultipartFile file = new MockMultipartFile("file", "a.png", "image/png", new byte[] {1});
|
||||
assertThatThrownBy(() -> service.upload(file, null, ClerkMediaUsage.PROFILE))
|
||||
.isInstanceOf(CustomException.class)
|
||||
.hasMessage("店员信息不存在");
|
||||
}
|
||||
|
||||
private MultipartFile buildImageFile(String filename, String contentType) throws IOException {
|
||||
BufferedImage image = new BufferedImage(2, 2, BufferedImage.TYPE_INT_RGB);
|
||||
try (ByteArrayOutputStream baos = new ByteArrayOutputStream()) {
|
||||
ImageIO.write(image, "png", baos);
|
||||
return new MockMultipartFile("file", filename, contentType, baos.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
private PlayClerkUserInfoEntity buildClerk() {
|
||||
PlayClerkUserInfoEntity entity = new PlayClerkUserInfoEntity();
|
||||
entity.setId("clerk-1");
|
||||
entity.setTenantId("tenant-1");
|
||||
return entity;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user