package jnpf.base.service; import com.baomidou.dynamic.datasource.tx.TransactionContext; import jnpf.base.ActionResult; import jnpf.base.UserInfo; import jnpf.exception.DataException; import jnpf.util.TicketUtil; import jnpf.util.UserProvider; import org.junit.Test; import java.util.Map; import static org.junit.Assert.*; import static org.mockito.Mockito.*; public class UploadEditNotificationsTest { @Test public void authorizedWatchOnlyWakesAfterCommitAndRejectsOtherUser() { var notifications = new UploadEditNotifications(); UserInfo user = new UserInfo(); user.setTenantId("tenant"); user.setUserId("owner"); try (var users = mockStatic(UserProvider.class); var tickets = mockStatic(TicketUtil.class)) { users.when(UserProvider::getUser).thenReturn(user); users.when(UserProvider::getLoginUserId).thenReturn("owner"); tickets.when(() -> TicketUtil.createTicket(anyString(), eq(60L))).thenAnswer(call -> call.getArgument(0)); tickets.when(() -> TicketUtil.parseTicket(anyString())).thenAnswer(call -> call.getArgument(0)); String ticket = notifications.ticket(Map.of("draft", 0L)); var waiting = notifications.watch(ticket); assertFalse(waiting.hasResult()); TransactionContext.bind("test-transaction"); try { notifications.changedAfterCommit("tenant", "draft"); assertFalse(waiting.hasResult()); TransactionContext.getSynchronizations().forEach(sync -> sync.afterCommit()); assertTrue(waiting.hasResult()); assertEquals(true, ((ActionResult) waiting.getResult()).getData()); } finally { TransactionContext.removeSynchronizations(); TransactionContext.remove(); } // A missed event between state read and subscription is detected immediately. assertTrue(notifications.watch(ticket).hasResult()); users.when(UserProvider::getLoginUserId).thenReturn("other"); try { notifications.watch(ticket); fail("other user must not subscribe"); } catch (DataException expected) { assertTrue(expected.getMessage().contains("失效")); } } } }