mateclaw/mateclaw-server/src/test/java/vip/mate/wiki/service/WikiRawMaterialDedupTest.java
matevip 0ffc224623 chore(test): backfill mateclaw-server/src/test/ that earlier PRIVATE_ITEMS pattern accidentally excluded
The PRIVATE_ITEMS list contained the bare 'test' entry, which rsync
interprets as 'any directory named test at any depth' — so it caught
the root-level /test/ scratch directory (intended) AND every src/test/
under each module (not intended).

Pattern is already anchored to /test (root-only). This commit rsyncs
the accumulated src/test/ tree forward so opensource has the unit tests
that have been written / updated against existing src/main/ code since
the pattern regression. Going forward each per-commit sync will carry
src/test/ files along with the main change.
2026-05-12 17:38:08 +08:00

145 lines
6.0 KiB
Java

package vip.mate.wiki.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.params.ParameterizedTest;
import org.junit.jupiter.params.provider.ValueSource;
import org.springframework.context.ApplicationEventPublisher;
import vip.mate.system.featureflag.FeatureFlagService;
import vip.mate.tool.builtin.DocumentExtractTool;
import vip.mate.tool.image.vision.ImageVisionService;
import vip.mate.wiki.WikiProperties;
import vip.mate.wiki.model.WikiRawMaterialEntity;
import vip.mate.wiki.repository.WikiRawMaterialMapper;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*;
/**
* Verifies that addText / addFile deduplication works across all processing
* statuses, not just 'completed'. A duplicate upload should always reuse
* the existing row — never insert a second one.
*/
class WikiRawMaterialDedupTest {
private WikiRawMaterialMapper rawMapper;
private WikiKnowledgeBaseService kbService;
private ApplicationEventPublisher eventPublisher;
private WikiChunkService chunkService;
private WikiRawMaterialService service;
private static final Long KB_ID = 1L;
@BeforeEach
void setUp() {
rawMapper = mock(WikiRawMaterialMapper.class);
kbService = mock(WikiKnowledgeBaseService.class);
eventPublisher = mock(ApplicationEventPublisher.class);
chunkService = mock(WikiChunkService.class);
WikiProperties props = new WikiProperties();
props.setAutoProcessOnUpload(false); // don't fire events during test
DocumentExtractTool docTool = mock(DocumentExtractTool.class);
ImageVisionService visionService = mock(ImageVisionService.class);
PdfImageExtractor pdfImageExtractor = mock(PdfImageExtractor.class);
FeatureFlagService featureFlagService = mock(FeatureFlagService.class);
service = new WikiRawMaterialService(rawMapper, kbService, props, eventPublisher, docTool,
chunkService, visionService, pdfImageExtractor, featureFlagService);
}
private WikiRawMaterialEntity existingRow(Long id, String status) {
WikiRawMaterialEntity e = new WikiRawMaterialEntity();
e.setId(id);
e.setKbId(KB_ID);
e.setTitle("test.pdf");
e.setProcessingStatus(status);
e.setContentHash("abc123");
return e;
}
// ==================== addText dedup ====================
@ParameterizedTest(name = "addText dedup when existing status = {0}")
@ValueSource(strings = {"completed", "partial", "failed", "pending", "processing"})
@DisplayName("addText: same hash should reuse existing row regardless of status")
void addText_dedup_all_statuses(String status) {
WikiRawMaterialEntity existing = existingRow(42L, status);
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(existing);
// reprocess() calls selectById internally for partial/failed
when(rawMapper.selectById(42L)).thenReturn(existing);
WikiRawMaterialEntity result = service.addText(KB_ID, "test", "some content");
assertEquals(42L, result.getId(), "should return existing id");
// Should never insert a new row
verify(rawMapper, never()).insert(any(WikiRawMaterialEntity.class));
}
@Test
@DisplayName("addText: reprocess triggered for partial status")
void addText_dedup_partial_triggers_reprocess() {
WikiRawMaterialEntity existing = existingRow(42L, "partial");
// First call: dedup lookup returns existing
// Second call (inside reprocess): selectById returns existing
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(existing);
when(rawMapper.selectById(42L)).thenReturn(existing);
service.addText(KB_ID, "test", "some content");
// reprocess changes status to pending and fires event
assertEquals("pending", existing.getProcessingStatus());
verify(eventPublisher).publishEvent(any());
}
@Test
@DisplayName("addText: reprocess triggered for failed status")
void addText_dedup_failed_triggers_reprocess() {
WikiRawMaterialEntity existing = existingRow(42L, "failed");
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(existing);
when(rawMapper.selectById(42L)).thenReturn(existing);
service.addText(KB_ID, "test", "some content");
assertEquals("pending", existing.getProcessingStatus());
verify(eventPublisher).publishEvent(any());
}
@Test
@DisplayName("addText: completed status returns as-is, no reprocess")
void addText_dedup_completed_no_reprocess() {
WikiRawMaterialEntity existing = existingRow(42L, "completed");
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(existing);
WikiRawMaterialEntity result = service.addText(KB_ID, "test", "some content");
assertEquals("completed", result.getProcessingStatus());
verify(eventPublisher, never()).publishEvent(any());
}
@Test
@DisplayName("addText: pending/processing status returns as-is, no reprocess")
void addText_dedup_pending_no_reprocess() {
WikiRawMaterialEntity existing = existingRow(42L, "pending");
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(existing);
WikiRawMaterialEntity result = service.addText(KB_ID, "test", "some content");
assertEquals("pending", result.getProcessingStatus());
verify(eventPublisher, never()).publishEvent(any());
}
@Test
@DisplayName("addText: no existing row inserts new record")
void addText_no_duplicate_inserts() {
when(rawMapper.selectOne(any(LambdaQueryWrapper.class))).thenReturn(null);
service.addText(KB_ID, "test", "brand new content");
verify(rawMapper).insert(any(WikiRawMaterialEntity.class));
verify(kbService).incrementRawCount(KB_ID);
}
}