package jnpf.bizcommon.onlyoffice.service.impl; import com.baomidou.mybatisplus.core.MybatisConfiguration; import com.baomidou.mybatisplus.core.conditions.Wrapper; import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper; import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; import jnpf.bizcommon.onlyoffice.entity.OnlyOfficeSessionEntity; import jnpf.bizcommon.onlyoffice.mapper.OnlyOfficeSessionMapper; import jnpf.exception.DataException; import org.apache.ibatis.builder.MapperBuilderAssistant; import org.junit.Before; import org.junit.Test; import org.springframework.dao.DuplicateKeyException; import java.util.concurrent.atomic.AtomicReference; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.isNull; 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 OnlyOfficeTemplatePublishFenceServiceTest { private OnlyOfficeSessionMapper mapper; private OnlyOfficeTemplatePublishFenceService service; @Before public void setUp() { TableInfoHelper.initTableInfo(new MapperBuilderAssistant(new MybatisConfiguration(), ""), OnlyOfficeSessionEntity.class); mapper = mock(OnlyOfficeSessionMapper.class); service = new OnlyOfficeTemplatePublishFenceService(mapper); } @Test public void idleSessionCanBeAtomicallyClaimedForPublishing() { OnlyOfficeSessionEntity existing = session(OnlyOfficeSessionEntity.SessionStatus.IDLE); when(mapper.selectOne(any())).thenReturn(existing); AtomicReference> update = new AtomicReference<>(); when(mapper.update(isNull(), any())).thenAnswer(invocation -> { update.set(invocation.getArgument(1)); return 1; }); String token = service.acquire("tenant-1", "template-1", "template.docx", "annex"); assertNotNull(token); assertFalse(token.trim().isEmpty()); update.get().getSqlSegment(); assertTrue(update.get().getSqlSet().contains("session_status")); assertTrue(update.get().getSqlSet().contains("publish_token")); assertTrue(update.get().getParamNameValuePairs().containsValue( OnlyOfficeSessionEntity.SessionStatus.IDLE)); assertTrue(update.get().getParamNameValuePairs().containsValue(token)); } @Test public void missingSessionIsInsertedDirectlyAsPublishing() { when(mapper.selectOne(any())).thenReturn(null); AtomicReference inserted = new AtomicReference<>(); when(mapper.insert(any(OnlyOfficeSessionEntity.class))).thenAnswer(invocation -> { inserted.set(invocation.getArgument(0)); return 1; }); String token = service.acquire("tenant-1", "template-1", "template.docx", "annex"); assertEquals("tenant-1", inserted.get().getTenantId()); assertEquals("template-1", inserted.get().getBizDataId()); assertEquals("template.docx", inserted.get().getFileId()); assertEquals("annex", inserted.get().getFileType()); assertEquals(Integer.valueOf(OnlyOfficeSessionEntity.SessionStatus.PUBLISHING), inserted.get().getSessionStatus()); assertEquals(token, inserted.get().getPublishToken()); assertNull(inserted.get().getLockUserId()); } @Test public void editingSavingFailedAndPublishingSessionsRejectPublishFence() { int[] blocked = { OnlyOfficeSessionEntity.SessionStatus.EDITING, OnlyOfficeSessionEntity.SessionStatus.SAVING, OnlyOfficeSessionEntity.SessionStatus.SAVE_FAILED, OnlyOfficeSessionEntity.SessionStatus.PUBLISHING }; for (int status : blocked) { when(mapper.selectOne(any())).thenReturn(session(status)); expectDataException(() -> service.acquire( "tenant-1", "template-1", "template.docx", "annex")); } verify(mapper, never()).update(isNull(), any()); } @Test public void concurrentInsertConflictIsRereadAndRejectedWithoutSecondRow() { when(mapper.selectOne(any())).thenReturn(null, session(OnlyOfficeSessionEntity.SessionStatus.EDITING)); when(mapper.insert(any(OnlyOfficeSessionEntity.class))) .thenThrow(new DuplicateKeyException("duplicate file")); expectDataException(() -> service.acquire( "tenant-1", "template-1", "template.docx", "annex")); } @Test public void releaseRequiresExactPublishingToken() { AtomicReference> release = new AtomicReference<>(); when(mapper.update(isNull(), any())).thenAnswer(invocation -> { release.set(invocation.getArgument(1)); return 1; }); assertTrue(service.release("tenant-1", "template-1", "template.docx", "annex", "token-1")); release.get().getSqlSegment(); assertTrue(release.get().getParamNameValuePairs().containsValue("token-1")); assertTrue(release.get().getParamNameValuePairs().containsValue( OnlyOfficeSessionEntity.SessionStatus.PUBLISHING)); assertTrue(release.get().getSqlSet().contains("session_status")); assertTrue(release.get().getSqlSet().contains("publish_token")); when(mapper.update(isNull(), any(Wrapper.class))).thenReturn(0); assertFalse(service.release( "tenant-1", "template-1", "template.docx", "annex", "old-token")); } private static OnlyOfficeSessionEntity session(int status) { OnlyOfficeSessionEntity session = new OnlyOfficeSessionEntity(); session.setId("session-1"); session.setTenantId("tenant-1"); session.setFileId("template.docx"); session.setFileType("annex"); session.setBizModule("eln.template"); session.setBizDataId("template-1"); session.setDocKey("doc-key-1"); session.setVersionNo(1); session.setVersion(3); session.setSessionStatus(status); return session; } private static DataException expectDataException(Runnable action) { try { action.run(); fail("预期抛出 DataException"); return null; } catch (DataException expected) { assertFalse(expected.getMessage().trim().isEmpty()); return expected; } } }