package jnpf.bizcommon.onlyoffice.service.impl; import com.sun.net.httpserver.HttpServer; import jnpf.base.UserInfo; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeConfigParam; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeCallbackParam; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeProperties; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeSessionEntity; import jnpf.bizcommon.onlyoffice.mapper.OnlyOfficeSessionMapper; import jnpf.exception.DataException; import jnpf.file.FileApi; import jnpf.file.FileUploadApi; import org.junit.After; import org.junit.Before; import org.junit.Test; import java.net.InetSocketAddress; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Collections; import java.util.List; import java.util.Map; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; public class OnlyOfficeServiceImplTest { private HttpServer server; private OnlyOfficeSessionMapper sessionMapper; private FileApi fileApi; private FileUploadApi fileUploadApi; private OnlyOfficeServiceImpl service; @Before public void setUp() throws Exception { server = HttpServer.create(new InetSocketAddress(0), 0); server.createContext("/saved.docx", exchange -> { byte[] body = "saved-document".getBytes(); exchange.sendResponseHeaders(200, body.length); exchange.getResponseBody().write(body); exchange.close(); }); server.start(); sessionMapper = mock(OnlyOfficeSessionMapper.class); fileApi = mock(FileApi.class); fileUploadApi = mock(FileUploadApi.class); when(fileApi.getPath("annex")).thenReturn("WebAnnexFile"); OnlyOfficeProperties properties = new OnlyOfficeProperties(); properties.setJwtSecret("test-secret"); service = new OnlyOfficeServiceImpl(properties, sessionMapper, mock(OnlyOfficePluginResolver.class), mock(OnlyOfficeFillTicketIssuer.class), fileApi, fileUploadApi); } @After public void tearDown() { server.stop(0); } @Test public void saveBackReplacesOriginalFile() { OnlyOfficeSessionEntity session = session(); when(fileUploadApi.replaceFile(any(), eq("WebAnnexFile/"), eq("template.docx"))).thenReturn(true); invokeSaveBack(session, callback(), true); verify(fileUploadApi).replaceFile(any(), eq("WebAnnexFile/"), eq("template.docx")); verify(fileUploadApi, never()).uploadFile(any(), any(), any()); assertEquals(Integer.valueOf(2), session.getVersionNo()); } @Test public void failedReplacementDoesNotAdvanceSession() { OnlyOfficeSessionEntity session = session(); when(fileUploadApi.replaceFile(any(), eq("WebAnnexFile/"), eq("template.docx"))).thenReturn(false); try { invokeSaveBack(session, callback(), true); fail("expected replacement failure"); } catch (DataException e) { assertEquals("文件服务写回文档失败", e.getMessage()); } assertEquals(Integer.valueOf(1), session.getVersionNo()); assertNull(session.getLastSaveTime()); verify(sessionMapper, never()).updateById(any(OnlyOfficeSessionEntity.class)); } @Test public void viewModeHidesCollaborationMenu() { OnlyOfficeConfigParam param = new OnlyOfficeConfigParam(); param.setFileId("template.docx"); param.setFileName("template.docx"); param.setBizScene("ordinary.view"); UserInfo user = new UserInfo(); user.setUserId("user-1"); user.setUserName("Tester"); Map config = invokeAssembleConfig(param, session(), user); Map document = castMap(config.get("document")); Map permissions = castMap(document.get("permissions")); Map editorConfig = castMap(config.get("editorConfig")); Map customization = castMap(editorConfig.get("customization")); assertEquals(false, permissions.get("comment")); assertEquals(false, permissions.get("review")); assertEquals(false, customization.get("comments")); } private OnlyOfficeSessionEntity session() { OnlyOfficeSessionEntity session = new OnlyOfficeSessionEntity(); session.setFileId("template.docx"); session.setFileType("annex"); session.setVersionNo(1); session.setDocKey("key-1"); return session; } private OnlyOfficeCallbackParam callback() { OnlyOfficeCallbackParam callback = new OnlyOfficeCallbackParam(); callback.setStatus(OnlyOfficeCallbackParam.Status.READY_FOR_SAVING); callback.setUrl("http://127.0.0.1:" + server.getAddress().getPort() + "/saved.docx"); return callback; } private void invokeSaveBack(OnlyOfficeSessionEntity session, OnlyOfficeCallbackParam callback, boolean rotateKey) { try { Method method = OnlyOfficeServiceImpl.class.getDeclaredMethod( "saveBack", OnlyOfficeSessionEntity.class, OnlyOfficeCallbackParam.class, boolean.class); method.setAccessible(true); method.invoke(service, session, callback, rotateKey); } catch (InvocationTargetException e) { if (e.getCause() instanceof RuntimeException) { throw (RuntimeException) e.getCause(); } throw new RuntimeException(e.getCause()); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private Map invokeAssembleConfig(OnlyOfficeConfigParam param, OnlyOfficeSessionEntity session, UserInfo user) { try { Method method = OnlyOfficeServiceImpl.class.getDeclaredMethod("assembleConfig", OnlyOfficeConfigParam.class, OnlyOfficeSessionEntity.class, String.class, String.class, String.class, UserInfo.class, List.class); method.setAccessible(true); return (Map) method.invoke(service, param, session, "word", "docx", "view", user, Collections.emptyList()); } catch (ReflectiveOperationException e) { throw new RuntimeException(e); } } @SuppressWarnings("unchecked") private Map castMap(Object value) { return (Map) value; } }