package jnpf.dmsService.search; import jnpf.dmsEntity.permission.vo.DmsPermissionDecisionVO; import jnpf.dmsEntity.search.form.DmsFullTextSearchForm; import jnpf.dmsEntity.search.vo.DmsFullTextSearchPageVO; import jnpf.dmsPermission.DmsCurrentIdentity; import jnpf.dmsService.DmsPermissionDecisionService; import jnpf.dmsService.search.meili.DmsMeilisearchClient; import jnpf.dmsService.search.meili.DmsMeilisearchSearchHit; import jnpf.dmsService.search.meili.DmsMeilisearchSearchPage; import jnpf.dmsService.search.meili.DmsSearchIndexDocument; import org.junit.jupiter.api.Test; import java.util.Arrays; import java.util.UUID; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyList; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; class DmsFullTextSearchServiceTest { private final jnpf.dmsMapper.DmsSearchIndexSourceMapper sources = mock(jnpf.dmsMapper.DmsSearchIndexSourceMapper.class); @org.junit.jupiter.api.BeforeEach void currentVersions() { when(sources.currentVersionIds(org.mockito.ArgumentMatchers.anyString(), anyList())) .thenAnswer(call -> call.getArgument(1)); } @Test void returnsOnlyReadableResultsAndEscapesHighlightedText() throws Exception { DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class); DmsPermissionDecisionService permissions = mock(DmsPermissionDecisionService.class); DmsMeilisearchClient meilisearch = mock(DmsMeilisearchClient.class); DmsFullTextSearchService service = new DmsFullTextSearchService( identity, permissions, meilisearch, sources); DmsMeilisearchSearchHit denied = hit("无权限文件", "正文"); DmsMeilisearchSearchHit allowed = hit("" + DmsMeilisearchClient.HIGHLIGHT_START + "规程" + DmsMeilisearchClient.HIGHLIGHT_END, "命中正文"); when(identity.tenantId()).thenReturn("tenant-1"); when(meilisearch.search("tenant-1", "规程", 0, 100)) .thenReturn(new DmsMeilisearchSearchPage(Arrays.asList(denied, allowed), 2)); when(permissions.evaluate(anyList())).thenReturn(Arrays.asList(decision(false), decision(true))); DmsFullTextSearchForm form = new DmsFullTextSearchForm(); form.setKeyword("规程"); DmsFullTextSearchPageVO result = service.search(form); assertEquals(1, result.getList().size()); assertEquals("<script>alert(1)</script>规程", result.getList().get(0).getHighlightedTitle()); assertFalse(result.isHasMore()); } @Test void returnsCursorForNextReadableResult() throws Exception { DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class); DmsPermissionDecisionService permissions = mock(DmsPermissionDecisionService.class); DmsMeilisearchClient meilisearch = mock(DmsMeilisearchClient.class); DmsFullTextSearchService service = new DmsFullTextSearchService( identity, permissions, meilisearch, sources); when(identity.tenantId()).thenReturn("tenant-1"); when(meilisearch.search("tenant-1", "文件", 0, 100)) .thenReturn(new DmsMeilisearchSearchPage( Arrays.asList(hit("文件一", ""), hit("文件二", "")), 2)); when(permissions.evaluate(anyList())) .thenReturn(Arrays.asList(decision(true), decision(true))); DmsFullTextSearchForm form = new DmsFullTextSearchForm(); form.setKeyword("文件"); form.setPageSize(1); DmsFullTextSearchPageVO result = service.search(form); assertEquals(1, result.getList().size()); assertTrue(result.isHasMore()); assertEquals("1", result.getNextCursor()); } private DmsMeilisearchSearchHit hit(String formattedTitle, String formattedContent) { DmsSearchIndexDocument document = new DmsSearchIndexDocument(); UUID versionId = UUID.randomUUID(); document.setId(versionId.toString()); document.setTenantId("tenant-1"); document.setDocumentId(UUID.randomUUID().toString()); document.setFileVersionId(versionId.toString()); document.setTitle("文件"); document.setFileName("file.docx"); return new DmsMeilisearchSearchHit(document, formattedTitle, formattedContent); } @Test void suppressesExpiredIndexHitEvenWhenHistoricalReadIsAllowed() throws Exception { DmsCurrentIdentity identity = mock(DmsCurrentIdentity.class); DmsPermissionDecisionService permissions = mock(DmsPermissionDecisionService.class); DmsMeilisearchClient client = mock(DmsMeilisearchClient.class); when(identity.tenantId()).thenReturn("tenant-1"); when(client.search("tenant-1", "文件", 0, 100)).thenReturn( new DmsMeilisearchSearchPage(java.util.List.of(hit("已过期", "历史正文")), 1)); when(permissions.evaluate(anyList())).thenReturn(java.util.List.of(decision(true))); when(sources.currentVersionIds(org.mockito.ArgumentMatchers.anyString(), anyList())).thenReturn(java.util.List.of()); DmsFullTextSearchForm form = new DmsFullTextSearchForm(); form.setKeyword("文件"); assertTrue(new DmsFullTextSearchService(identity, permissions, client, sources).search(form).getList().isEmpty()); } private DmsPermissionDecisionVO decision(boolean allowed) { DmsPermissionDecisionVO decision = new DmsPermissionDecisionVO(); decision.setAllowed(allowed); return decision; } }