From 14b23b52f2cd9fc039091f3e7a890234d2bb875c Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Fri, 29 May 2026 14:48:06 +0300 Subject: [PATCH 1/3] Add health-check-interval MTA module parameter Introduce the liveness health check interval parameter end-to-end: - SupportedParameters: new HEALTH_CHECK_INTERVAL constant in MODULE_PARAMETERS - Messages: INVALID_HEALTH_CHECK_INTERVAL validation error message - StagingParametersParser: parse, validate (must be > 0), and forward to ImmutableStaging builder - Staging interface + CloudProcess: getHealthCheckInterval() accessors (Immutables regenerates) - RawCloudProcess: map Data.getInterval() from CF API response into CloudProcess - HealthCheckInfo: add interval field, getter, and equals check for change detection - CloudControllerRestClientImpl: forward interval to Data builder; widen updateApplicationProcess guard to also fire when interval is non-null; guard buildHealthCheck against HealthCheckType.from(null) NPE JIRA:LMCROSSITXSADEPLOY-3316 --- .../facade/adapters/RawCloudProcess.java | 3 +++ .../client/facade/domain/CloudProcess.java | 3 +++ .../client/facade/domain/Staging.java | 6 +++++ .../rest/CloudControllerRestClientImpl.java | 22 ++++++++++--------- .../client/lib/domain/HealthCheckInfo.java | 17 ++++++++++---- .../multiapps/controller/core/Messages.java | 1 + .../core/model/SupportedParameters.java | 2 ++ .../core/parser/StagingParametersParser.java | 12 ++++++++++ 8 files changed, 52 insertions(+), 14 deletions(-) diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java index 4bd4b83775..931f528b75 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcess.java @@ -23,11 +23,13 @@ public CloudProcess derive() { Integer healthCheckTimeout = null; String healthCheckHttpEndpoint = null; Integer healthCheckInvocationTimeout = null; + Integer healthCheckInterval = null; if (healthCheck.getData() != null) { Data healthCheckData = healthCheck.getData(); healthCheckTimeout = healthCheckData.getTimeout(); healthCheckInvocationTimeout = healthCheckData.getInvocationTimeout(); healthCheckHttpEndpoint = healthCheckData.getEndpoint(); + healthCheckInterval = healthCheckData.getInterval(); } Integer readinessHealthCheckInvocationTimeout = null; String readinessHealthCheckHttpEndpoint = null; @@ -49,6 +51,7 @@ public CloudProcess derive() { .healthCheckHttpEndpoint(healthCheckHttpEndpoint) .healthCheckTimeout(healthCheckTimeout) .healthCheckInvocationTimeout(healthCheckInvocationTimeout) + .healthCheckInterval(healthCheckInterval) .readinessHealthCheckType(readinessHealthCheckType.getType() .getValue()) .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java index 86df6003a7..6dde60e414 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/facade/domain/CloudProcess.java @@ -32,6 +32,9 @@ public abstract class CloudProcess extends CloudEntity implements Derivable routes, UUID applicationGuid) { diff --git a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfo.java b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfo.java index 11bc0d49ec..2990f21a98 100644 --- a/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfo.java +++ b/multiapps-controller-client/src/main/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfo.java @@ -11,12 +11,14 @@ public class HealthCheckInfo { private final Integer timeout; private final Integer invocationTimeout; private final String httpEndpoint; + private final Integer interval; - private HealthCheckInfo(String type, Integer timeout, Integer invocationTimeout, String httpEndpoint) { + private HealthCheckInfo(String type, Integer timeout, Integer invocationTimeout, String httpEndpoint, Integer interval) { this.type = type; this.timeout = timeout; this.invocationTimeout = invocationTimeout; this.httpEndpoint = httpEndpoint; + this.interval = interval; } public static HealthCheckInfo fromStaging(Staging staging) { @@ -27,7 +29,8 @@ public static HealthCheckInfo fromStaging(Staging staging) { var timeout = staging.getHealthCheckTimeout(); var invocationTimeout = staging.getInvocationTimeout(); var httpEndpoint = staging.getHealthCheckHttpEndpoint(); - return new HealthCheckInfo(type, timeout, invocationTimeout, httpEndpoint); + var interval = staging.getHealthCheckInterval(); + return new HealthCheckInfo(type, timeout, invocationTimeout, httpEndpoint, interval); } public static HealthCheckInfo fromProcess(CloudProcess process) { @@ -35,7 +38,8 @@ public static HealthCheckInfo fromProcess(CloudProcess process) { var timeout = process.getHealthCheckTimeout(); var invocationTimeout = process.getHealthCheckInvocationTimeout(); var httpEndpoint = process.getHealthCheckHttpEndpoint(); - return new HealthCheckInfo(type.toString(), timeout, invocationTimeout, httpEndpoint); + var interval = process.getHealthCheckInterval(); + return new HealthCheckInfo(type.toString(), timeout, invocationTimeout, httpEndpoint, interval); } public String getType() { @@ -54,6 +58,10 @@ public String getHttpEndpoint() { return httpEndpoint; } + public Integer getInterval() { + return interval; + } + @Override public boolean equals(Object obj) { if (obj == this) { @@ -66,6 +74,7 @@ public boolean equals(Object obj) { return Objects.equals(getType(), other.getType()) && Objects.equals(getTimeout(), other.getTimeout()) && Objects.equals(getInvocationTimeout(), other.getInvocationTimeout()) - && Objects.equals(getHttpEndpoint(), other.getHttpEndpoint()); + && Objects.equals(getHttpEndpoint(), other.getHttpEndpoint()) + && Objects.equals(getInterval(), other.getInterval()); } } diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java index 6efb7e7b3d..127cd131ba 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/Messages.java @@ -88,6 +88,7 @@ public final class Messages { public static final String ERROR_OCCURRED_WHILE_CHECKING_DATABASE_INSTANCE_0 = "Error occurred while checking database instance: \"{0}\""; public static final String DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE = "Docker information must not be provided when lifecycle is set to \"{0}\""; public static final String UNSUPPORTED_LIFECYCLE_VALUE = "Unsupported lifecycle value: \"{0}\""; + public static final String INVALID_HEALTH_CHECK_INTERVAL = "Parameter \"health-check-interval\" must be a positive integer greater than 0, but was: {0}"; public static final String BUILDPACKS_REQUIRED_FOR_CNB = "Buildpacks must be provided when lifecycle is set to 'cnb'."; public static final String DOCKER_INFO_REQUIRED = "Docker information must be provided when lifecycle is set to 'docker'."; public static final String BUILDPACKS_NOT_ALLOWED_WITH_DOCKER = "Buildpacks must not be provided when lifecycle is set to 'docker'."; diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/model/SupportedParameters.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/model/SupportedParameters.java index 1e84c0acd4..ad2f15c1c7 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/model/SupportedParameters.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/model/SupportedParameters.java @@ -67,6 +67,7 @@ public class SupportedParameters { public static final String READINESS_HEALTH_CHECK_HTTP_ENDPOINT = "readiness-health-check-http-endpoint"; public static final String READINESS_HEALTH_CHECK_INVOCATION_TIMEOUT = "readiness-health-check-invocation-timeout"; public static final String READINESS_HEALTH_CHECK_INTERVAL = "readiness-health-check-interval"; + public static final String HEALTH_CHECK_INTERVAL = "health-check-interval"; public static final String UPLOAD_TIMEOUT = "upload-timeout"; public static final String STAGE_TIMEOUT = "stage-timeout"; public static final String START_TIMEOUT = "start-timeout"; @@ -190,6 +191,7 @@ public class SupportedParameters { DEPENDENCY_TYPE, DISK_QUOTA, DOCKER, DOMAIN, DOMAINS, DEFAULT_DOMAIN, ENABLE_SSH, ENABLE_PARALLEL_SERVICE_BINDINGS, HEALTH_CHECK_HTTP_ENDPOINT, HEALTH_CHECK_TIMEOUT, HEALTH_CHECK_INVOCATION_TIMEOUT, HEALTH_CHECK_TYPE, + HEALTH_CHECK_INTERVAL, HOST, HOSTS, IDLE_DOMAIN, IDLE_DOMAINS, IDLE_HOST, IDLE_HOSTS, IDLE_ROUTES, INSTANCES, KEEP_EXISTING_APPLICATION_ATTRIBUTES_UPDATE_STRATEGY, KEEP_EXISTING_ROUTES, MEMORY, NO_ROUTE, NO_START, RESTART_ON_ENV_CHANGE, diff --git a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParser.java b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParser.java index 14d6021864..e9f838d1d6 100644 --- a/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParser.java +++ b/multiapps-controller-core/src/main/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParser.java @@ -20,6 +20,7 @@ import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_REQUIRED_FOR_CNB; import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE; import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_REQUIRED; +import static org.cloudfoundry.multiapps.controller.core.Messages.INVALID_HEALTH_CHECK_INTERVAL; import static org.cloudfoundry.multiapps.controller.core.Messages.UNSUPPORTED_LIFECYCLE_VALUE; public class StagingParametersParser implements ParametersParser { @@ -53,6 +54,9 @@ public Staging parse(List> parametersList) { Integer readinessHealthCheckInterval = (Integer) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.READINESS_HEALTH_CHECK_INTERVAL, null); + Integer healthCheckInterval = (Integer) PropertiesUtil.getPropertyValue(parametersList, + SupportedParameters.HEALTH_CHECK_INTERVAL, + null); Boolean isSshEnabled = (Boolean) PropertiesUtil.getPropertyValue(parametersList, SupportedParameters.ENABLE_SSH, null); Map appFeatures = getAppFeatures(parametersList); overrideSshFeatureIfMissing(appFeatures, isSshEnabled); @@ -60,6 +64,7 @@ public Staging parse(List> parametersList) { LifecycleType lifecycleType = parseLifecycleType(parametersList); validateLifecycleType(lifecycleType, buildpacks, dockerInfo); + validateHealthCheckInterval(healthCheckInterval); return ImmutableStaging.builder() .command(command) @@ -69,6 +74,7 @@ public Staging parse(List> parametersList) { .invocationTimeout(healthCheckInvocationTimeout) .healthCheckType(healthCheckType) .healthCheckHttpEndpoint(healthCheckHttpEndpoint) + .healthCheckInterval(healthCheckInterval) .readinessHealthCheckType(readinessHealthCheckType) .readinessHealthCheckHttpEndpoint(readinessHealthCheckHttpEndpoint) .readinessHealthCheckInvocationTimeout(readinessHealthCheckInvocationTimeout) @@ -141,4 +147,10 @@ private String getDefaultHealthCheckHttpEndpoint(String healthCheckType) { return HTTP_HEALTH_CHECK_TYPE.equals(healthCheckType) ? DEFAULT_HEALTH_CHECK_HTTP_ENDPOINT : null; } + private void validateHealthCheckInterval(Integer healthCheckInterval) { + if (healthCheckInterval != null && healthCheckInterval <= 0) { + throw new ContentException(MessageFormat.format(INVALID_HEALTH_CHECK_INTERVAL, healthCheckInterval)); + } + } + } From 23f6cc5cfdc874240a5049de2a2bb016c66896bb Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Fri, 29 May 2026 14:50:21 +0300 Subject: [PATCH 2/3] Add unit tests for health-check-interval parsing and equality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - StagingParametersParserTest: three new tests — correct parse (interval=15), validation rejection (interval=0 throws ContentException), and null when absent - HealthCheckInfoTest: four tests covering equal instances, different intervals, null-vs-non-null, and fromProcess/fromStaging cross-equality JIRA:LMCROSSITXSADEPLOY-3316 --- .../lib/domain/HealthCheckInfoTest.java | 79 +++++++++++++++++++ .../parser/StagingParametersParserTest.java | 27 +++++++ 2 files changed, 106 insertions(+) create mode 100644 multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfoTest.java diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfoTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfoTest.java new file mode 100644 index 0000000000..4d781214d3 --- /dev/null +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/lib/domain/HealthCheckInfoTest.java @@ -0,0 +1,79 @@ +package org.cloudfoundry.multiapps.controller.client.lib.domain; + +import org.cloudfoundry.multiapps.controller.client.facade.domain.HealthCheckType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableCloudProcess; +import org.cloudfoundry.multiapps.controller.client.facade.domain.ImmutableStaging; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; + +class HealthCheckInfoTest { + + @Test + void testEqualHealthCheckInfoObjectsAreEqual() { + ImmutableStaging staging = ImmutableStaging.builder() + .healthCheckType("http") + .healthCheckInterval(15) + .build(); + HealthCheckInfo first = HealthCheckInfo.fromStaging(staging); + HealthCheckInfo second = HealthCheckInfo.fromStaging(staging); + + assertEquals(first, second); + } + + @Test + void testHealthCheckInfoWithDifferentIntervalsAreNotEqual() { + ImmutableStaging stagingWithInterval15 = ImmutableStaging.builder() + .healthCheckType("http") + .healthCheckInterval(15) + .build(); + ImmutableStaging stagingWithInterval30 = ImmutableStaging.builder() + .healthCheckType("http") + .healthCheckInterval(30) + .build(); + + HealthCheckInfo first = HealthCheckInfo.fromStaging(stagingWithInterval15); + HealthCheckInfo second = HealthCheckInfo.fromStaging(stagingWithInterval30); + + assertNotEquals(first, second); + } + + @Test + void testHealthCheckInfoIntervalNullVsNonNullAreNotEqual() { + ImmutableStaging stagingWithInterval = ImmutableStaging.builder() + .healthCheckType("http") + .healthCheckInterval(15) + .build(); + ImmutableStaging stagingWithoutInterval = ImmutableStaging.builder() + .healthCheckType("http") + .build(); + + HealthCheckInfo withInterval = HealthCheckInfo.fromStaging(stagingWithInterval); + HealthCheckInfo withoutInterval = HealthCheckInfo.fromStaging(stagingWithoutInterval); + + assertNotEquals(withInterval, withoutInterval); + } + + @Test + void testHealthCheckInfoFromProcessWithInterval() { + ImmutableCloudProcess process = ImmutableCloudProcess.builder() + .command("start") + .diskInMb(256) + .instances(1) + .memoryInMb(512) + .healthCheckType(HealthCheckType.HTTP) + .healthCheckInterval(15) + .build(); + + HealthCheckInfo fromProcess = HealthCheckInfo.fromProcess(process); + + ImmutableStaging staging = ImmutableStaging.builder() + .healthCheckType("http") + .healthCheckInterval(15) + .build(); + HealthCheckInfo fromStaging = HealthCheckInfo.fromStaging(staging); + + assertEquals(fromProcess, fromStaging); + } +} diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java index 98677fc307..7dd010a531 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java @@ -17,10 +17,12 @@ import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_REQUIRED_FOR_CNB; import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_NOT_ALLOWED_WITH_LIFECYCLE; import static org.cloudfoundry.multiapps.controller.core.Messages.DOCKER_INFO_REQUIRED; +import static org.cloudfoundry.multiapps.controller.core.Messages.INVALID_HEALTH_CHECK_INTERVAL; import static org.cloudfoundry.multiapps.controller.core.Messages.UNSUPPORTED_LIFECYCLE_VALUE; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACK; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.BUILDPACKS; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.DOCKER; +import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.HEALTH_CHECK_INTERVAL; import static org.cloudfoundry.multiapps.controller.core.model.SupportedParameters.LIFECYCLE; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -138,6 +140,31 @@ void testValidateWithAllParametersMissing() { assertNull(staging.getDockerInfo()); } + @Test + void testHealthCheckIntervalIsParsedCorrectly() { + parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, 15)); + + Staging staging = parser.parse(parametersList); + + assertNotNull(staging); + assertEquals(15, staging.getHealthCheckInterval()); + } + + @Test + void testHealthCheckIntervalValidationRejectsNonPositiveValue() { + parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, 0)); + + ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList)); + assertEquals(MessageFormat.format(INVALID_HEALTH_CHECK_INTERVAL, 0), exception.getMessage()); + } + + @Test + void testHealthCheckIntervalIsNullWhenAbsent() { + Staging staging = parser.parse(parametersList); + + assertNull(staging.getHealthCheckInterval()); + } + private static Map mapOf(String key, Object value) { return Collections.singletonMap(key, value); } From fe7b56add19f021efed65feb86df45d9cabe5858 Mon Sep 17 00:00:00 2001 From: Krasimir Kargov Date: Fri, 29 May 2026 15:12:49 +0300 Subject: [PATCH 3/3] Add parameterized health-check-interval parser tests and RawCloudProcess test coverage - StagingParametersParserTest: parameterized test covering positive interval values (1, 15, 60, Integer.MAX_VALUE) - RawCloudProcessTest: new test class covering RawCloudProcess.derive() mapping including the new health-check interval propagation from the CF API response JIRA:LMCROSSITXSADEPLOY-3316 --- .../facade/adapters/RawCloudProcessTest.java | 118 ++++++++++++++++++ .../parser/StagingParametersParserTest.java | 31 +++++ 2 files changed, 149 insertions(+) create mode 100644 multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java diff --git a/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java new file mode 100644 index 0000000000..c6f7bb2a37 --- /dev/null +++ b/multiapps-controller-client/src/test/java/org/cloudfoundry/multiapps/controller/client/facade/adapters/RawCloudProcessTest.java @@ -0,0 +1,118 @@ +package org.cloudfoundry.multiapps.controller.client.facade.adapters; + +import org.cloudfoundry.client.v3.Metadata; +import org.cloudfoundry.client.v3.Relationship; +import org.cloudfoundry.client.v3.ToOneRelationship; +import org.cloudfoundry.client.v3.processes.Data; +import org.cloudfoundry.client.v3.processes.HealthCheck; +import org.cloudfoundry.client.v3.processes.HealthCheckType; +import org.cloudfoundry.client.v3.processes.ProcessRelationships; +import org.cloudfoundry.client.v3.processes.ProcessResource; +import org.cloudfoundry.client.v3.processes.ReadinessHealthCheck; +import org.cloudfoundry.client.v3.processes.ReadinessHealthCheckType; +import org.cloudfoundry.multiapps.controller.client.facade.domain.CloudProcess; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; + +class RawCloudProcessTest { + + private static final String PROCESS_ID = "0d6b6f94-2e9b-4f62-9c3a-bdf9a7e4d1d0"; + private static final String CREATED_AT = "2024-01-26T10:00:00Z"; + private static final String COMMAND = "start"; + private static final Integer INSTANCES = 2; + private static final Integer MEMORY_IN_MB = 256; + private static final Integer DISK_IN_MB = 1024; + private static final Integer HEALTH_CHECK_TIMEOUT = 60; + private static final Integer HEALTH_CHECK_INVOCATION_TIMEOUT = 5; + private static final String HEALTH_CHECK_HTTP_ENDPOINT = "/health"; + private static final Integer HEALTH_CHECK_INTERVAL = 15; + + @Test + void testDeriveMapsHealthCheckIntervalFromData() { + RawCloudProcess raw = buildRawProcess(buildHealthCheck(HEALTH_CHECK_INTERVAL), buildReadinessHealthCheck()); + + CloudProcess derived = raw.derive(); + + assertEquals(HEALTH_CHECK_INTERVAL, derived.getHealthCheckInterval()); + assertEquals(HEALTH_CHECK_TIMEOUT, derived.getHealthCheckTimeout()); + assertEquals(HEALTH_CHECK_INVOCATION_TIMEOUT, derived.getHealthCheckInvocationTimeout()); + assertEquals(HEALTH_CHECK_HTTP_ENDPOINT, derived.getHealthCheckHttpEndpoint()); + } + + @Test + void testDeriveLeavesHealthCheckIntervalNullWhenAbsentFromData() { + HealthCheck healthCheck = HealthCheck.builder() + .type(HealthCheckType.HTTP) + .data(Data.builder() + .timeout(HEALTH_CHECK_TIMEOUT) + .invocationTimeout(HEALTH_CHECK_INVOCATION_TIMEOUT) + .endpoint(HEALTH_CHECK_HTTP_ENDPOINT) + .build()) + .build(); + RawCloudProcess raw = buildRawProcess(healthCheck, buildReadinessHealthCheck()); + + CloudProcess derived = raw.derive(); + + assertNull(derived.getHealthCheckInterval()); + assertEquals(HEALTH_CHECK_TIMEOUT, derived.getHealthCheckTimeout()); + } + + @Test + void testDeriveLeavesHealthCheckIntervalNullWhenDataIsNull() { + HealthCheck healthCheck = HealthCheck.builder() + .type(HealthCheckType.PORT) + .build(); + RawCloudProcess raw = buildRawProcess(healthCheck, buildReadinessHealthCheck()); + + CloudProcess derived = raw.derive(); + + assertNull(derived.getHealthCheckInterval()); + assertNull(derived.getHealthCheckTimeout()); + assertNull(derived.getHealthCheckInvocationTimeout()); + assertNull(derived.getHealthCheckHttpEndpoint()); + } + + private static HealthCheck buildHealthCheck(Integer interval) { + return HealthCheck.builder() + .type(HealthCheckType.HTTP) + .data(Data.builder() + .timeout(HEALTH_CHECK_TIMEOUT) + .invocationTimeout(HEALTH_CHECK_INVOCATION_TIMEOUT) + .endpoint(HEALTH_CHECK_HTTP_ENDPOINT) + .interval(interval) + .build()) + .build(); + } + + private static ReadinessHealthCheck buildReadinessHealthCheck() { + return ReadinessHealthCheck.builder() + .type(ReadinessHealthCheckType.PROCESS) + .build(); + } + + private static RawCloudProcess buildRawProcess(HealthCheck healthCheck, ReadinessHealthCheck readinessHealthCheck) { + ProcessResource processResource = ProcessResource.builder() + .id(PROCESS_ID) + .createdAt(CREATED_AT) + .type("web") + .command(COMMAND) + .instances(INSTANCES) + .memoryInMb(MEMORY_IN_MB) + .diskInMb(DISK_IN_MB) + .healthCheck(healthCheck) + .readinessHealthCheck(readinessHealthCheck) + .metadata(Metadata.builder() + .build()) + .relationships(ProcessRelationships.builder() + .app(ToOneRelationship.builder() + .data(Relationship.builder() + .id("app-guid") + .build()) + .build()) + .build()) + .build(); + return ImmutableRawCloudProcess.of(processResource); + } +} diff --git a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java index 7dd010a531..e6b749dace 100644 --- a/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java +++ b/multiapps-controller-core/src/test/java/org/cloudfoundry/multiapps/controller/core/parser/StagingParametersParserTest.java @@ -5,12 +5,16 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import org.cloudfoundry.multiapps.common.ContentException; import org.cloudfoundry.multiapps.controller.client.facade.domain.LifecycleType; import org.cloudfoundry.multiapps.controller.client.facade.domain.Staging; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; import org.springframework.util.CollectionUtils; import static org.cloudfoundry.multiapps.controller.core.Messages.BUILDPACKS_NOT_ALLOWED_WITH_DOCKER; @@ -150,6 +154,20 @@ void testHealthCheckIntervalIsParsedCorrectly() { assertEquals(15, staging.getHealthCheckInterval()); } + static Stream testHealthCheckIntervalAcceptsPositiveValues() { + return Stream.of(Arguments.of(1), Arguments.of(15), Arguments.of(60), Arguments.of(Integer.MAX_VALUE)); + } + + @ParameterizedTest + @MethodSource + void testHealthCheckIntervalAcceptsPositiveValues(int interval) { + parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, interval)); + + Staging staging = parser.parse(parametersList); + + assertEquals(interval, staging.getHealthCheckInterval()); + } + @Test void testHealthCheckIntervalValidationRejectsNonPositiveValue() { parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, 0)); @@ -158,6 +176,19 @@ void testHealthCheckIntervalValidationRejectsNonPositiveValue() { assertEquals(MessageFormat.format(INVALID_HEALTH_CHECK_INTERVAL, 0), exception.getMessage()); } + static Stream testHealthCheckIntervalValidationRejectsNonPositiveValues() { + return Stream.of(Arguments.of(0), Arguments.of(-1), Arguments.of(-15), Arguments.of(Integer.MIN_VALUE)); + } + + @ParameterizedTest + @MethodSource + void testHealthCheckIntervalValidationRejectsNonPositiveValues(int interval) { + parametersList.add(mapOf(HEALTH_CHECK_INTERVAL, interval)); + + ContentException exception = assertThrows(ContentException.class, () -> parser.parse(parametersList)); + assertEquals(MessageFormat.format(INVALID_HEALTH_CHECK_INTERVAL, interval), exception.getMessage()); + } + @Test void testHealthCheckIntervalIsNullWhenAbsent() { Staging staging = parser.parse(parametersList);