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;
}
}