diff --git a/backend/src/main/scala/cromwell/backend/validation/RuntimeAttributesValidation.scala b/backend/src/main/scala/cromwell/backend/validation/RuntimeAttributesValidation.scala index 5fa52dac53a..a5febfd88e2 100644 --- a/backend/src/main/scala/cromwell/backend/validation/RuntimeAttributesValidation.scala +++ b/backend/src/main/scala/cromwell/backend/validation/RuntimeAttributesValidation.scala @@ -84,6 +84,8 @@ object RuntimeAttributesValidation { override protected def validateValue: PartialFunction[WomValue, ErrorOr[ValidatedType]] = validation.validateValuePackagePrivate + override protected def validateNone: ErrorOr[ValidatedType] = validateValue(default) + override protected def validateExpression: PartialFunction[WomValue, Boolean] = validation.validateExpressionPackagePrivate @@ -108,6 +110,8 @@ object RuntimeAttributesValidation { override protected def validateValue: PartialFunction[WomValue, ErrorOr[ValidatedType]] = validation.validateValuePackagePrivate + override protected def validateNone: ErrorOr[ValidatedType] = validation.validateNone + override protected def validateExpression: PartialFunction[WomValue, Boolean] = validation.validateExpressionPackagePrivate @@ -482,5 +486,9 @@ trait OptionalRuntimeAttributesValidation[ValidatedType] extends RuntimeAttribut validateOption.apply(womValue).map(Option.apply) } - final override protected lazy val validateNone: ErrorOr[None.type] = None.validNel[String] + final override protected lazy val validateNone: ErrorOr[Option[ValidatedType]] = + staticDefaultOption match { + case Some(default) => validateValue(default) + case None => None.validNel[String] + } } diff --git a/supportedBackends/sfs/src/test/scala/cromwell/backend/impl/sfs/config/ConfigInitializationActorSpec.scala b/supportedBackends/sfs/src/test/scala/cromwell/backend/impl/sfs/config/ConfigInitializationActorSpec.scala index 085ac5cd974..a86cf3cc7ef 100644 --- a/supportedBackends/sfs/src/test/scala/cromwell/backend/impl/sfs/config/ConfigInitializationActorSpec.scala +++ b/supportedBackends/sfs/src/test/scala/cromwell/backend/impl/sfs/config/ConfigInitializationActorSpec.scala @@ -4,8 +4,18 @@ import com.typesafe.config.ConfigFactory import common.assertion.CromwellTimeoutSpec import org.scalatest.flatspec.AnyFlatSpec import org.scalatest.matchers.should.Matchers +import org.scalatest.prop.{TableDrivenPropertyChecks, TableFor4} +import wdl4s.parser.MemoryUnit +import wom.format.MemorySize +import wom.values.{WomString, WomValue} +import cats.syntax.validated._ +import common.validation.ErrorOr._ -class ConfigInitializationActorSpec extends AnyFlatSpec with CromwellTimeoutSpec with Matchers { +class ConfigInitializationActorSpec + extends AnyFlatSpec + with CromwellTimeoutSpec + with Matchers + with TableDrivenPropertyChecks { it should "read the list of call cache attributes from config" in { val tripleQuote = "\"\"\"" @@ -45,4 +55,64 @@ class ConfigInitializationActorSpec extends AnyFlatSpec with CromwellTimeoutSpec } + private val attributeValidationTests: TableFor4[String, String, Map[String, WomValue], ErrorOr[_]] = Table( + ("description", "declaration", "womValue", "expected"), + ("present required custom", "Int my_attr", Map("my_attr" -> WomString("1")), 1.valid), + ("missing required custom", + "Int my_attr", + Map(), + "Expecting my_attr runtime attribute to be an Integer".invalidNel + ), + ("present defaulted custom", "Int my_attr = 415", Map("my_attr" -> WomString("1")), 1.valid), + ("missing defaulted custom", "Int my_attr = 415", Map(), 415.valid), + ("present optional custom", "Int? my_attr", Map("my_attr" -> WomString("1")), Option(1).valid), + ("missing optional custom", "Int? my_attr", Map(), None.valid), + ("present defaulted optional custom", "Int? my_attr = 415", Map("my_attr" -> WomString("1")), Option(1).valid), + ("missing defaulted optional custom", "Int? my_attr = 415", Map(), Option(415).valid), + ("present required memory", + "Float memory_gb", + Map("memory" -> WomString("3 GB")), + MemorySize(3, MemoryUnit.GB).valid + ), + ("missing required memory", + "Float memory_gb", + Map(), + "Expecting memory runtime attribute to be an Integer or String with format '8 GB'. Exception: Not supported WDL type value".invalidNel + ), + ("present defaulted memory", + "Float memory_gb = 1", + Map("memory" -> WomString("3 GB")), + MemorySize(3, MemoryUnit.GB).valid + ), + ("missing defaulted memory", "Float memory_gb = 1", Map(), MemorySize(1, MemoryUnit.GB).valid), + ("present optional memory", + "Float? memory_gb", + Map("memory" -> WomString("3 GB")), + Option(MemorySize(3, MemoryUnit.GB)).valid + ), + ("missing optional memory", "Float? memory_gb", Map(), None.valid), + ("present defaulted optional memory", + "Float? memory_gb = 1", + Map("memory" -> WomString("3 GB")), + Option(MemorySize(3, MemoryUnit.GB)).valid + ), + ("missing defaulted optional memory", "Float? memory_gb = 1", Map(), Option(MemorySize(1, MemoryUnit.GB)).valid) + ) + forAll(attributeValidationTests) { (description, declaration, values, expected) => + it should s"validate a $description attribute" in { + val backendConfig = ConfigFactory.parseString( + s"""submit: "unused" + |runtime-attributes: "$declaration" + |""".stripMargin + ) + lazy val configWdlNamespace = new ConfigWdlNamespace(backendConfig) + val validations = DeclarationValidation.fromDeclarations( + configWdlNamespace.runtimeDeclarations, + configWdlNamespace.callCachedRuntimeAttributes + ) + val validation = validations.head.makeValidation() + val actual = validation.validate(values) + actual should be(expected) + } + } }