From 778ecd71474827fa6486f498fc91751cf1be91af Mon Sep 17 00:00:00 2001 From: Sergey Grigoriev Date: Wed, 18 Dec 2024 15:50:32 +0100 Subject: [PATCH] feat: extensions for unit tests Refs: #220 --- .../fields/CustomFieldEnumConverterTest.java | 31 +- .../generic/polarion/CustomExtensionMock.java | 11 + .../polarion/CustomExtensionMockInjector.java | 21 ++ .../PlatformContextMockExtension.java | 76 +++++ .../TransactionalExecutorExtension.java | 62 ++++ .../generic/service/PolarionServiceTest.java | 42 ++- .../generic/settings/SettingsServiceTest.java | 290 ++++++++---------- pom.xml | 19 ++ 8 files changed, 347 insertions(+), 205 deletions(-) create mode 100644 app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMock.java create mode 100644 app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMockInjector.java create mode 100644 app/src/test/java/ch/sbb/polarion/extension/generic/polarion/PlatformContextMockExtension.java create mode 100644 app/src/test/java/ch/sbb/polarion/extension/generic/polarion/TransactionalExecutorExtension.java diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/fields/CustomFieldEnumConverterTest.java b/app/src/test/java/ch/sbb/polarion/extension/generic/fields/CustomFieldEnumConverterTest.java index a481560..5ae5893 100644 --- a/app/src/test/java/ch/sbb/polarion/extension/generic/fields/CustomFieldEnumConverterTest.java +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/fields/CustomFieldEnumConverterTest.java @@ -2,23 +2,19 @@ import ch.sbb.polarion.extension.generic.fields.converters.ChainConverter; import ch.sbb.polarion.extension.generic.fields.model.FieldMetadata; +import ch.sbb.polarion.extension.generic.polarion.CustomExtensionMock; +import ch.sbb.polarion.extension.generic.polarion.PlatformContextMockExtension; import com.polarion.alm.shared.util.Pair; import com.polarion.alm.tracker.ITrackerService; -import com.polarion.platform.core.IPlatform; -import com.polarion.platform.core.PlatformContext; import com.polarion.platform.persistence.IDataService; import com.polarion.platform.persistence.IEnumOption; import com.polarion.platform.persistence.IEnumeration; import com.polarion.platform.persistence.spi.EnumOption; import com.polarion.subterra.base.data.model.internal.EnumType; import com.polarion.subterra.base.data.model.internal.ListType; -import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.Answers; -import org.mockito.Mock; -import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; @@ -32,31 +28,23 @@ import static org.mockito.Mockito.lenient; import static org.mockito.Mockito.mock; -@ExtendWith(MockitoExtension.class) +@ExtendWith({MockitoExtension.class, PlatformContextMockExtension.class}) public class CustomFieldEnumConverterTest { public static final String YES_NO_ENUM_ID = "yes_no"; public static final Pair YES = Pair.of("yes", "Ja"); public static final Pair NO = Pair.of("no", "Nein"); - @Mock(answer = Answers.RETURNS_DEEP_STUBS) - MockedStatic mockPlatformContext; + @CustomExtensionMock + private IDataService dataService; + @CustomExtensionMock + private ITrackerService trackerService; private ArrayList allOptions; @BeforeEach @SuppressWarnings("unchecked") void setup() { - IPlatform platform = mock(IPlatform.class); - mockPlatformContext.when(PlatformContext::getPlatform).thenReturn(platform); - - IDataService dataService = mock(IDataService.class); - lenient().when(platform.lookupService(IDataService.class)).thenReturn(dataService); - - ITrackerService trackerService = mock(ITrackerService.class); - lenient().when(platform.lookupService(ITrackerService.class)).thenReturn(trackerService); - lenient().when(trackerService.getDataService()).thenReturn(dataService); - IEnumeration enumeration = mock(IEnumeration.class); lenient().when(dataService.getEnumerationForEnumId(any(), any())).thenReturn(enumeration); @@ -66,11 +54,6 @@ void setup() { lenient().when(enumeration.getAllOptions()).thenReturn(allOptions); } - @AfterEach - void cleanup() { - mockPlatformContext.close(); - } - @Test void testGetEnumOptionById() { FieldMetadata fieldMetadata = ConverterTestUtils.getWorkItemCustomField(); diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMock.java b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMock.java new file mode 100644 index 0000000..fece097 --- /dev/null +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMock.java @@ -0,0 +1,11 @@ +package ch.sbb.polarion.extension.generic.polarion; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +@Target(ElementType.FIELD) +@Retention(RetentionPolicy.RUNTIME) +public @interface CustomExtensionMock { +} diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMockInjector.java b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMockInjector.java new file mode 100644 index 0000000..41fe194 --- /dev/null +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/CustomExtensionMockInjector.java @@ -0,0 +1,21 @@ +package ch.sbb.polarion.extension.generic.polarion; + +import org.junit.jupiter.api.extension.ExtensionContext; + +import java.lang.reflect.Field; + +public class CustomExtensionMockInjector { + + public static void inject(ExtensionContext context, T what) throws IllegalAccessException { + Object testInstance = context.getTestInstance().orElse(null); + if (testInstance != null) { + for (Field field : testInstance.getClass().getDeclaredFields()) { + if (field.isAnnotationPresent(CustomExtensionMock.class) && field.getType().isAssignableFrom(what.getClass())) { + field.setAccessible(true); + field.set(testInstance, what); + } + } + } + } + +} diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/PlatformContextMockExtension.java b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/PlatformContextMockExtension.java new file mode 100644 index 0000000..e0f39ad --- /dev/null +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/PlatformContextMockExtension.java @@ -0,0 +1,76 @@ +package ch.sbb.polarion.extension.generic.polarion; + +import com.polarion.alm.projects.IProjectService; +import com.polarion.alm.tracker.ITestManagementService; +import com.polarion.alm.tracker.ITrackerService; +import com.polarion.platform.IPlatformService; +import com.polarion.platform.core.IPlatform; +import com.polarion.platform.core.PlatformContext; +import com.polarion.platform.persistence.IDataService; +import com.polarion.platform.security.ILoginPolicy; +import com.polarion.platform.security.ISecurityService; +import com.polarion.platform.service.repository.IRepositoryService; +import com.polarion.portal.internal.server.navigation.TestManagementServiceAccessor; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.mockito.MockedConstruction; +import org.mockito.MockedStatic; + +import static org.mockito.Mockito.*; + +public class PlatformContextMockExtension implements BeforeEachCallback, AfterEachCallback { + + private MockedConstruction testManagementServiceAccessorMockedConstruction; + private MockedStatic platformContextMockedStatic; + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + IPlatform platformMock = mock(IPlatform.class); + + ITrackerService trackerService = mock(ITrackerService.class); + IProjectService projectService = mock(IProjectService.class); + ISecurityService securityService = mock(ISecurityService.class); + IPlatformService platformService = mock(IPlatformService.class); + IRepositoryService repositoryService = mock(IRepositoryService.class); + IDataService dataService = mock(IDataService.class); + ILoginPolicy loginPolicy = mock(ILoginPolicy.class); + ITestManagementService testManagementService = mock(ITestManagementService.class); + + lenient().when(platformMock.lookupService(ITrackerService.class)).thenReturn(trackerService); + lenient().when(platformMock.lookupService(IProjectService.class)).thenReturn(projectService); + lenient().when(platformMock.lookupService(ISecurityService.class)).thenReturn(securityService); + lenient().when(platformMock.lookupService(IPlatformService.class)).thenReturn(platformService); + lenient().when(platformMock.lookupService(IRepositoryService.class)).thenReturn(repositoryService); + lenient().when(platformMock.lookupService(IDataService.class)).thenReturn(dataService); + lenient().when(platformMock.lookupService(ILoginPolicy.class)).thenReturn(loginPolicy); + + lenient().when(trackerService.getDataService()).thenReturn(dataService); + + platformContextMockedStatic = mockStatic(PlatformContext.class); + platformContextMockedStatic.when(PlatformContext::getPlatform).thenReturn(platformMock); + + CustomExtensionMockInjector.inject(context, trackerService); + CustomExtensionMockInjector.inject(context, projectService); + CustomExtensionMockInjector.inject(context, securityService); + CustomExtensionMockInjector.inject(context, platformService); + CustomExtensionMockInjector.inject(context, repositoryService); + CustomExtensionMockInjector.inject(context, dataService); + CustomExtensionMockInjector.inject(context, loginPolicy); + + testManagementServiceAccessorMockedConstruction = mockConstruction(TestManagementServiceAccessor.class, (testManagementServiceAccessor, mockedContructionContext) -> { + lenient().when(testManagementServiceAccessor.getTestingService()).thenReturn(testManagementService); + }); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + if (testManagementServiceAccessorMockedConstruction != null) { + testManagementServiceAccessorMockedConstruction.close(); + } + if (platformContextMockedStatic != null) { + platformContextMockedStatic.close(); + } + } + +} diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/TransactionalExecutorExtension.java b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/TransactionalExecutorExtension.java new file mode 100644 index 0000000..f991e83 --- /dev/null +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/polarion/TransactionalExecutorExtension.java @@ -0,0 +1,62 @@ +package ch.sbb.polarion.extension.generic.polarion; + +import com.polarion.alm.shared.api.transaction.RunnableInReadOnlyTransaction; +import com.polarion.alm.shared.api.transaction.TransactionalExecutor; +import com.polarion.alm.shared.api.transaction.internal.InternalReadOnlyTransaction; +import com.polarion.alm.shared.api.utils.RunnableWithResult; +import com.polarion.alm.shared.api.utils.internal.InternalPolarionUtils; +import org.junit.jupiter.api.extension.AfterEachCallback; +import org.junit.jupiter.api.extension.BeforeEachCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.ParameterContext; +import org.junit.jupiter.api.extension.ParameterResolutionException; +import org.junit.jupiter.api.extension.ParameterResolver; +import org.mockito.MockedStatic; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +public class TransactionalExecutorExtension implements BeforeEachCallback, AfterEachCallback, ParameterResolver { + + private MockedStatic transactionalExecutorMockedStatic; + + @Override + public void beforeEach(ExtensionContext context) throws Exception { + transactionalExecutorMockedStatic = mockStatic(TransactionalExecutor.class); + + InternalReadOnlyTransaction internalReadOnlyTransactionMock = mock(InternalReadOnlyTransaction.class); + transactionalExecutorMockedStatic.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any())).thenAnswer(invocation -> { + RunnableInReadOnlyTransaction runnable = invocation.getArgument(0); + return runnable.run(internalReadOnlyTransactionMock); + }); + transactionalExecutorMockedStatic.when(TransactionalExecutor::currentTransaction).thenReturn(internalReadOnlyTransactionMock); + + InternalPolarionUtils internalPolarionUtils = mock(InternalPolarionUtils.class); + lenient().when(internalPolarionUtils.executeInBaseline(any(), any())).thenAnswer(invocation -> { + RunnableWithResult runnableWithResult = invocation.getArgument(1); + return runnableWithResult.run(); + }); + lenient().when(internalReadOnlyTransactionMock.utils()).thenReturn(internalPolarionUtils); + + CustomExtensionMockInjector.inject(context, internalPolarionUtils); + CustomExtensionMockInjector.inject(context, internalReadOnlyTransactionMock); + } + + @Override + public void afterEach(ExtensionContext context) throws Exception { + if (transactionalExecutorMockedStatic != null) { + transactionalExecutorMockedStatic.close(); + } + } + + @Override + public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return parameterContext.getParameter().getType().equals(ExtensionContext.class); + } + + @Override + public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext) throws ParameterResolutionException { + return extensionContext; + } + +} diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/service/PolarionServiceTest.java b/app/src/test/java/ch/sbb/polarion/extension/generic/service/PolarionServiceTest.java index f46c886..61e5dea 100644 --- a/app/src/test/java/ch/sbb/polarion/extension/generic/service/PolarionServiceTest.java +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/service/PolarionServiceTest.java @@ -4,11 +4,15 @@ import ch.sbb.polarion.extension.generic.fields.FieldType; import ch.sbb.polarion.extension.generic.fields.model.FieldMetadata; import ch.sbb.polarion.extension.generic.fields.model.Option; +import ch.sbb.polarion.extension.generic.polarion.PlatformContextMockExtension; +import ch.sbb.polarion.extension.generic.polarion.TransactionalExecutorExtension; import ch.sbb.polarion.extension.generic.util.AssigneeUtils; import ch.sbb.polarion.extension.generic.util.TestUtils; import com.polarion.alm.projects.IProjectService; import com.polarion.alm.projects.model.IProject; -import com.polarion.alm.shared.api.transaction.TransactionalExecutor; +import com.polarion.alm.shared.api.model.baselinecollection.BaselineCollection; +import com.polarion.alm.shared.api.model.baselinecollection.BaselineCollectionReference; +import com.polarion.alm.shared.api.transaction.ReadOnlyTransaction; import com.polarion.alm.tracker.IModuleManager; import com.polarion.alm.tracker.ITrackerService; import com.polarion.alm.tracker.model.IModule; @@ -34,6 +38,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.Mock; +import org.mockito.MockedConstruction; import org.mockito.MockedStatic; import org.mockito.junit.jupiter.MockitoExtension; @@ -48,7 +53,7 @@ import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.Mockito.*; -@ExtendWith(MockitoExtension.class) +@ExtendWith({MockitoExtension.class, PlatformContextMockExtension.class, TransactionalExecutorExtension.class}) @SuppressWarnings({"rawtypes", "unchecked", "UnusedReturnValue"}) public class PolarionServiceTest { @@ -120,11 +125,14 @@ void testGetNotExistentModule() { @Test void testGetNotExistentCollection() { - try (MockedStatic transactionalExecutorMockedStatic = mockStatic(TransactionalExecutor.class)) { - mockProject(Boolean.TRUE); - IBaselineCollection collection = mockCollection(Boolean.FALSE); - transactionalExecutorMockedStatic.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any())).thenReturn(collection); + mockProject(Boolean.TRUE); + IBaselineCollection collection = mockCollection(Boolean.FALSE); + try (MockedConstruction baselineCollectionReferenceMockedConstruction = mockConstruction(BaselineCollectionReference.class, (mock, context) -> { + BaselineCollection baselineCollection = mock(BaselineCollection.class); + when(baselineCollection.getOldApi()).thenReturn(collection); + when(mock.get(any(ReadOnlyTransaction.class))).thenReturn(baselineCollection); + })) { ObjectNotFoundException exception = assertThrows(ObjectNotFoundException.class, () -> polarionService.getCollection(PROJECT_ID, COLLECTION_ID)); assertEquals("Collection with id '1' not found in project 'project_id'", exception.getMessage()); @@ -217,22 +225,28 @@ void testGetModuleWithRevision() { @Test void testGetCollection() { - try (MockedStatic transactionalExecutorMockedStatic = mockStatic(TransactionalExecutor.class)) { - mockProject(Boolean.TRUE); - IBaselineCollection collection = mockCollection(Boolean.TRUE); - transactionalExecutorMockedStatic.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any())).thenReturn(collection); + mockProject(Boolean.TRUE); + IBaselineCollection collection = mockCollection(Boolean.TRUE); + try (MockedConstruction baselineCollectionReferenceMockedConstruction = mockConstruction(BaselineCollectionReference.class, (mock, context) -> { + BaselineCollection baselineCollection = mock(BaselineCollection.class); + when(baselineCollection.getOldApi()).thenReturn(collection); + when(mock.get(any(ReadOnlyTransaction.class))).thenReturn(baselineCollection); + })) { assertNotNull(polarionService.getCollection(PROJECT_ID, COLLECTION_ID)); } } @Test void testGetCollectionWithRevision() { - try (MockedStatic transactionalExecutorMockedStatic = mockStatic(TransactionalExecutor.class)) { - mockProject(Boolean.TRUE); - IBaselineCollection collection = mockCollection(Boolean.TRUE); - transactionalExecutorMockedStatic.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any())).thenReturn(collection); + mockProject(Boolean.TRUE); + IBaselineCollection collection = mockCollection(Boolean.TRUE); + try (MockedConstruction baselineCollectionReferenceMockedConstruction = mockConstruction(BaselineCollectionReference.class, (mock, context) -> { + BaselineCollection baselineCollection = mock(BaselineCollection.class); + when(baselineCollection.getOldApi()).thenReturn(collection); + when(mock.get(any(ReadOnlyTransaction.class))).thenReturn(baselineCollection); + })) { assertNotNull(polarionService.getCollection(PROJECT_ID, COLLECTION_ID, REVISION)); IllegalArgumentException exception = assertThrows(IllegalArgumentException.class, diff --git a/app/src/test/java/ch/sbb/polarion/extension/generic/settings/SettingsServiceTest.java b/app/src/test/java/ch/sbb/polarion/extension/generic/settings/SettingsServiceTest.java index 503aa1f..fdd7450 100644 --- a/app/src/test/java/ch/sbb/polarion/extension/generic/settings/SettingsServiceTest.java +++ b/app/src/test/java/ch/sbb/polarion/extension/generic/settings/SettingsServiceTest.java @@ -1,12 +1,10 @@ package ch.sbb.polarion.extension.generic.settings; +import ch.sbb.polarion.extension.generic.polarion.CustomExtensionMock; +import ch.sbb.polarion.extension.generic.polarion.PlatformContextMockExtension; +import ch.sbb.polarion.extension.generic.polarion.TransactionalExecutorExtension; import com.polarion.alm.projects.IProjectService; import com.polarion.alm.projects.model.IUser; -import com.polarion.alm.shared.api.transaction.ReadOnlyTransaction; -import com.polarion.alm.shared.api.transaction.RunnableInReadOnlyTransaction; -import com.polarion.alm.shared.api.transaction.RunnableInWriteTransaction; -import com.polarion.alm.shared.api.transaction.TransactionalExecutor; -import com.polarion.alm.shared.api.transaction.WriteTransaction; import com.polarion.alm.tracker.ITrackerService; import com.polarion.alm.tracker.model.IBaseline; import com.polarion.alm.tracker.model.ITrackerProject; @@ -43,53 +41,52 @@ import static org.junit.jupiter.api.Assertions.*; import static org.mockito.Mockito.*; -@ExtendWith(MockitoExtension.class) +@ExtendWith({MockitoExtension.class, PlatformContextMockExtension.class, TransactionalExecutorExtension.class}) @SuppressWarnings({"unused", "unchecked", "rawtypes"}) class SettingsServiceTest { private static final String MOCK_SETTINGS = "mock.settings"; + @CustomExtensionMock + private IProjectService projectService; + @CustomExtensionMock + private ITrackerService trackerService; + @CustomExtensionMock + private IRepositoryService repositoryService; + @Test void testSave() { - IRepositoryService repositoryService = mock(IRepositoryService.class); - SettingsService settingsService = new SettingsService(repositoryService, mock(IProjectService.class), mock(ITrackerService.class)); + SettingsService settingsService = new SettingsService(repositoryService, projectService, trackerService); - try (final MockedStatic executor = mockStatic(TransactionalExecutor.class)) { - executor.when(() -> TransactionalExecutor.executeInWriteTransaction(any())).thenAnswer(invocation -> { - RunnableInWriteTransaction runnable = invocation.getArgument(0); - return runnable.run(mock(WriteTransaction.class)); - }); + IRepositoryConnection connection = mock(IRepositoryConnection.class); + when(repositoryService.getConnection(any(ILocation.class))).thenReturn(connection); - IRepositoryConnection connection = mock(IRepositoryConnection.class); - when(repositoryService.getConnection(any(ILocation.class))).thenReturn(connection); + when(connection.exists(any(ILocation.class))).thenReturn(true); + byte[] bytes = "someContent".getBytes(StandardCharsets.UTF_8); + ILocation location = mock(ILocation.class); + settingsService.save(location, bytes); + verify(connection, times(1)).setContent(any(), any()); + verify(connection, times(0)).create(any(), any()); - when(connection.exists(any(ILocation.class))).thenReturn(true); - byte[] bytes = "someContent".getBytes(StandardCharsets.UTF_8); - ILocation location = mock(ILocation.class); + // it must suppress IOException + try (MockedConstruction mockByteArrayInputStream = Mockito.mockConstruction(ByteArrayInputStream.class, + (mock, context) -> doThrow(new IOException("Emulated IOException")).when(mock).close())) { settingsService.save(location, bytes); - verify(connection, times(1)).setContent(any(), any()); - verify(connection, times(0)).create(any(), any()); - - // it must suppress IOException - try (MockedConstruction mockByteArrayInputStream = Mockito.mockConstruction(ByteArrayInputStream.class, - (mock, context) -> doThrow(new IOException("Emulated IOException")).when(mock).close())) { - settingsService.save(location, bytes); - } + } - // but not other exceptions - try (MockedConstruction mockByteArrayInputStream = Mockito.mockConstruction(ByteArrayInputStream.class, - (mock, context) -> doThrow(new RuntimeException("Emulated RuntimeException")).when(mock).close())) { - assertThrows(RuntimeException.class, () -> settingsService.save(location, bytes)); - } + // but not other exceptions + try (MockedConstruction mockByteArrayInputStream = Mockito.mockConstruction(ByteArrayInputStream.class, + (mock, context) -> doThrow(new RuntimeException("Emulated RuntimeException")).when(mock).close())) { + assertThrows(RuntimeException.class, () -> settingsService.save(location, bytes)); + } - settingsService.save(location, bytes); - verify(connection, times(4)).setContent(any(), any()); - verify(connection, times(0)).create(any(), any()); + settingsService.save(location, bytes); + verify(connection, times(4)).setContent(any(), any()); + verify(connection, times(0)).create(any(), any()); - when(connection.exists(any(ILocation.class))).thenReturn(false); - settingsService.save(location, bytes); - verify(connection, times(4)).setContent(any(), any()); - verify(connection, times(1)).create(any(), any()); - } + when(connection.exists(any(ILocation.class))).thenReturn(false); + settingsService.save(location, bytes); + verify(connection, times(4)).setContent(any(), any()); + verify(connection, times(1)).create(any(), any()); } @Test @@ -103,158 +100,117 @@ void testOverloadedSaveMethodCall() { @Test void testDelete() { - IRepositoryService repositoryService = mock(IRepositoryService.class); - SettingsService settingsService = new SettingsService(repositoryService, mock(IProjectService.class), mock(ITrackerService.class)); - - try (final MockedStatic executor = mockStatic(TransactionalExecutor.class)) { - executor.when(() -> TransactionalExecutor.executeInWriteTransaction(any())).thenAnswer(invocation -> { - RunnableInWriteTransaction runnable = invocation.getArgument(0); - return runnable.run(mock(WriteTransaction.class)); - }); - - ILocation location = mock(ILocation.class); - IRepositoryConnection connection = mock(IRepositoryConnection.class); - when(repositoryService.getConnection(any(ILocation.class))).thenReturn(connection); - - when(connection.exists(any(ILocation.class))).thenReturn(false); - settingsService.delete(location); - verify(connection, times(0)).delete(any()); - - when(connection.exists(any(ILocation.class))).thenReturn(true); - settingsService.delete(location); - verify(connection, times(1)).delete(any()); - } - } + SettingsService settingsService = new SettingsService(repositoryService, projectService, trackerService); - @Test - void testRead() { - try (MockedStatic mockedExecutor = mockStatic(TransactionalExecutor.class); - MockedStatic mockedLocation = mockStatic(Location.class)) { - IRepositoryService mockedRepositoryService = mock(IRepositoryService.class); - ITrackerService mockedTrackerService = mock(ITrackerService.class); + ILocation location = mock(ILocation.class); + IRepositoryConnection connection = mock(IRepositoryConnection.class); + when(repositoryService.getConnection(any(ILocation.class))).thenReturn(connection); - IRepositoryReadOnlyConnection mockedConnection = mock(IRepositoryReadOnlyConnection.class); - when(mockedRepositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(mockedConnection); + when(connection.exists(any(ILocation.class))).thenReturn(false); + settingsService.delete(location); + verify(connection, times(0)).delete(any()); - IProjectService mockedProjectService = mock(IProjectService.class); + when(connection.exists(any(ILocation.class))).thenReturn(true); + settingsService.delete(location); + verify(connection, times(1)).delete(any()); + } - mockedExecutor.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any(RunnableInReadOnlyTransaction.class))) - .thenAnswer(invocation -> { - RunnableInReadOnlyTransaction transaction = invocation.getArgument(0); - return transaction.run(mock(ReadOnlyTransaction.class)); - }); + @Test + void testRead() { + IRepositoryReadOnlyConnection mockedConnection = mock(IRepositoryReadOnlyConnection.class); + when(repositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(mockedConnection); - when(mockedConnection.exists(any(ILocation.class))).thenReturn(false); + when(mockedConnection.exists(any(ILocation.class))).thenReturn(false); - assertNull(new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService).read(mock(ILocation.class), null)); + assertNull(new SettingsService(repositoryService, projectService, trackerService).read(mock(ILocation.class), null)); - when(mockedConnection.exists(any(ILocation.class))).thenReturn(true); - when(mockedConnection.getContent(any(ILocation.class))).thenReturn(null); - assertNull(new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService).read(mock(ILocation.class), null)); + when(mockedConnection.exists(any(ILocation.class))).thenReturn(true); + when(mockedConnection.getContent(any(ILocation.class))).thenReturn(null); + assertNull(new SettingsService(repositoryService, projectService, trackerService).read(mock(ILocation.class), null)); - when(mockedConnection.getContent(any(ILocation.class))).thenReturn(new ByteArrayInputStream(new byte[0])); - assertNotNull(new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService).read(mock(ILocation.class), null)); + when(mockedConnection.getContent(any(ILocation.class))).thenReturn(new ByteArrayInputStream(new byte[0])); + assertNotNull(new SettingsService(repositoryService, projectService, trackerService).read(mock(ILocation.class), null)); + try (MockedStatic mockedLocation = mockStatic(Location.class)) { mockedLocation.when(() -> Location.getLocationWithRevision(any(), anyString())).thenReturn(mock(ILocation.class)); - assertNotNull(new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService).read(mock(ILocation.class), "123")); + assertNotNull(new SettingsService(repositoryService, projectService, trackerService).read(mock(ILocation.class), "123")); when(mockedConnection.getContent(any(ILocation.class))).thenThrow(new DriverException()); //DriverException is thrown when wrong revision used - assertNull(new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService).read(mock(ILocation.class), "123")); + assertNull(new SettingsService(repositoryService, projectService, trackerService).read(mock(ILocation.class), "123")); } } @Test void testExists() { - IRepositoryService repositoryService = mock(IRepositoryService.class); - SettingsService settingsService = new SettingsService(repositoryService, mock(IProjectService.class), mock(ITrackerService.class)); - - try (MockedStatic mockedExecutor = mockStatic(TransactionalExecutor.class)) { - mockedExecutor.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any(RunnableInReadOnlyTransaction.class))) - .thenAnswer(invocation -> { - RunnableInReadOnlyTransaction transaction = invocation.getArgument(0); - return transaction.run(mock(ReadOnlyTransaction.class)); - }); + SettingsService settingsService = new SettingsService(repositoryService, projectService, trackerService); - IRepositoryReadOnlyConnection connection = mock(IRepositoryReadOnlyConnection.class); - when(repositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(connection); - ILocation location = mock(ILocation.class); + IRepositoryReadOnlyConnection connection = mock(IRepositoryReadOnlyConnection.class); + when(repositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(connection); + ILocation location = mock(ILocation.class); - when(connection.exists(any())).thenReturn(false); - assertFalse(settingsService.exists(location)); + when(connection.exists(any())).thenReturn(false); + assertFalse(settingsService.exists(location)); - when(connection.exists(any())).thenReturn(true); - assertTrue(settingsService.exists(location)); - } + when(connection.exists(any())).thenReturn(true); + assertTrue(settingsService.exists(location)); } @Test void testRevisionsList() { - try (MockedStatic mockedExecutor = mockStatic(TransactionalExecutor.class)) { - IRepositoryService mockedRepositoryService = mock(IRepositoryService.class); - - IRepositoryReadOnlyConnection mockedConnection = mock(IRepositoryReadOnlyConnection.class); - when(mockedConnection.getRevisionsMetaData(any(ILocation.class), anyBoolean())).thenReturn(Arrays.asList( - constructExtendedRevisionMetaData("73", "author1", "desc1", DateUtils.date(2023, 1, 20, 14, 45)), - constructExtendedRevisionMetaData("456", "author2", "desc2", DateUtils.date(2023, 5, 10, 10, 24)), - constructExtendedRevisionMetaData("789", "author3", "desc3", DateUtils.date(2023, 2, 12, 12, 21)), - constructExtendedRevisionMetaData("555", "author4", "desc4", DateUtils.date(2023, 7, 11, 22, 12)) - )); - when(mockedRepositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(mockedConnection); - - ITrackerService mockedTrackerService = mock(ITrackerService.class); - ITrackerProject trackerProject = mock(ITrackerProject.class); - when(mockedTrackerService.getTrackerProject(anyString())).thenReturn(trackerProject); - IInternalBaselinesManager baselinesManager = mock(IInternalBaselinesManager.class); - when(trackerProject.getBaselinesManager()).thenReturn(baselinesManager); - IBaseline baseline = mock(IBaseline.class); - when(baseline.getName()).thenReturn("baselineName"); - when(baselinesManager.getRevisionBaseline(anyString())).thenReturn(baseline); - - IProjectService mockedProjectService = mock(IProjectService.class); - List users = Arrays.asList( - mockUser("author1", "USER NAME"), - mockUser("author2", "USER NAME 2") - ); - when(mockedProjectService.getUsers()).thenReturn(new PObjectList(null, users)); - - mockedExecutor.when(() -> TransactionalExecutor.executeSafelyInReadOnlyTransaction(any(RunnableInReadOnlyTransaction.class))) - .thenAnswer(invocation -> { - RunnableInReadOnlyTransaction transaction = invocation.getArgument(0); - return transaction.run(mock(ReadOnlyTransaction.class)); - }); - - ILocation location = mock(ILocation.class); - when(location.getLastComponent()).thenReturn(MOCK_SETTINGS); - - // non-existing location - when(mockedConnection.exists(any(ILocation.class))).thenReturn(false); - SettingsService settingsService = new SettingsService(mockedRepositoryService, mockedProjectService, mockedTrackerService); - List revisions = settingsService.listRevisions(location, null); - assertTrue(revisions.isEmpty()); - - // general case - when(mockedConnection.exists(any(ILocation.class))).thenReturn(true); - revisions = settingsService.listRevisions(location, null); - assertEquals(4, revisions.size()); - assertEquals(Arrays.asList("789", "555", "456", "73"), revisions.stream().map(Revision::getName).toList()); - assertEquals("author3", revisions.get(0).getAuthor()); - assertEquals("USER NAME 2", revisions.get(2).getAuthor()); - assertEquals(Set.of("baselineName"), revisions.stream().map(Revision::getBaseline).collect(Collectors.toSet())); - - // missing user's name - users = Arrays.asList( - mockUser("author1", "USER NAME"), - mockUser("author2", null) - ); - when(mockedProjectService.getUsers()).thenReturn(new PObjectList(null, users)); - revisions = settingsService.listRevisions(location, null); - assertEquals("author2", revisions.get(2).getAuthor()); - - // emulate case when revision's location name not match - when(location.getLastComponent()).thenReturn("unknown_name.settings"); - revisions = settingsService.listRevisions(location, null); - assertTrue(revisions.isEmpty()); - } + IRepositoryReadOnlyConnection mockedConnection = mock(IRepositoryReadOnlyConnection.class); + when(mockedConnection.getRevisionsMetaData(any(ILocation.class), anyBoolean())).thenReturn(Arrays.asList( + constructExtendedRevisionMetaData("73", "author1", "desc1", DateUtils.date(2023, 1, 20, 14, 45)), + constructExtendedRevisionMetaData("456", "author2", "desc2", DateUtils.date(2023, 5, 10, 10, 24)), + constructExtendedRevisionMetaData("789", "author3", "desc3", DateUtils.date(2023, 2, 12, 12, 21)), + constructExtendedRevisionMetaData("555", "author4", "desc4", DateUtils.date(2023, 7, 11, 22, 12)) + )); + when(repositoryService.getReadOnlyConnection(any(ILocation.class))).thenReturn(mockedConnection); + + ITrackerProject trackerProject = mock(ITrackerProject.class); + when(trackerService.getTrackerProject(anyString())).thenReturn(trackerProject); + IInternalBaselinesManager baselinesManager = mock(IInternalBaselinesManager.class); + when(trackerProject.getBaselinesManager()).thenReturn(baselinesManager); + IBaseline baseline = mock(IBaseline.class); + when(baseline.getName()).thenReturn("baselineName"); + when(baselinesManager.getRevisionBaseline(anyString())).thenReturn(baseline); + + List users = Arrays.asList( + mockUser("author1", "USER NAME"), + mockUser("author2", "USER NAME 2") + ); + when(projectService.getUsers()).thenReturn(new PObjectList(null, users)); + + ILocation location = mock(ILocation.class); + when(location.getLastComponent()).thenReturn(MOCK_SETTINGS); + + // non-existing location + when(mockedConnection.exists(any(ILocation.class))).thenReturn(false); + SettingsService settingsService = new SettingsService(repositoryService, projectService, trackerService); + List revisions = settingsService.listRevisions(location, null); + assertTrue(revisions.isEmpty()); + + // general case + when(mockedConnection.exists(any(ILocation.class))).thenReturn(true); + revisions = settingsService.listRevisions(location, null); + assertEquals(4, revisions.size()); + assertEquals(Arrays.asList("789", "555", "456", "73"), revisions.stream().map(Revision::getName).toList()); + assertEquals("author3", revisions.get(0).getAuthor()); + assertEquals("USER NAME 2", revisions.get(2).getAuthor()); + assertEquals(Set.of("baselineName"), revisions.stream().map(Revision::getBaseline).collect(Collectors.toSet())); + + // missing user's name + users = Arrays.asList( + mockUser("author1", "USER NAME"), + mockUser("author2", null) + ); + when(projectService.getUsers()).thenReturn(new PObjectList(null, users)); + revisions = settingsService.listRevisions(location, null); + assertEquals("author2", revisions.get(2).getAuthor()); + + // emulate case when revision's location name not match + when(location.getLastComponent()).thenReturn("unknown_name.settings"); + revisions = settingsService.listRevisions(location, null); + assertTrue(revisions.isEmpty()); } @Test diff --git a/pom.xml b/pom.xml index 4975146..47795f3 100644 --- a/pom.xml +++ b/pom.xml @@ -462,6 +462,25 @@ ${javax.transaction-api.version} test + + + com.polarion.alm.builder + builder + ${polarion.version} + test + + + com.siemens.polarion.integration.ci + integrationci + ${polarion.version} + test + + + com.polarion.alm.oslc + oslc + ${polarion.version} + test +