广告管理service、controller单元测试完善

This commit is contained in:
gaoqr
2025-10-28 15:49:51 +08:00
parent 8d6ce2a915
commit a091b7d574
12 changed files with 602 additions and 298 deletions
@@ -0,0 +1,201 @@
package com.cf.imes.module.system.controller.admin.funds.advertisement;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRePublishReqVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRespVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementSaveReqVO;
import com.cf.imes.module.system.service.funds.advertisement.AdvertisementService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.security.servlet.SecurityAutoConfiguration;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.web.servlet.MockMvc;
import java.time.LocalDate;
import java.util.Collections;
import java.util.List;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.multipart;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
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;
/**
* @author Gqr
* @since 2025/10/28 15:05
*/
@WebMvcTest(controllers = AdvertisementController.class, excludeAutoConfiguration = SecurityAutoConfiguration.class)
public class AdvertisementControllerTest {
@Autowired
private MockMvc mockMvc;
@MockitoBean
private AdvertisementService advertisementService;
@Autowired
private ObjectMapper objectMapper;
@Test
void testCreateAdvertisement() throws Exception {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setAdName("测试广告");
reqVO.setAdPosition(List.of(1));
reqVO.setAdImagePath("test.jpg");
LocalDate now = LocalDate.now();
reqVO.setStartTime(now);
reqVO.setEndTime(now.plusDays(1));
when(advertisementService.create(any())).thenReturn(101L);
mockMvc.perform(put("/system/ad")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(101));
verify(advertisementService).create(any());
}
@Test
void testUpdateAdvertisement() throws Exception {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setId(1L);
reqVO.setAdName("测试更新广告");
reqVO.setAdPosition(List.of(1));
reqVO.setAdImagePath("test.jpg");
LocalDate now = LocalDate.now();
reqVO.setStartTime(now);
reqVO.setEndTime(now.plusDays(1));
doNothing().when(advertisementService).update(any());
mockMvc.perform(post("/system/ad")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(advertisementService).update(any());
}
@Test
void testUploadAdImage() throws Exception {
MockMultipartFile file = new MockMultipartFile("file", "ad.png", "image/png", "test data".getBytes());
when(advertisementService.uploadAdImage(any())).thenReturn("https://cdn.example.com/ad.png");
mockMvc.perform(multipart("/system/ad/image")
.file(file).contentType(MediaType.MULTIPART_FORM_DATA))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value("https://cdn.example.com/ad.png"));
verify(advertisementService).uploadAdImage(any());
}
@Test
void testDeleteAdvertisement() throws Exception {
mockMvc.perform(delete("/system/ad/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(advertisementService).delete(1L);
}
@Test
void testGetAdvertisement() throws Exception {
AdvertisementRespVO respVO = new AdvertisementRespVO();
respVO.setId(1L);
respVO.setAdName("首页广告");
when(advertisementService.get(1L)).thenReturn(respVO);
mockMvc.perform(get("/system/ad/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.id").value(1L))
.andExpect(jsonPath("$.data.adName").value("首页广告"));
verify(advertisementService).get(1L);
}
@Test
void testGetAdvertisementPage() throws Exception {
AdvertisementPageReqVO reqVO = new AdvertisementPageReqVO();
PageResult<AdvertisementRespVO> mockPage = new PageResult<>();
mockPage.setTotal(1L);
mockPage.setList(List.of(new AdvertisementRespVO().setId(1L).setAdName("分页广告")));
when(advertisementService.getPage(any())).thenReturn(mockPage);
mockMvc.perform(get("/system/ad/page")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data.list[0].id").value(1L))
.andExpect(jsonPath("$.data.list[0].adName").value("分页广告"));
verify(advertisementService, times(1)).getPage(any());
}
@Test
void testGetAdvertisementByPosition() throws Exception {
AdvertisementRespVO respVO = new AdvertisementRespVO();
respVO.setId(1L);
respVO.setAdName("首页banner");
when(advertisementService.getAdvertisementByPosition(1))
.thenReturn(Collections.singletonList(respVO));
mockMvc.perform(get("/system/ad/position/page")
.param("adPosition", "1"))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data[0].id").value(1L))
.andExpect(jsonPath("$.data[0].adName").value("首页banner"));
verify(advertisementService).getAdvertisementByPosition(1);
}
@Test
void testPublish() throws Exception {
mockMvc.perform(post("/system/ad/publish/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(advertisementService).publish(1L);
}
@Test
void testEndPublish() throws Exception {
mockMvc.perform(post("/system/ad/endPublish/{id}", 1L))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(advertisementService).endPublish(1L);
}
@Test
void testRePublish() throws Exception {
AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO();
reqVO.setId(1L);
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
mockMvc.perform(post("/system/ad/republish")
.contentType(MediaType.APPLICATION_JSON)
.content(objectMapper.writeValueAsString(reqVO)))
.andExpect(status().isOk())
.andExpect(jsonPath("$.data").value(true));
verify(advertisementService).republish(any());
}
}
@@ -0,0 +1,368 @@
package com.cf.imes.module.system.service.funds.advertisement;
import com.cf.imes.framework.common.pojo.PageResult;
import com.cf.imes.framework.test.core.ut.BaseDbAndRedisUnitTest;
import com.cf.imes.module.infra.api.file.FileApi;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementPageReqVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRePublishReqVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementRespVO;
import com.cf.imes.module.system.controller.admin.funds.advertisement.vo.AdvertisementSaveReqVO;
import com.cf.imes.module.system.dal.dataobject.funds.advertisement.AdvertisementDO;
import com.cf.imes.module.system.dal.mysql.funds.advertisement.AdvertisementMapper;
import com.cf.imes.module.system.enums.advertisement.AdvertisementStatusEnum;
import jakarta.annotation.Resource;
import org.junit.jupiter.api.MethodOrderer;
import org.junit.jupiter.api.Order;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.TestMethodOrder;
import org.springframework.context.annotation.Import;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.time.LocalDate;
import java.util.List;
import static com.cf.imes.framework.test.core.util.AssertUtils.assertServiceException;
import static com.cf.imes.framework.test.core.util.RandomUtils.randomPojo;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_IMAGE_EMPTY_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_IMAGE_UPLOAD_FAIL;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NAME_IS_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NO_DELETE;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_NO_EXIST;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_STATUS_ENDTIME_EXPIRED;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_TIME_ERROR;
import static com.cf.imes.module.system.enums.ErrorCodeConstants.ADVERTISEMENT_UPDATE_STATUS_ERROR;
import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.mockito.ArgumentMatchers.any;
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.when;
/**
* @author Gqr
* @since 2025/10/28 10:15
*/
@Import(AdvertisementServiceImpl.class)
@TestMethodOrder(MethodOrderer.OrderAnnotation.class)
public class AdvertisementServiceImplTest extends BaseDbAndRedisUnitTest {
@Resource
private AdvertisementService advertisementService;
@Resource
private AdvertisementMapper advertisementMapper;
@MockitoBean
private FileApi fileApi;
private static AdvertisementDO insertedOne;
private static Long insertTwoId;
@Test
@Order(1)
void testCreate_whenStartTimeAfterEndTime_shouldThrow() {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setStartTime(LocalDate.now().plusDays(1));
reqVO.setEndTime(LocalDate.now());
assertServiceException(() -> advertisementService.create(reqVO), ADVERTISEMENT_TIME_ERROR);
}
@Test
@Order(2)
void testCreate_whenAdNameExists() {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
reqVO.setAdName("TestAd");
AdvertisementDO existing = randomPojo(AdvertisementDO.class, o -> o.setAdName("TestAd"));
advertisementMapper.insert(existing);
assertServiceException(() -> advertisementService.create(reqVO), ADVERTISEMENT_NAME_IS_EXIST);
}
@Test
@Order(3)
void testCreate_success() {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
reqVO.setAdName("Ad-CREATE-1");
reqVO.setAdPosition(List.of(3, 1, 3));
reqVO.setAdImagePath("https://example.com/image.png");
Long id = advertisementService.create(reqVO);
insertedOne = advertisementMapper.selectById(id);
// 验证id
assertEquals(insertedOne.getId(), id);
// 验证 adPosition 去重排序
assertEquals(List.of(1, 3), insertedOne.getAdPosition());
// 验证状态
assertEquals(AdvertisementStatusEnum.UNPUBLISHED.getStatus(), insertedOne.getStatus());
// 验证名称
assertEquals("Ad-CREATE-1", insertedOne.getAdName());
}
@Test
@Order(4)
void testUpdate_whenStartTimeAfterEndTime() {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setStartTime(LocalDate.now().plusDays(1));
reqVO.setEndTime(LocalDate.now());
assertServiceException(() -> advertisementService.update(reqVO), ADVERTISEMENT_TIME_ERROR);
}
@Test
@Order(5)
void testUpdate_adExist(){
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setId(123L);
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
assertServiceException(() -> advertisementService.update(reqVO), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(5)
void testUpdate_whenAdNameExists() {
AdvertisementSaveReqVO reqVO = new AdvertisementSaveReqVO();
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
reqVO.setAdName("Ad-CREATE-2");
reqVO.setAdPosition(List.of(3, 1, 3));
reqVO.setAdImagePath("https://example.com/image.png");
insertTwoId = advertisementService.create(reqVO);
AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO();
updateReqVO.setId(insertedOne.getId());
updateReqVO.setStartTime(LocalDate.now());
updateReqVO.setEndTime(LocalDate.now().plusDays(1));
updateReqVO.setAdName("Ad-CREATE-2");
assertServiceException(() -> advertisementService.update(updateReqVO), ADVERTISEMENT_NAME_IS_EXIST);
}
@Test
@Order(6)
void testUpdate_success() {
AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO();
updateReqVO.setId(insertedOne.getId());
updateReqVO.setStartTime(LocalDate.now());
updateReqVO.setEndTime(LocalDate.now().plusDays(1));
updateReqVO.setAdName("Ad-UPDATE-1");
updateReqVO.setAdPosition(List.of(1,3));
updateReqVO.setAdImagePath("https://example.com/image.jpg");
advertisementService.update(updateReqVO);
AdvertisementDO db = advertisementMapper.selectById(insertedOne.getId());
assertEquals(db.getAdName(), "Ad-UPDATE-1");
assertEquals(db.getAdPosition(), List.of(1,3));
assertEquals(db.getAdImagePath(), "https://example.com/image.jpg");
}
@Test
@Order(7)
void testDelete_notExist() {
assertServiceException(() -> advertisementService.delete(123L), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(8)
void testPublish_notExist(){
assertServiceException(() -> advertisementService.publish(123L), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(9)
void testUpdate_endTime_success() {
AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO();
updateReqVO.setId(insertTwoId);
updateReqVO.setStartTime(LocalDate.now().minusDays(5));
updateReqVO.setEndTime(LocalDate.now().minusDays(4));
updateReqVO.setAdName("Ad-UPDATE-2");
updateReqVO.setAdPosition(List.of(2));
assertDoesNotThrow(() -> advertisementService.update(updateReqVO));
}
@Test
@Order(10)
void testPublish_endtime_expired() {
assertServiceException(() -> advertisementService.publish(insertTwoId), ADVERTISEMENT_STATUS_ENDTIME_EXPIRED);
}
@Test
@Order(11)
void testPublish_success() {
advertisementService.publish(insertedOne.getId());
AdvertisementDO db = advertisementMapper.selectById(insertedOne.getId());
assertEquals(db.getStatus(), AdvertisementStatusEnum.PUBLISHED.getStatus());
}
@Test
@Order(12)
void testUpdate_published() {
AdvertisementSaveReqVO updateReqVO = new AdvertisementSaveReqVO();
updateReqVO.setId(insertedOne.getId());
updateReqVO.setStartTime(LocalDate.now());
updateReqVO.setEndTime(LocalDate.now());
assertServiceException(() -> advertisementService.update(updateReqVO), ADVERTISEMENT_UPDATE_STATUS_ERROR);
}
@Test
@Order(13)
void testDelete_published() {
assertServiceException(() -> advertisementService.delete(insertedOne.getId()), ADVERTISEMENT_NO_DELETE);
}
@Test
@Order(14)
void testEndPublish_notExist() {
assertServiceException(() -> advertisementService.endPublish(null), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(15)
void testGet_notExist() {
assertServiceException(() -> advertisementService.get(123L), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(16)
void testGet() {
assertNotNull(advertisementService.get(insertedOne.getId()));
}
@Test
@Order(17)
void testGetByPosition() {
List<AdvertisementRespVO> advertisementRespVOS = advertisementService.getAdvertisementByPosition(1);
assertEquals(1, advertisementRespVOS.size());
advertisementRespVOS = advertisementService.getAdvertisementByPosition(2);
assertEquals(0, advertisementRespVOS.size());
}
@Test
@Order(18)
void testEndPublish() {
advertisementService.endPublish(insertTwoId);
AdvertisementDO db = advertisementMapper.selectById(insertTwoId);
assertEquals(db.getStatus(), AdvertisementStatusEnum.ENDED.getStatus());
}
@Test
@Order(19)
void testGetPage(){
AdvertisementPageReqVO pageReqVO = new AdvertisementPageReqVO();
pageReqVO.setStatus(AdvertisementStatusEnum.PUBLISHED.getStatus());
pageReqVO.setAdName("Ad-UPDATE-1");
PageResult<AdvertisementRespVO> page = advertisementService.getPage(pageReqVO);
assertEquals(1, page.getTotal());
pageReqVO.setStatus(AdvertisementStatusEnum.ENDED.getStatus());
pageReqVO.setAdName("Ad-UPDATE-2");
page = advertisementService.getPage(pageReqVO);
assertEquals(1, page.getTotal());
pageReqVO.setStatus(AdvertisementStatusEnum.UNPUBLISHED.getStatus());
page = advertisementService.getPage(pageReqVO);
assertEquals(0, page.getTotal());
}
@Test
@Order(20)
void testDelete_success() {
advertisementService.delete(insertTwoId);
AdvertisementDO db = advertisementMapper.selectById(insertTwoId);
assertNull(db);
}
@Test
@Order(21)
void testRepublish_notExist() {
AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO();
reqVO.setId(null);
assertServiceException(() -> advertisementService.republish(reqVO), ADVERTISEMENT_NO_EXIST);
}
@Test
@Order(22)
void testRepublish_time_error() {
AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO();
reqVO.setId(insertedOne.getId());
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().minusDays(1));
assertServiceException(() -> advertisementService.republish(reqVO), ADVERTISEMENT_TIME_ERROR);
}
@Test
@Order(23)
void testRepublish_success() {
AdvertisementRePublishReqVO reqVO = new AdvertisementRePublishReqVO();
reqVO.setId(insertedOne.getId());
reqVO.setStartTime(LocalDate.now());
reqVO.setEndTime(LocalDate.now().plusDays(1));
advertisementService.republish(reqVO);
assertEquals(AdvertisementStatusEnum.PUBLISHED.getStatus(), advertisementMapper.selectById(insertedOne.getId()).getStatus());
}
@Test
void testUploadAdImage_success() throws IOException {
// 模拟文件
MultipartFile file = mock(MultipartFile.class);
when(file.isEmpty()).thenReturn(false);
when(file.getBytes()).thenReturn("test".getBytes());
// 模拟 fileApi 返回路径
when(fileApi.createFile(any(byte[].class))).thenReturn("/path/to/file.jpg");
String result = advertisementService.uploadAdImage(file);
assertEquals("/path/to/file.jpg", result);
verify(fileApi, times(1)).createFile(any(byte[].class));
}
@Test
void testUploadAdImage_emptyFile() {
MultipartFile file = mock(MultipartFile.class);
when(file.isEmpty()).thenReturn(true);
assertServiceException(() -> advertisementService.uploadAdImage(file), ADVERTISEMENT_IMAGE_EMPTY_ERROR);
verify(fileApi, never()).createFile(any(byte[].class));
}
@Test
void testUploadAdImage_ioException() throws IOException {
MultipartFile file = mock(MultipartFile.class);
// 模拟非空文件
when(file.isEmpty()).thenReturn(false);
// 模拟 getBytes() 抛出 IOException
when(file.getBytes()).thenThrow(new IOException("io error"));
assertServiceException(() -> advertisementService.uploadAdImage(file), ADVERTISEMENT_IMAGE_UPLOAD_FAIL);
verify(fileApi, never()).createFile(any(byte[].class));
}
}
@@ -731,3 +731,22 @@ CREATE TABLE IF NOT EXISTS "gift_money_details" (
"deleted" BIT NOT NULL DEFAULT FALSE COMMENT '是否删除:0否 1是',
PRIMARY KEY ("id")
) COMMENT '赠送金规则明细表(base表)';
CREATE TABLE IF NOT EXISTS "advertisement" (
"id" BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY COMMENT '主键,根据ID号规则生成',
"ad_name" VARCHAR(32) NOT NULL COMMENT '广告名称',
"software_id" VARCHAR(32) NOT NULL DEFAULT '1' COMMENT '软件id,不同软件的标识',
"ad_position" VARCHAR(64) NOT NULL COMMENT '广告位置,0首页工作台轮播 1系统管理余额查询轮播 2基础配置资金查询轮播(多选)',
"ad_image_path" VARCHAR(255) NOT NULL COMMENT '广告图片路径',
"start_time" DATE NOT NULL COMMENT '投放开始时间',
"end_time" DATE NOT NULL COMMENT '投放结束时间',
"ad_redirect_url" VARCHAR(255) DEFAULT NULL COMMENT '广告跳转链接',
"sort" SMALLINT DEFAULT 0 COMMENT '顺序',
"status" TINYINT DEFAULT 0 NOT NULL COMMENT '广告状态,0未发布 1已发布 2已结束',
"creator" VARCHAR(64) DEFAULT NULL COMMENT '创建者/操作人',
"create_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '创建时间/操作时间',
"updater" VARCHAR(64) DEFAULT NULL COMMENT '更新者',
"update_time" TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL COMMENT '更新时间',
"deleted" BIT DEFAULT FALSE NOT NULL COMMENT '是否删除,0否1是',
PRIMARY KEY ("id")
) COMMENT '广告表 advertisement';