|
|
@@ -1,217 +0,0 @@
|
|
|
-import com.alibaba.fastjson.JSONObject;
|
|
|
-import com.tzld.videoVector.api.AigcApiService;
|
|
|
-import com.tzld.videoVector.dao.mapper.pgVector.DeconstructVectorConfigMapper;
|
|
|
-import com.tzld.videoVector.dao.mapper.pgVector.ext.ArticleDeconstructResultMapperExt;
|
|
|
-import com.tzld.videoVector.job.ArticleVectorJob;
|
|
|
-import com.tzld.videoVector.model.po.pgVector.ArticleDeconstructResult;
|
|
|
-import com.tzld.videoVector.model.po.pgVector.DeconstructVectorConfig;
|
|
|
-import com.tzld.videoVector.service.ArticleVectorStoreService;
|
|
|
-import com.tzld.videoVector.service.EmbeddingService;
|
|
|
-import com.xxl.job.core.biz.model.ReturnT;
|
|
|
-import lombok.extern.slf4j.Slf4j;
|
|
|
-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;
|
|
|
-
|
|
|
-import java.util.Arrays;
|
|
|
-import java.util.Collections;
|
|
|
-import java.util.List;
|
|
|
-
|
|
|
-import static org.junit.jupiter.api.Assertions.assertEquals;
|
|
|
-import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
-import static org.mockito.ArgumentMatchers.any;
|
|
|
-import static org.mockito.ArgumentMatchers.anyCollection;
|
|
|
-import static org.mockito.ArgumentMatchers.anyInt;
|
|
|
-import static org.mockito.ArgumentMatchers.anyString;
|
|
|
-import static org.mockito.ArgumentMatchers.eq;
|
|
|
-import static org.mockito.Mockito.never;
|
|
|
-import static org.mockito.Mockito.times;
|
|
|
-import static org.mockito.Mockito.verify;
|
|
|
-import static org.mockito.Mockito.when;
|
|
|
-
|
|
|
-/**
|
|
|
- * ArticleVectorJob 单元测试(对称 MaterialVectorJobTest)
|
|
|
- * Mock 外部依赖,不连 DB / AIGC / DashScope
|
|
|
- */
|
|
|
-@ExtendWith(MockitoExtension.class)
|
|
|
-@Slf4j
|
|
|
-public class ArticleVectorJobTest {
|
|
|
-
|
|
|
- private static final String ARTICLE_ID = "10086";
|
|
|
- private static final long TASK_INSTANCE_ID = 9002L;
|
|
|
- private static final int TASK_ID = 66;
|
|
|
-
|
|
|
- @Mock
|
|
|
- private DeconstructVectorConfigMapper vectorConfigMapper;
|
|
|
-
|
|
|
- @Mock
|
|
|
- private ArticleDeconstructResultMapperExt articleDeconstructResultMapperExt;
|
|
|
-
|
|
|
- @Mock
|
|
|
- private ArticleVectorStoreService articleVectorStoreService;
|
|
|
-
|
|
|
- @Mock
|
|
|
- private EmbeddingService embeddingService;
|
|
|
-
|
|
|
- @Mock
|
|
|
- private AigcApiService aigcApiService;
|
|
|
-
|
|
|
- @InjectMocks
|
|
|
- private ArticleVectorJob articleVectorJob;
|
|
|
-
|
|
|
- @BeforeEach
|
|
|
- void setUp() {
|
|
|
- ReflectionTestUtils.setField(articleVectorJob, "articleTaskId", TASK_ID);
|
|
|
- }
|
|
|
-
|
|
|
- // ==============================
|
|
|
- // syncArticleDeconstructJob
|
|
|
- // ==============================
|
|
|
-
|
|
|
- @Test
|
|
|
- void syncArticleDeconstructJob_skipsWhenNoData() {
|
|
|
- when(aigcApiService.getTaskInputList(TASK_ID)).thenReturn(Collections.emptyList());
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.syncArticleDeconstructJob(null);
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(articleDeconstructResultMapperExt, never()).batchInsertIgnore(any());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void syncArticleDeconstructJob_insertsArticle() {
|
|
|
- AigcApiService.AigcTaskInput input = new AigcApiService.AigcTaskInput();
|
|
|
- input.setBizUniqueId(ARTICLE_ID);
|
|
|
- input.setTaskInstanceId(TASK_INSTANCE_ID);
|
|
|
- when(aigcApiService.getTaskInputList(TASK_ID)).thenReturn(Collections.singletonList(input));
|
|
|
- when(articleDeconstructResultMapperExt.selectExistingArticleIds(eq("aigc_deconstruct"), any()))
|
|
|
- .thenReturn(Collections.emptyList());
|
|
|
- JSONObject dataContent = new JSONObject();
|
|
|
- dataContent.put("title", "测试文章标题");
|
|
|
- dataContent.put("content", "测试文章正文内容");
|
|
|
- when(aigcApiService.getTaskCallbackDetail(TASK_INSTANCE_ID)).thenReturn(dataContent);
|
|
|
- when(articleDeconstructResultMapperExt.batchInsertIgnore(any())).thenReturn(1);
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.syncArticleDeconstructJob(null);
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
-
|
|
|
- @SuppressWarnings("unchecked")
|
|
|
- ArgumentCaptor<List<ArticleDeconstructResult>> captor = ArgumentCaptor.forClass(List.class);
|
|
|
- verify(articleDeconstructResultMapperExt).batchInsertIgnore(captor.capture());
|
|
|
- ArticleDeconstructResult saved = captor.getValue().get(0);
|
|
|
- assertEquals(ARTICLE_ID, saved.getArticleId());
|
|
|
- assertEquals("aigc_deconstruct", saved.getSource());
|
|
|
- assertTrue(saved.getResult().contains("测试文章标题"));
|
|
|
- assertTrue(saved.getResult().contains("测试文章正文内容"));
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void syncArticleDeconstructJob_skipsExistingArticle() {
|
|
|
- AigcApiService.AigcTaskInput input = new AigcApiService.AigcTaskInput();
|
|
|
- input.setBizUniqueId(ARTICLE_ID);
|
|
|
- input.setTaskInstanceId(TASK_INSTANCE_ID);
|
|
|
- when(aigcApiService.getTaskInputList(TASK_ID)).thenReturn(Collections.singletonList(input));
|
|
|
- when(articleDeconstructResultMapperExt.selectExistingArticleIds(eq("aigc_deconstruct"), any()))
|
|
|
- .thenReturn(Collections.singletonList(ARTICLE_ID));
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.syncArticleDeconstructJob(null);
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(aigcApiService, never()).getTaskCallbackDetail(anyInt());
|
|
|
- verify(articleDeconstructResultMapperExt, never()).batchInsertIgnore(any());
|
|
|
- }
|
|
|
-
|
|
|
- // ==============================
|
|
|
- // vectorArticleJob
|
|
|
- // ==============================
|
|
|
-
|
|
|
- @Test
|
|
|
- void vectorArticleJob_returnsSuccessWhenNoConfig() {
|
|
|
- when(vectorConfigMapper.selectByExample(any())).thenReturn(Collections.emptyList());
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.vectorArticleJob("5");
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(articleDeconstructResultMapperExt, never()).selectArticleIdsBySourcePaged(anyString(), anyInt(), anyInt());
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void vectorArticleJob_embedsAndStoresArticleVector() {
|
|
|
- DeconstructVectorConfig config = buildTopicConfig();
|
|
|
- when(vectorConfigMapper.selectByExample(any())).thenReturn(Collections.singletonList(config));
|
|
|
- when(articleDeconstructResultMapperExt.selectArticleIdsBySourcePaged(eq("aigc_deconstruct"), eq(0), anyInt()))
|
|
|
- .thenReturn(Collections.singletonList(ARTICLE_ID));
|
|
|
- when(articleVectorStoreService.existsByIds(eq("VIDEO_TOPIC"), anyCollection()))
|
|
|
- .thenReturn(Collections.emptySet());
|
|
|
-
|
|
|
- ArticleDeconstructResult deconstructResult = new ArticleDeconstructResult();
|
|
|
- deconstructResult.setArticleId(ARTICLE_ID);
|
|
|
- JSONObject dataContent = new JSONObject();
|
|
|
- dataContent.put("topic", "AI技术发展趋势");
|
|
|
- dataContent.put("title", "AI技术发展趋势深度分析");
|
|
|
- deconstructResult.setResult(dataContent.toJSONString());
|
|
|
- when(articleDeconstructResultMapperExt.selectResultsByArticleIds(eq("aigc_deconstruct"), any()))
|
|
|
- .thenReturn(Collections.singletonList(deconstructResult));
|
|
|
-
|
|
|
- List<Float> vector = Arrays.asList(0.1f, 0.2f, 0.3f, 0.4f);
|
|
|
- when(articleVectorStoreService.getVectorByTextHash(anyString(), eq("VIDEO_TOPIC"))).thenReturn(null);
|
|
|
- when(embeddingService.embed(anyString(), eq(config))).thenReturn(vector);
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.vectorArticleJob("1");
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(articleVectorStoreService).save(
|
|
|
- eq("VIDEO_TOPIC"),
|
|
|
- eq(ARTICLE_ID),
|
|
|
- eq(vector),
|
|
|
- eq("AI技术发展趋势")
|
|
|
- );
|
|
|
- }
|
|
|
-
|
|
|
- @Test
|
|
|
- void vectorArticleJob_skipsWhenArticleAlreadyVectorized() {
|
|
|
- DeconstructVectorConfig config = buildTopicConfig();
|
|
|
- when(vectorConfigMapper.selectByExample(any())).thenReturn(Collections.singletonList(config));
|
|
|
- when(articleDeconstructResultMapperExt.selectArticleIdsBySourcePaged(eq("aigc_deconstruct"), eq(0), anyInt()))
|
|
|
- .thenReturn(Collections.singletonList(ARTICLE_ID));
|
|
|
- when(articleVectorStoreService.existsByIds(eq("VIDEO_TOPIC"), anyCollection()))
|
|
|
- .thenReturn(Collections.singleton(ARTICLE_ID));
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.vectorArticleJob("1");
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(articleDeconstructResultMapperExt, never()).selectResultsByArticleIds(anyString(), any());
|
|
|
- verify(embeddingService, never()).embed(anyString(), any());
|
|
|
- }
|
|
|
-
|
|
|
- // ==============================
|
|
|
- // articleJob 编排
|
|
|
- // ==============================
|
|
|
-
|
|
|
- @Test
|
|
|
- void articleJob_runsSyncThenVectorize() {
|
|
|
- when(aigcApiService.getTaskInputList(TASK_ID)).thenReturn(Collections.emptyList());
|
|
|
- when(vectorConfigMapper.selectByExample(any())).thenReturn(Collections.emptyList());
|
|
|
-
|
|
|
- ReturnT<String> result = articleVectorJob.articleJob("3");
|
|
|
-
|
|
|
- assertEquals(ReturnT.SUCCESS_CODE, result.getCode());
|
|
|
- verify(vectorConfigMapper, times(1)).selectByExample(any());
|
|
|
- }
|
|
|
-
|
|
|
- private DeconstructVectorConfig buildTopicConfig() {
|
|
|
- DeconstructVectorConfig config = new DeconstructVectorConfig();
|
|
|
- config.setConfigCode("VIDEO_TOPIC");
|
|
|
- config.setSourceField("aigc_deconstruct");
|
|
|
- config.setSourcePath("$.topic");
|
|
|
- config.setEnabled((short) 1);
|
|
|
- config.setPriority(1);
|
|
|
- return config;
|
|
|
- }
|
|
|
-}
|