package jnpf.dmsBusinessOccupancy; import com.baomidou.dynamic.datasource.tx.TransactionContext; import org.apache.ibatis.mapping.Environment; import org.apache.ibatis.session.Configuration; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import org.junit.jupiter.api.Test; import org.mybatis.spring.SqlSessionTemplate; import org.mybatis.spring.transaction.SpringManagedTransactionFactory; import org.springframework.transaction.support.TransactionSynchronizationManager; import javax.sql.DataSource; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; import static jnpf.dmsBusinessOccupancy.BusinessOccupancyException.Reason.*; class BusinessFileOccupancyCoordinatorTest { private BusinessFileOccupancyCoordinator service(DataSource source) { var config = new Configuration(new Environment("unit", new SpringManagedTransactionFactory(), source)); return new BusinessFileOccupancyCoordinator(new SqlSessionTemplate(new SqlSessionFactoryBuilder().build(config))); } @Test void refusesBatchExecutorBecauseOccupancyRequiresImmediateWriteResults() { DataSource source = mock(DataSource.class); var config = new Configuration(new Environment("unit", new SpringManagedTransactionFactory(), source)); var template = new SqlSessionTemplate(new SqlSessionFactoryBuilder().build(config), org.apache.ibatis.session.ExecutorType.BATCH); var error = assertThrows(BusinessOccupancyException.class, () -> new BusinessFileOccupancyCoordinator(template)); assertEquals(UNSUPPORTED_TRANSACTION, error.getReason()); verifyNoInteractions(source); } @Test void refusesExpiryWithoutTransactionBeforeAnyDatabaseAccess() { DataSource source = mock(DataSource.class); var service = service(source); var error = assertThrows(BusinessOccupancyException.class, () -> service.tryEnterExpiry("tenant", List.of("A"))); assertEquals(TRANSACTION_REQUIRED, error.getReason()); verifyNoInteractions(source); } @Test void readOnlyTransactionCannotBeMistakenForAdmissionTransaction() { DataSource source = mock(DataSource.class); var service = service(source); TransactionSynchronizationManager.setActualTransactionActive(true); TransactionSynchronizationManager.setCurrentTransactionReadOnly(true); try { var error = assertThrows(BusinessOccupancyException.class, () -> service.tryEnterExpiry("tenant", List.of("A"))); assertEquals(READ_ONLY_TRANSACTION, error.getReason()); verifyNoInteractions(source); } finally { TransactionSynchronizationManager.clear(); } } @Test void rejectsUnboundSourceBeforeObtainingAnyConnection() { DataSource source = mock(DataSource.class); var service = service(source); TransactionSynchronizationManager.setActualTransactionActive(true); try { var error = assertThrows(BusinessOccupancyException.class, () -> service.tryEnterExpiry("tenant", List.of("A"))); assertEquals(TRANSACTION_CONNECTION_MISMATCH, error.getReason()); verifyNoInteractions(source); } finally { TransactionSynchronizationManager.clear(); } } @Test void refusesMixedTransactionManagersBeforeDatabaseAccess() { DataSource source = mock(DataSource.class); var service = service(source); TransactionSynchronizationManager.setActualTransactionActive(true); TransactionContext.bind("mixed"); try { var error = assertThrows(BusinessOccupancyException.class, () -> service.tryEnterExpiry("tenant", List.of("A"))); assertEquals(UNSUPPORTED_TRANSACTION, error.getReason()); verifyNoInteractions(source); } finally { TransactionContext.remove(); TransactionSynchronizationManager.clear(); } } }