package vip.mate.wiki.service; import com.fasterxml.jackson.databind.ObjectMapper; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; import vip.mate.agent.AgentGraphBuilder; import vip.mate.llm.service.ModelConfigService; import vip.mate.wiki.WikiProperties; import vip.mate.wiki.model.WikiKnowledgeBaseEntity; import vip.mate.wiki.model.WikiRawMaterialEntity; import vip.mate.wiki.sse.WikiProgressBus; import java.util.List; import java.util.Map; import java.util.concurrent.CountDownLatch; import java.util.concurrent.TimeUnit; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyLong; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.*; /** * RFC-051 PR-1b: lazy ingest short-circuits the heavy pipeline. *
* These tests pin the lazy branch's contract so later PRs (1c preprocessor,
* structured output, etc.) can't accidentally re-enable LLM calls for a
* lazy-configured KB.
*/
class WikiProcessingServiceLazyTest {
private WikiKnowledgeBaseService kbService;
private WikiRawMaterialService rawService;
private WikiPageService pageService;
private WikiChunkService chunkService;
private WikiEmbeddingService embeddingService;
private WikiProperties properties;
private ModelConfigService modelConfigService;
private AgentGraphBuilder agentGraphBuilder;
private WikiProgressBus progressBus;
private WikiCitationService citationService;
private WikiProcessingService service;
private static final Long KB_ID = 42L;
private static final Long RAW_ID = 4479907L;
@BeforeEach
void setUp() {
kbService = mock(WikiKnowledgeBaseService.class);
rawService = mock(WikiRawMaterialService.class);
pageService = mock(WikiPageService.class);
chunkService = mock(WikiChunkService.class);
embeddingService = mock(WikiEmbeddingService.class);
properties = new WikiProperties();
modelConfigService = mock(ModelConfigService.class);
agentGraphBuilder = mock(AgentGraphBuilder.class);
progressBus = mock(WikiProgressBus.class);
citationService = mock(WikiCitationService.class);
service = new WikiProcessingService(
kbService, rawService, pageService, chunkService, embeddingService,
properties, modelConfigService, agentGraphBuilder, new ObjectMapper(),
progressBus, citationService,
mock(org.springframework.context.ApplicationEventPublisher.class));
}
private WikiRawMaterialEntity raw() {
WikiRawMaterialEntity r = new WikiRawMaterialEntity();
r.setId(RAW_ID);
r.setKbId(KB_ID);
r.setContentHash("abc123");
r.setProcessingStatus("pending");
return r;
}
private WikiKnowledgeBaseEntity kb(String ingestMode) {
WikiKnowledgeBaseEntity k = new WikiKnowledgeBaseEntity();
k.setId(KB_ID);
if (ingestMode != null) {
k.setConfigContent("{\"ingestMode\":\"" + ingestMode + "\"}");
}
return k;
}
@Test
@DisplayName("lazy mode: skips LLM pipeline, persists chunks, marks completed with 0 pages")
void lazyMode_noLlmCalls() throws InterruptedException {
WikiRawMaterialEntity rawEntity = raw();
WikiKnowledgeBaseEntity kbEntity = kb("lazy");
when(rawService.claimForProcessing(RAW_ID)).thenReturn(true);
when(rawService.getById(RAW_ID)).thenReturn(rawEntity);
when(rawService.getTextContent(rawEntity)).thenReturn("Some document text for lazy ingest. ".repeat(20));
when(kbService.getById(KB_ID)).thenReturn(kbEntity);
when(pageService.countByKbId(KB_ID)).thenReturn(0);
// Latch so we can deterministically wait for the async embedding submission.
CountDownLatch embedCalled = new CountDownLatch(1);
when(embeddingService.embedMissingChunks(KB_ID)).thenAnswer(inv -> {
embedCalled.countDown();
return 0;
});
service.processRawMaterial(RAW_ID);
// Chunk persistence happened via the legacy (no-metadata) overload.
verify(chunkService, times(1)).persistChunks(eq(KB_ID), eq(RAW_ID), anyList(), anyList());
// No LLM-backed APIs touched.
verify(pageService, never()).deleteExclusiveBySourceRawId(anyLong(), anyLong());
// Async embedding fired.
assertTrue(embedCalled.await(5, TimeUnit.SECONDS), "embedMissingChunks should have been invoked");
// Raw marked completed with lastProcessedHash recorded.
verify(rawService).updateProcessingStatus(eq(RAW_ID), eq("completed"), eq(null));
verify(rawService).setLastProcessedHash(eq(RAW_ID), eq("abc123"));
// Terminal progress = done.
ArgumentCaptor