diff --git a/.github/workflows/pull-request-check.yml b/.github/workflows/pull-request-check.yml index a0389a7d..95dc610f 100644 --- a/.github/workflows/pull-request-check.yml +++ b/.github/workflows/pull-request-check.yml @@ -15,12 +15,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - java: [ 8, 11, 17, 21 ] + java: [ 11, 17, 21 ] fail-fast: false steps: - uses: actions/checkout@v3 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v3 with: distribution: 'temurin' diff --git a/.github/workflows/xtf-maven-release.yml b/.github/workflows/xtf-maven-release.yml index 35a95e3e..9c6865dd 100644 --- a/.github/workflows/xtf-maven-release.yml +++ b/.github/workflows/xtf-maven-release.yml @@ -14,10 +14,10 @@ jobs: steps: - uses: actions/checkout@v1 - - name: Set up JDK 8 + - name: Set up JDK 11 uses: actions/setup-java@v1 with: - java-version: 8 + java-version: 11 java-package: jdk architecture: x64 - name: Check well-formatted code diff --git a/builder/pom.xml b/builder/pom.xml index d67e1771..9aa25d54 100644 --- a/builder/pom.xml +++ b/builder/pom.xml @@ -28,11 +28,6 @@ provided - - io.fabric8 - openshift-server-mock - test - diff --git a/builder/src/test/java/cz/xtf/builder/openshift/mocked/smoke/BasicOpenShiftTest.java b/builder/src/test/java/cz/xtf/builder/openshift/mocked/smoke/BasicOpenShiftTest.java deleted file mode 100644 index 67e4b9ad..00000000 --- a/builder/src/test/java/cz/xtf/builder/openshift/mocked/smoke/BasicOpenShiftTest.java +++ /dev/null @@ -1,129 +0,0 @@ -package cz.xtf.builder.openshift.mocked.smoke; - -import java.util.Collections; -import java.util.List; -import java.util.Map; - -import org.junit.Assert; -import org.junit.jupiter.api.Test; - -import cz.xtf.builder.builders.ConfigMapBuilder; -import cz.xtf.builder.builders.DeploymentConfigBuilder; -import cz.xtf.core.openshift.OpenShift; -import io.fabric8.kubernetes.api.model.ConfigMap; -import io.fabric8.kubernetes.api.model.PodSpec; -import io.fabric8.openshift.api.model.DeploymentConfig; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; - -public class BasicOpenShiftTest { - - private static final String TEST_RESOURCE_LABEL_NAME_APP = "app"; - private static final String TEST_RESOURCE_LABEL_VALUE_APP = "xtf-core-test-openshift-mocked-smoke"; - private final static Map TEST_RESOURCE_LABELS = Collections.singletonMap( - TEST_RESOURCE_LABEL_NAME_APP, TEST_RESOURCE_LABEL_VALUE_APP); - - private static final String TEST_CONFIGMAP_NAME = "test-configmap"; - private static final String TEST_DEPLOYMENT_NAME = "test-deployment"; - private static final String TEST_DEPLOYMENT_CONFIG_NAME = "test-deployment-config"; - private static final Integer TEST_DEPLOYMENT_CONFIG_REPLICAS = 3; - - private final OpenShiftServer openShiftServer; - private final OpenShift openShiftClient; - - public BasicOpenShiftTest() { - this.openShiftServer = new OpenShiftServer(false, true); - this.openShiftServer.before(); - // we want to test the XTF OpenShift client, but we need to configure it with the mocked server client - // properties - OpenShiftClient mockedServerClient = openShiftServer.getOpenshiftClient(); - this.openShiftClient = OpenShift.get( - mockedServerClient.getMasterUrl().toString(), - mockedServerClient.getNamespace(), - mockedServerClient.getConfiguration().getUsername(), - mockedServerClient.getConfiguration().getPassword()); - } - - @Test - public void testConfigMapBuilder() { - // arrange - final String dataEntryKey = "test.properties"; - final String dataEntryValue = "foo=bar"; - final ConfigMap configMap = new ConfigMapBuilder(TEST_CONFIGMAP_NAME) - .addLabels(TEST_RESOURCE_LABELS) - .configEntry(dataEntryKey, dataEntryValue) - .build(); - // act - openShiftClient.createResources(configMap); - // assert - List actualConfigMaps = openShiftClient.getConfigMaps(); - Assert.assertEquals(String.format("ConfigMap resource list has unexpected size: %d.", - actualConfigMaps.size()), 1, actualConfigMaps.size()); - ConfigMap actualConfigMap = openShiftClient.getConfigMap(TEST_CONFIGMAP_NAME); - Assert.assertNotNull("ConfigMap resource creation failed.", actualConfigMap); - Assert.assertEquals("ConfigMap resource has unexpected name.", - TEST_CONFIGMAP_NAME, - actualConfigMap.getMetadata().getName()); - Assert.assertEquals( - String.format("ConfigMap resource has unexpected data size: %d", actualConfigMap.getData().entrySet().size()), - 1, - actualConfigMap.getData().entrySet().size()); - // safe now - final Map.Entry dataEntry = actualConfigMap.getData().entrySet().stream().findFirst().get(); - Assert.assertEquals( - String.format("ConfigMap resource has unexpected data entry key: %s", dataEntry.getKey()), - dataEntryKey, - dataEntry.getKey()); - Assert.assertEquals( - String.format("ConfigMap resource has unexpected data entry value: %s", dataEntry.getValue()), - dataEntryValue, - dataEntry.getValue()); - } - - @Test - public void testDeploymentConfigBuilder() { - // arrange - final DeploymentConfig deploymentConfig = new DeploymentConfigBuilder(TEST_DEPLOYMENT_CONFIG_NAME) - .addLabels(TEST_RESOURCE_LABELS) - .setReplicas(TEST_DEPLOYMENT_CONFIG_REPLICAS) - .onImageChange() - .setRecreateStrategy() - .build(); - // act - openShiftClient.createResources(deploymentConfig); - // assert - List actualResources = openShiftClient.deploymentConfigs() - .withLabel(TEST_RESOURCE_LABEL_NAME_APP, TEST_RESOURCE_LABEL_VALUE_APP) - .list() - .getItems(); - Assert.assertEquals( - String.format("DeploymentConfig resource list has unexpected size: %d.", actualResources.size()), - 1, actualResources.size()); - DeploymentConfig actualResource = actualResources.get(0); - Assert.assertEquals( - String.format("DeploymentConfig resource has unexpected name: %s.", actualResource.getMetadata().getName()), - TEST_DEPLOYMENT_CONFIG_NAME, actualResource.getMetadata().getName()); - Assert.assertNotNull(String.format("DeploymentConfig resource has null \".spec\"", actualResource.getSpec())); - Assert.assertEquals( - String.format("DeploymentConfig resource has unexpected \".spec.replicas\": %s.", - actualResource.getSpec().getReplicas()), - TEST_DEPLOYMENT_CONFIG_REPLICAS, - actualResource.getSpec().getReplicas()); - Assert.assertNotNull( - String.format("DeploymentConfig resource has null \".spec.strategy\"", actualResource.getSpec().getStrategy())); - Assert.assertEquals( - String.format("DeploymentConfig resource has unexpected \".spec.strategy\": %s.", - actualResource.getSpec().getStrategy().getType()), - "Recreate", - actualResource.getSpec().getStrategy().getType()); - Assert.assertNotNull( - String.format("DeploymentConfig resource has null \".spec.template\"", actualResource.getSpec().getTemplate())); - Assert.assertNotNull(String.format("DeploymentConfig resource has null \".spec.template.spec\"", - actualResource.getSpec().getTemplate().getSpec())); - PodSpec podSpec = actualResource.getSpec().getTemplate().getSpec(); - Assert.assertEquals( - String.format("DeploymentConfig resource has unexpected \".spec.template.spec.containers\": %s.", - podSpec.getContainers().size()), - 0, podSpec.getContainers().size()); - } -} diff --git a/builder/src/test/java/cz/xtf/builder/openshift/smoke/BuildersTest.java b/builder/src/test/java/cz/xtf/builder/openshift/smoke/BuildersTest.java new file mode 100644 index 00000000..92fe39b1 --- /dev/null +++ b/builder/src/test/java/cz/xtf/builder/openshift/smoke/BuildersTest.java @@ -0,0 +1,106 @@ +package cz.xtf.builder.openshift.smoke; + +import java.util.Collections; +import java.util.Map; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +import cz.xtf.builder.builders.ConfigMapBuilder; +import cz.xtf.builder.builders.DeploymentConfigBuilder; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.PodSpec; +import io.fabric8.openshift.api.model.DeploymentConfig; + +/** + * Unit tests for XTF builder classes. + * Tests verify that builders correctly construct Kubernetes/OpenShift resource objects. + * + * Replaces BasicOpenShiftTest which used OpenShiftServer mock (removed in Fabric8 7.4.0). + */ +public class BuildersTest { + + private static final String TEST_RESOURCE_LABEL_NAME_APP = "app"; + private static final String TEST_RESOURCE_LABEL_VALUE_APP = "xtf-core-test-openshift-mocked-smoke"; + private final static Map TEST_RESOURCE_LABELS = Collections.singletonMap( + TEST_RESOURCE_LABEL_NAME_APP, TEST_RESOURCE_LABEL_VALUE_APP); + + private static final String TEST_CONFIGMAP_NAME = "test-configmap"; + private static final String TEST_DEPLOYMENT_CONFIG_NAME = "test-deployment-config"; + private static final Integer TEST_DEPLOYMENT_CONFIG_REPLICAS = 3; + + @Test + public void testConfigMapBuilder() { + // arrange + final String dataEntryKey = "test.properties"; + final String dataEntryValue = "foo=bar"; + final ConfigMap configMap = new ConfigMapBuilder(TEST_CONFIGMAP_NAME) + .addLabels(TEST_RESOURCE_LABELS) + .configEntry(dataEntryKey, dataEntryValue) + .build(); + + // assert - verify the built ConfigMap structure + Assertions.assertNotNull(configMap, "ConfigMap resource creation failed."); + Assertions.assertEquals(TEST_CONFIGMAP_NAME, + configMap.getMetadata().getName(), + "ConfigMap resource has unexpected name."); + Assertions.assertEquals(1, + configMap.getData().entrySet().size(), + String.format("ConfigMap resource has unexpected data size: %d", + configMap.getData().entrySet().size())); + + // safe now + final Map.Entry dataEntry = configMap.getData().entrySet().stream().findFirst().get(); + Assertions.assertEquals(dataEntryKey, + dataEntry.getKey(), + String.format("ConfigMap resource has unexpected data entry key: %s", dataEntry.getKey())); + Assertions.assertEquals(dataEntryValue, + dataEntry.getValue(), + String.format("ConfigMap resource has unexpected data entry value: %s", dataEntry.getValue())); + } + + @Test + public void testDeploymentConfigBuilder() { + // arrange + final DeploymentConfig deploymentConfig = new DeploymentConfigBuilder(TEST_DEPLOYMENT_CONFIG_NAME) + .addLabels(TEST_RESOURCE_LABELS) + .setReplicas(TEST_DEPLOYMENT_CONFIG_REPLICAS) + .onImageChange() + .setRecreateStrategy() + .build(); + + // assert - verify the built DeploymentConfig structure + Assertions.assertNotNull(deploymentConfig, "DeploymentConfig resource creation failed."); + Assertions.assertEquals(TEST_DEPLOYMENT_CONFIG_NAME, + deploymentConfig.getMetadata().getName(), + String.format("DeploymentConfig resource has unexpected name: %s.", + deploymentConfig.getMetadata().getName())); + Assertions.assertEquals(TEST_RESOURCE_LABELS, + deploymentConfig.getMetadata().getLabels(), + "DeploymentConfig resource has unexpected labels."); + Assertions.assertNotNull(deploymentConfig.getSpec(), + String.format("DeploymentConfig resource has null \".spec\"", deploymentConfig.getSpec())); + Assertions.assertEquals(TEST_DEPLOYMENT_CONFIG_REPLICAS, + deploymentConfig.getSpec().getReplicas(), + String.format("DeploymentConfig resource has unexpected \".spec.replicas\": %s.", + deploymentConfig.getSpec().getReplicas())); + Assertions.assertNotNull(deploymentConfig.getSpec().getStrategy(), + String.format("DeploymentConfig resource has null \".spec.strategy\"", + deploymentConfig.getSpec().getStrategy())); + Assertions.assertEquals("Recreate", + deploymentConfig.getSpec().getStrategy().getType(), + String.format("DeploymentConfig resource has unexpected \".spec.strategy\": %s.", + deploymentConfig.getSpec().getStrategy().getType())); + Assertions.assertNotNull(deploymentConfig.getSpec().getTemplate(), + String.format("DeploymentConfig resource has null \".spec.template\"", + deploymentConfig.getSpec().getTemplate())); + Assertions.assertNotNull(deploymentConfig.getSpec().getTemplate().getSpec(), + String.format("DeploymentConfig resource has null \".spec.template.spec\"", + deploymentConfig.getSpec().getTemplate().getSpec())); + PodSpec podSpec = deploymentConfig.getSpec().getTemplate().getSpec(); + Assertions.assertEquals(0, + podSpec.getContainers().size(), + String.format("DeploymentConfig resource has unexpected \".spec.template.spec.containers\": %s.", + podSpec.getContainers().size())); + } +} diff --git a/core/pom.xml b/core/pom.xml index 9d1c3b08..fe3ec52d 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -16,11 +16,6 @@ io.fabric8 openshift-client - - io.fabric8 - openshift-server-mock - test - org.projectlombok @@ -77,6 +72,18 @@ test + + org.mockito + mockito-core + test + + + + org.mockito + mockito-junit-jupiter + test + + org.assertj assertj-core diff --git a/core/src/test/java/cz/xtf/core/bm/BinaryBuildMemoryTest.java b/core/src/test/java/cz/xtf/core/bm/BinaryBuildMemoryTest.java new file mode 100644 index 00000000..d4ba1ca9 --- /dev/null +++ b/core/src/test/java/cz/xtf/core/bm/BinaryBuildMemoryTest.java @@ -0,0 +1,187 @@ +package cz.xtf.core.bm; + +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; + +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import cz.xtf.core.config.BuildManagerConfig; +import cz.xtf.core.config.XTFConfig; +import io.fabric8.openshift.api.model.BuildConfig; + +public class BinaryBuildMemoryTest { + + private static final String TEST_BUILD_ID = "test-binary-build"; + private static final String TEST_BUILDER_IMAGE = "registry.access.redhat.com/ubi8/openjdk-11:latest"; + + @BeforeEach + public void cleanSystemProperties() { + // Safety net: ensure clean state before each test + // Each test also handles its own cleanup in finally blocks + System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); + System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); + XTFConfig.loadConfig(); + } + + @Test + public void testBuildConfig_WhenMemoryLimitsSet_ShouldContainResourceRequirements() throws IOException { + // Given: System properties configured for memory request and limit + String memoryRequest = "512Mi"; + String memoryLimit = "2Gi"; + + try { + // Set properties inside try block to ensure cleanup via finally + System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); + System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); + XTFConfig.loadConfig(); + + Path tempFileWithLimits = Files.createTempFile("test-with-limits", ".war"); + Files.write(tempFileWithLimits, "test content".getBytes()); + + BinaryBuildFromFile buildWithLimits = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithLimits, + null, + TEST_BUILD_ID + "-with-limits"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithLimits.bc; + + // Then: BuildConfig should contain resource requirements + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory limits are configured"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), + "BuildConfig should have resource requests"); + Assertions.assertEquals(memoryRequest, + buildConfig.getSpec().getResources().getRequests().get("memory").toString(), + "Memory request should match configured value"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), + "BuildConfig should have resource limits"); + Assertions.assertEquals(memoryLimit, + buildConfig.getSpec().getResources().getLimits().get("memory").toString(), + "Memory limit should match configured value"); + + Files.delete(tempFileWithLimits); + } finally { + // Cleanup always runs, even if property setting or test fails + System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); + System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenOnlyMemoryRequestSet_ShouldContainOnlyRequest() throws IOException { + // Given: System property configured for memory request only + String memoryRequest = "256Mi"; + + try { + // Set properties inside try block to ensure cleanup via finally + System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); + XTFConfig.loadConfig(); + + Path tempFileWithRequest = Files.createTempFile("test-with-request", ".war"); + Files.write(tempFileWithRequest, "test content".getBytes()); + + BinaryBuildFromFile buildWithRequest = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithRequest, + null, + TEST_BUILD_ID + "-with-request"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithRequest.bc; + + // Then: BuildConfig should contain only resource requests + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory request is configured"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), + "BuildConfig should have resource requests"); + Assertions.assertEquals(memoryRequest, + buildConfig.getSpec().getResources().getRequests().get("memory").toString(), + "Memory request should match configured value"); + + Assertions.assertTrue(buildConfig.getSpec().getResources().getLimits() == null + || buildConfig.getSpec().getResources().getLimits().isEmpty(), + "BuildConfig should not have limits when only request is set"); + + Files.delete(tempFileWithRequest); + } finally { + // Cleanup always runs, even if property setting or test fails + System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenOnlyMemoryLimitSet_ShouldContainOnlyLimit() throws IOException { + // Given: System property configured for memory limit only + String memoryLimit = "1Gi"; + + try { + // Set properties inside try block to ensure cleanup via finally + System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); + XTFConfig.loadConfig(); + + Path tempFileWithLimit = Files.createTempFile("test-with-limit", ".war"); + Files.write(tempFileWithLimit, "test content".getBytes()); + + BinaryBuildFromFile buildWithLimit = new BinaryBuildFromFile( + TEST_BUILDER_IMAGE, + tempFileWithLimit, + null, + TEST_BUILD_ID + "-with-limit"); + + // When: Getting the BuildConfig + BuildConfig buildConfig = buildWithLimit.bc; + + // Then: BuildConfig should contain only resource limits + Assertions.assertNotNull(buildConfig.getSpec().getResources(), + "BuildConfig should have resources set when memory limit is configured"); + + Assertions.assertTrue(buildConfig.getSpec().getResources().getRequests() == null + || buildConfig.getSpec().getResources().getRequests().isEmpty(), + "BuildConfig should not have requests when only limit is set"); + + Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), + "BuildConfig should have resource limits"); + Assertions.assertEquals(memoryLimit, + buildConfig.getSpec().getResources().getLimits().get("memory").toString(), + "Memory limit should match configured value"); + + Files.delete(tempFileWithLimit); + } finally { + // Cleanup always runs, even if property setting or test fails + System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); + XTFConfig.loadConfig(); + } + } + + @Test + public void testBuildConfig_WhenMemoryLimitsNotSet_ShouldNotContainResources() throws IOException { + // Given: BinaryBuild without memory configuration + // Create a temporary test file for BinaryBuildFromFile + Path tempFile = Files.createTempFile("test", ".war"); + Files.write(tempFile, "test content".getBytes()); + + try { + // Create BinaryBuild instance + BinaryBuildFromFile binaryBuild = new BinaryBuildFromFile(TEST_BUILDER_IMAGE, tempFile, null, TEST_BUILD_ID); + + // When: Getting the BuildConfig + BuildConfig buildConfig = binaryBuild.bc; + + // Then: BuildConfig should not contain resource requirements + Assertions.assertNull(buildConfig.getSpec().getResources(), + "BuildConfig should not have resources when memory limits are not configured"); + } finally { + Files.delete(tempFile); + } + } +} diff --git a/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java b/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java index 70536f44..3d6d0bda 100644 --- a/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java +++ b/core/src/test/java/cz/xtf/core/bm/BinaryBuildTest.java @@ -1,5 +1,10 @@ package cz.xtf.core.bm; +import static org.mockito.Mockito.lenient; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; @@ -8,47 +13,51 @@ import org.junit.jupiter.api.Assertions; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; -import cz.xtf.core.config.BuildManagerConfig; -import cz.xtf.core.config.XTFConfig; import cz.xtf.core.openshift.OpenShift; import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import io.fabric8.kubernetes.client.dsl.MixedOperation; +import io.fabric8.kubernetes.client.dsl.Resource; import io.fabric8.openshift.api.model.Build; import io.fabric8.openshift.api.model.BuildBuilder; import io.fabric8.openshift.api.model.BuildConfig; import io.fabric8.openshift.api.model.BuildConfigBuilder; +import io.fabric8.openshift.api.model.BuildConfigList; import io.fabric8.openshift.api.model.BuildStatusBuilder; import io.fabric8.openshift.api.model.ImageStream; import io.fabric8.openshift.api.model.ImageStreamBuilder; -import io.fabric8.openshift.client.OpenShiftClient; -import io.fabric8.openshift.client.server.mock.OpenShiftServer; +import io.fabric8.openshift.api.model.ImageStreamList; +import io.fabric8.openshift.client.dsl.BuildConfigResource; /** * Tests for BinaryBuild class, specifically testing build status handling. */ +@ExtendWith(MockitoExtension.class) public class BinaryBuildTest { private static final String TEST_BUILD_ID = "test-binary-build"; private static final String TEST_BUILDER_IMAGE = "registry.access.redhat.com/ubi8/openjdk-11:latest"; - private OpenShiftServer openShiftServer; + @Mock private OpenShift openShift; + + @Mock + private MixedOperation> buildConfigOp; + + @Mock + private MixedOperation> imageStreamOp; + private Path tempFile; private BinaryBuildFromFile binaryBuild; @BeforeEach public void setup() throws IOException { - // Initialize OpenShift mock server - this.openShiftServer = new OpenShiftServer(false, true); - this.openShiftServer.before(); - - // Create XTF OpenShift client from mocked server - OpenShiftClient mockedServerClient = openShiftServer.getOpenshiftClient(); - this.openShift = OpenShift.get( - mockedServerClient.getMasterUrl().toString(), - mockedServerClient.getNamespace(), - mockedServerClient.getConfiguration().getUsername(), - mockedServerClient.getConfiguration().getPassword()); + // Setup fluent API chains + when(openShift.buildConfigs()).thenReturn(buildConfigOp); + when(openShift.imageStreams()).thenReturn(imageStreamOp); // Create a temporary test file for BinaryBuildFromFile tempFile = Files.createTempFile("test", ".war"); @@ -60,9 +69,6 @@ public void setup() throws IOException { @AfterEach public void cleanup() throws IOException { - if (openShiftServer != null) { - openShiftServer.after(); - } if (tempFile != null && Files.exists(tempFile)) { Files.delete(tempFile); } @@ -72,12 +78,14 @@ public void cleanup() throws IOException { public void testNeedsUpdate_WhenBuildStatusIsError_ShouldReturnTrue() { // Given: BuildConfig and ImageStream exist with a build in "Error" status ImageStream imageStream = createImageStream(TEST_BUILD_ID); - BuildConfig buildConfig = createBuildConfig(TEST_BUILD_ID, 1); + BuildConfig buildConfig = createBuildConfigWithContentHash(TEST_BUILD_ID, 1); Build build = createBuildWithStatus(TEST_BUILD_ID + "-1", "Error"); - openShift.imageStreams().create(imageStream); - openShift.buildConfigs().create(buildConfig); - openShift.builds().create(build); + setupResourceMocks(buildConfig, imageStream); + + // lenient() needed because getBuild() is only called if previous checks pass (conditional execution), + // and Mockito's strict stubbing sees this as potentially unused stubbing + lenient().when(openShift.getBuild(TEST_BUILD_ID + "-1")).thenReturn(build); // When: Checking if build needs update boolean needsUpdate = binaryBuild.needsUpdate(openShift); @@ -85,18 +93,22 @@ public void testNeedsUpdate_WhenBuildStatusIsError_ShouldReturnTrue() { // Then: Should return true because build is in Error status Assertions.assertTrue(needsUpdate, "Build with 'Error' status should trigger needsUpdate=true"); + + verify(openShift).getBuild(TEST_BUILD_ID + "-1"); } @Test public void testNeedsUpdate_WhenBuildStatusIsFailed_ShouldReturnTrue() { // Given: BuildConfig and ImageStream exist with a build in "Failed" status ImageStream imageStream = createImageStream(TEST_BUILD_ID); - BuildConfig buildConfig = createBuildConfig(TEST_BUILD_ID, 1); + BuildConfig buildConfig = createBuildConfigWithContentHash(TEST_BUILD_ID, 1); Build build = createBuildWithStatus(TEST_BUILD_ID + "-1", "Failed"); - openShift.imageStreams().create(imageStream); - openShift.buildConfigs().create(buildConfig); - openShift.builds().create(build); + setupResourceMocks(buildConfig, imageStream); + + // lenient() needed because getBuild() is only called if previous checks pass (conditional execution), + // and Mockito's strict stubbing sees this as potentially unused stubbing + lenient().when(openShift.getBuild(TEST_BUILD_ID + "-1")).thenReturn(build); // When: Checking if build needs update boolean needsUpdate = binaryBuild.needsUpdate(openShift); @@ -104,6 +116,8 @@ public void testNeedsUpdate_WhenBuildStatusIsFailed_ShouldReturnTrue() { // Then: Should return true because build is in Failed status Assertions.assertTrue(needsUpdate, "Build with 'Failed' status should trigger needsUpdate=true"); + + verify(openShift).getBuild(TEST_BUILD_ID + "-1"); } @Test @@ -113,9 +127,11 @@ public void testNeedsUpdate_WhenBuildStatusIsComplete_ShouldReturnFalse() { BuildConfig buildConfig = createBuildConfigWithContentHash(TEST_BUILD_ID, 1); Build build = createBuildWithStatus(TEST_BUILD_ID + "-1", "Complete"); - openShift.imageStreams().create(imageStream); - openShift.buildConfigs().create(buildConfig); - openShift.builds().create(build); + setupResourceMocks(buildConfig, imageStream); + + // lenient() needed because getBuild() is only called if previous checks pass (conditional execution), + // and Mockito's strict stubbing sees this as potentially unused stubbing + lenient().when(openShift.getBuild(TEST_BUILD_ID + "-1")).thenReturn(build); // When: Checking if build needs update boolean needsUpdate = binaryBuild.needsUpdate(openShift); @@ -123,11 +139,14 @@ public void testNeedsUpdate_WhenBuildStatusIsComplete_ShouldReturnFalse() { // Then: Should return false because build is successful Assertions.assertFalse(needsUpdate, "Build with 'Complete' status should trigger needsUpdate=false"); + + verify(openShift).getBuild(TEST_BUILD_ID + "-1"); } @Test public void testNeedsUpdate_WhenNoBuildConfigExists_ShouldReturnTrue() { // Given: No BuildConfig or ImageStream exists + setupResourceMocks(null, null); // When: Checking if build needs update boolean needsUpdate = binaryBuild.needsUpdate(openShift); @@ -141,11 +160,11 @@ public void testNeedsUpdate_WhenNoBuildConfigExists_ShouldReturnTrue() { public void testNeedsUpdate_WhenBuildIsNull_ShouldReturnTrue() { // Given: BuildConfig exists but no Build ImageStream imageStream = createImageStream(TEST_BUILD_ID); - BuildConfig buildConfig = createBuildConfig(TEST_BUILD_ID, 1); + BuildConfig buildConfig = createBuildConfigWithContentHash(TEST_BUILD_ID, 1); - openShift.imageStreams().create(imageStream); - openShift.buildConfigs().create(buildConfig); + setupResourceMocks(buildConfig, imageStream); // Intentionally not creating the Build + lenient().when(openShift.getBuild(TEST_BUILD_ID + "-1")).thenReturn(null); // When: Checking if build needs update boolean needsUpdate = binaryBuild.needsUpdate(openShift); @@ -153,150 +172,8 @@ public void testNeedsUpdate_WhenBuildIsNull_ShouldReturnTrue() { // Then: Should return true because build doesn't exist Assertions.assertTrue(needsUpdate, "Missing Build should trigger needsUpdate=true"); - } - - @Test - public void testBuildConfig_WhenMemoryLimitsSet_ShouldContainResourceRequirements() throws IOException { - // Given: System properties configured for memory request and limit - String memoryRequest = "512Mi"; - String memoryLimit = "2Gi"; - System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); - System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); - XTFConfig.loadConfig(); - - try { - Path tempFileWithLimits = Files.createTempFile("test-with-limits", ".war"); - Files.write(tempFileWithLimits, "test content".getBytes()); - - BinaryBuildFromFile buildWithLimits = new BinaryBuildFromFile( - TEST_BUILDER_IMAGE, - tempFileWithLimits, - null, - TEST_BUILD_ID + "-with-limits"); - - // When: Getting the BuildConfig - BuildConfig buildConfig = buildWithLimits.bc; - - // Then: BuildConfig should contain resource requirements - Assertions.assertNotNull(buildConfig.getSpec().getResources(), - "BuildConfig should have resources set when memory limits are configured"); - - Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), - "BuildConfig should have resource requests"); - Assertions.assertEquals(memoryRequest, - buildConfig.getSpec().getResources().getRequests().get("memory").toString(), - "Memory request should match configured value"); - - Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), - "BuildConfig should have resource limits"); - Assertions.assertEquals(memoryLimit, - buildConfig.getSpec().getResources().getLimits().get("memory").toString(), - "Memory limit should match configured value"); - - // Cleanup - Files.delete(tempFileWithLimits); - } finally { - System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); - System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); - XTFConfig.loadConfig(); - } - } - - @Test - public void testBuildConfig_WhenOnlyMemoryRequestSet_ShouldContainOnlyRequest() throws IOException { - // Given: System property configured for memory request only - String memoryRequest = "256Mi"; - System.setProperty(BuildManagerConfig.MEMORY_REQUEST, memoryRequest); - XTFConfig.loadConfig(); - - try { - Path tempFileWithRequest = Files.createTempFile("test-with-request", ".war"); - Files.write(tempFileWithRequest, "test content".getBytes()); - - BinaryBuildFromFile buildWithRequest = new BinaryBuildFromFile( - TEST_BUILDER_IMAGE, - tempFileWithRequest, - null, - TEST_BUILD_ID + "-with-request"); - - // When: Getting the BuildConfig - BuildConfig buildConfig = buildWithRequest.bc; - - // Then: BuildConfig should contain only resource requests - Assertions.assertNotNull(buildConfig.getSpec().getResources(), - "BuildConfig should have resources set when memory request is configured"); - - Assertions.assertNotNull(buildConfig.getSpec().getResources().getRequests(), - "BuildConfig should have resource requests"); - Assertions.assertEquals(memoryRequest, - buildConfig.getSpec().getResources().getRequests().get("memory").toString(), - "Memory request should match configured value"); - - Assertions.assertTrue(buildConfig.getSpec().getResources().getLimits() == null - || buildConfig.getSpec().getResources().getLimits().isEmpty(), - "BuildConfig should not have limits when only request is set"); - - // Cleanup - Files.delete(tempFileWithRequest); - } finally { - System.clearProperty(BuildManagerConfig.MEMORY_REQUEST); - XTFConfig.loadConfig(); - } - } - - @Test - public void testBuildConfig_WhenOnlyMemoryLimitSet_ShouldContainOnlyLimit() throws IOException { - // Given: System property configured for memory limit only - String memoryLimit = "1Gi"; - - System.setProperty(BuildManagerConfig.MEMORY_LIMIT, memoryLimit); - XTFConfig.loadConfig(); - - try { - Path tempFileWithLimit = Files.createTempFile("test-with-limit", ".war"); - Files.write(tempFileWithLimit, "test content".getBytes()); - - BinaryBuildFromFile buildWithLimit = new BinaryBuildFromFile( - TEST_BUILDER_IMAGE, - tempFileWithLimit, - null, - TEST_BUILD_ID + "-with-limit"); - - // When: Getting the BuildConfig - BuildConfig buildConfig = buildWithLimit.bc; - - // Then: BuildConfig should contain only resource limits - Assertions.assertNotNull(buildConfig.getSpec().getResources(), - "BuildConfig should have resources set when memory limit is configured"); - - Assertions.assertTrue(buildConfig.getSpec().getResources().getRequests() == null - || buildConfig.getSpec().getResources().getRequests().isEmpty(), - "BuildConfig should not have requests when only limit is set"); - - Assertions.assertNotNull(buildConfig.getSpec().getResources().getLimits(), - "BuildConfig should have resource limits"); - Assertions.assertEquals(memoryLimit, - buildConfig.getSpec().getResources().getLimits().get("memory").toString(), - "Memory limit should match configured value"); - - // Cleanup - Files.delete(tempFileWithLimit); - } finally { - System.clearProperty(BuildManagerConfig.MEMORY_LIMIT); - XTFConfig.loadConfig(); - } - } - - @Test - public void testBuildConfig_WhenMemoryLimitsNotSet_ShouldNotContainResources() { - // Given: BinaryBuild without memory configuration (current setup in @BeforeEach) - - // When: Getting the BuildConfig - BuildConfig buildConfig = binaryBuild.bc; - // Then: BuildConfig should not contain resource requirements - Assertions.assertNull(buildConfig.getSpec().getResources(), - "BuildConfig should not have resources when memory limits are not configured"); + verify(openShift).getBuild(TEST_BUILD_ID + "-1"); } // Helper methods to create test resources @@ -309,30 +186,6 @@ private ImageStream createImageStream(String name) { .build(); } - private BuildConfig createBuildConfig(String name, long lastVersion) { - return new BuildConfigBuilder() - .withMetadata(new ObjectMetaBuilder() - .withName(name) - .addToLabels("xtf.bm/content-hash", "differenthash") - .build()) - .withNewSpec() - .withNewStrategy() - .withType("Source") - .withNewSourceStrategy() - .withForcePull(true) - .withNewFrom() - .withKind("DockerImage") - .withName(TEST_BUILDER_IMAGE) - .endFrom() - .endSourceStrategy() - .endStrategy() - .endSpec() - .withNewStatus() - .withLastVersion(lastVersion) - .endStatus() - .build(); - } - private BuildConfig createBuildConfigWithContentHash(String name, long lastVersion) { // Get the actual content hash from the BinaryBuild String contentHash = binaryBuild.getContentHash(); @@ -370,4 +223,19 @@ private Build createBuildWithStatus(String name, String phase) { .build()) .build(); } + + private void setupResourceMocks(BuildConfig buildConfig, ImageStream imageStream) { + // Mock the fluent API chains properly + @SuppressWarnings("unchecked") + BuildConfigResource buildConfigResource = mock(BuildConfigResource.class); + @SuppressWarnings("unchecked") + Resource imageStreamResource = mock(Resource.class); + + // Tell mocks what to return - chain each method call + when(buildConfigOp.withName(TEST_BUILD_ID)).thenReturn(buildConfigResource); + when(buildConfigResource.get()).thenReturn(buildConfig); + + when(imageStreamOp.withName(TEST_BUILD_ID)).thenReturn(imageStreamResource); + when(imageStreamResource.get()).thenReturn(imageStream); + } } diff --git a/junit5/src/main/java/cz/xtf/junit5/model/DockerImageMetadata.java b/junit5/src/main/java/cz/xtf/junit5/model/DockerImageMetadata.java index f475f57e..d09c7451 100644 --- a/junit5/src/main/java/cz/xtf/junit5/model/DockerImageMetadata.java +++ b/junit5/src/main/java/cz/xtf/junit5/model/DockerImageMetadata.java @@ -18,6 +18,7 @@ import cz.xtf.core.waiting.SimpleWaiter; import cz.xtf.core.waiting.Waiter; import cz.xtf.core.waiting.failfast.FailFastCheck; +import io.fabric8.kubernetes.api.model.GenericKubernetesResource; import io.fabric8.openshift.api.model.ImageStream; import io.fabric8.openshift.api.model.ImageStreamTag; import io.fabric8.openshift.api.model.NamedTagEventList; @@ -107,14 +108,26 @@ private static DockerImageMetadata getMetadata(OpenShift openShift, Image image) } private static DockerImageMetadata getMetadataFromTag(ImageStreamTag imageStreamTag) { - return areMetadataForImageReady(imageStreamTag) - ? new DockerImageMetadata((Map) imageStreamTag.getImage().getDockerImageMetadata().getValue()) - : null; + if (!areMetadataForImageReady(imageStreamTag)) { + return null; + } + + Object dockerImageMetadata = imageStreamTag.getImage().getDockerImageMetadata(); + + if (!(dockerImageMetadata instanceof GenericKubernetesResource)) { + throw new IllegalStateException(String.format( + "Fabric8 API compatibility issue: Expected GenericKubernetesResource from " + + "Image.getDockerImageMetadata() but got %s. This likely indicates a Fabric8 " + + "OpenShift Client version incompatibility.", + dockerImageMetadata != null ? dockerImageMetadata.getClass().getName() : "null")); + } + + return new DockerImageMetadata( + ((GenericKubernetesResource) dockerImageMetadata).getAdditionalProperties()); } private static boolean areMetadataForImageReady(ImageStreamTag tag) { - return tag != null && tag.getImage() != null && tag.getImage().getDockerImageMetadata() != null - && tag.getImage().getDockerImageMetadata().getValue() != null; + return tag != null && tag.getImage() != null && tag.getImage().getDockerImageMetadata() != null; } private static String randomString() { diff --git a/pom.xml b/pom.xml index d5978ff5..43edeb07 100644 --- a/pom.xml +++ b/pom.xml @@ -56,12 +56,12 @@ UTF-8 - 1.8 - 1.8 + 11 + 11 4.5.13 - 6.14.0 + 7.4.0 2.15.1 3.11 1.26.0 @@ -76,6 +76,7 @@ 5.7.0 5.7.0 2.0.1 + 5.8.0 3.17.2 1.18.30 2.8.9 @@ -184,6 +185,20 @@ ${version.junit5.jupiter.params} + + org.mockito + mockito-core + ${version.mockito} + + + + org.mockito + mockito-junit-jupiter + ${version.mockito} + + + + org.assertj assertj-core @@ -222,12 +237,6 @@ ${version.guava} - - io.fabric8 - openshift-server-mock - ${version.openshift-client} - - org.eclipse.jgit org.eclipse.jgit