mirror of
https://github.com/digital-asset/daml.git
synced 2024-11-10 00:35:25 +03:00
ledger-configuration: Add tests for the Configuration
decoding methods. [KVL-1002] (#10293)
* ledger-configuration: Add tests for Configuration. CHANGELOG_BEGIN CHANGELOG_END * ledger-configuration: Reject a negative maximum deduplication time. * ledger-configuration: Remove duplication in Configuration. * ledger-configuration: Make some Configuration methods private. Co-authored-by: Miklos <57664299+miklos-da@users.noreply.github.com> * ledger-configuration: Remove some extra braces. * ledger-configuration: Use tables to simplify rejection tests. * ledger-configuration: Add a test for an unknown version. Co-authored-by: Miklos <57664299+miklos-da@users.noreply.github.com>
This commit is contained in:
parent
cb29f34d4b
commit
88886beb8d
@ -42,11 +42,13 @@ da_scala_test_suite(
|
||||
"@maven//:org_scalatest_scalatest",
|
||||
"@maven//:org_scalaz_scalaz_core",
|
||||
"@maven//:org_scala_lang_modules_scala_collection_compat",
|
||||
"@maven//:org_scala_lang_modules_scala_java8_compat",
|
||||
],
|
||||
deps = [
|
||||
":ledger-configuration",
|
||||
"//daml-lf/data",
|
||||
"//daml-lf/transaction",
|
||||
"//ledger-api/grpc-definitions:ledger_api_proto_scala",
|
||||
"//ledger/ledger-configuration/protobuf:ledger_configuration_proto_scala",
|
||||
],
|
||||
)
|
||||
|
@ -21,6 +21,7 @@ final case class Configuration(
|
||||
)
|
||||
|
||||
object Configuration {
|
||||
|
||||
import com.daml.ledger.participant.state.protobuf
|
||||
|
||||
/** Version history:
|
||||
@ -29,73 +30,6 @@ object Configuration {
|
||||
*/
|
||||
val protobufVersion: Long = 2L
|
||||
|
||||
def decode(bytes: Array[Byte]): Either[String, Configuration] =
|
||||
Try(protobuf.LedgerConfiguration.parseFrom(bytes)).toEither.left
|
||||
.map(_.getMessage)
|
||||
.flatMap(decode)
|
||||
|
||||
def decode(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
config.getVersion match {
|
||||
case 1 => DecodeV1.decode(config)
|
||||
case 2 => DecodeV2.decode(config)
|
||||
case v => Left(s"Unknown version: $v")
|
||||
}
|
||||
|
||||
private object DecodeV1 {
|
||||
|
||||
def decode(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
for {
|
||||
tm <-
|
||||
if (config.hasTimeModel)
|
||||
decodeTimeModel(config.getTimeModel)
|
||||
else
|
||||
Left("Missing time model")
|
||||
} yield {
|
||||
Configuration(
|
||||
generation = config.getGeneration,
|
||||
timeModel = tm,
|
||||
maxDeduplicationTime = Duration.ofDays(1),
|
||||
)
|
||||
}
|
||||
|
||||
def decodeTimeModel(tm: protobuf.LedgerTimeModel): Either[String, LedgerTimeModel] =
|
||||
LedgerTimeModel(
|
||||
avgTransactionLatency = parseDuration(tm.getAvgTransactionLatency),
|
||||
minSkew = parseDuration(tm.getMinSkew),
|
||||
maxSkew = parseDuration(tm.getMaxSkew),
|
||||
).toEither.left.map(e => s"decodeTimeModel: ${e.getMessage}")
|
||||
}
|
||||
|
||||
private object DecodeV2 {
|
||||
|
||||
def decode(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
for {
|
||||
tm <-
|
||||
if (config.hasTimeModel)
|
||||
decodeTimeModel(config.getTimeModel)
|
||||
else
|
||||
Left("Missing time model")
|
||||
maxDeduplicationTime <-
|
||||
if (config.hasMaxDeduplicationTime)
|
||||
Right(parseDuration(config.getMaxDeduplicationTime))
|
||||
else
|
||||
Left("Missing maximum command time to live")
|
||||
} yield {
|
||||
Configuration(
|
||||
generation = config.getGeneration,
|
||||
timeModel = tm,
|
||||
maxDeduplicationTime = maxDeduplicationTime,
|
||||
)
|
||||
}
|
||||
|
||||
def decodeTimeModel(tm: protobuf.LedgerTimeModel): Either[String, LedgerTimeModel] =
|
||||
LedgerTimeModel(
|
||||
avgTransactionLatency = parseDuration(tm.getAvgTransactionLatency),
|
||||
minSkew = parseDuration(tm.getMinSkew),
|
||||
maxSkew = parseDuration(tm.getMaxSkew),
|
||||
).toEither.left.map(e => s"decodeTimeModel: ${e.getMessage}")
|
||||
}
|
||||
|
||||
def encode(config: Configuration): protobuf.LedgerConfiguration = {
|
||||
val tm = config.timeModel
|
||||
protobuf.LedgerConfiguration.newBuilder
|
||||
@ -111,15 +45,74 @@ object Configuration {
|
||||
.build
|
||||
}
|
||||
|
||||
private def parseDuration(dur: com.google.protobuf.Duration): Duration = {
|
||||
Duration.ofSeconds(dur.getSeconds, dur.getNanos.toLong)
|
||||
}
|
||||
def decode(bytes: Array[Byte]): Either[String, Configuration] =
|
||||
Try(protobuf.LedgerConfiguration.parseFrom(bytes)).toEither.left
|
||||
.map(_.getMessage)
|
||||
.flatMap(decode)
|
||||
|
||||
private def buildDuration(dur: Duration): com.google.protobuf.Duration = {
|
||||
def decode(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
config.getVersion match {
|
||||
case 1 => decodeV1(config)
|
||||
case 2 => decodeV2(config)
|
||||
case v => Left(s"Unknown version: $v")
|
||||
}
|
||||
|
||||
private def decodeV1(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
for {
|
||||
tm <-
|
||||
if (config.hasTimeModel) {
|
||||
decodeTimeModel(config.getTimeModel)
|
||||
} else {
|
||||
Left("Missing time model")
|
||||
}
|
||||
} yield {
|
||||
Configuration(
|
||||
generation = config.getGeneration,
|
||||
timeModel = tm,
|
||||
maxDeduplicationTime = Duration.ofDays(1),
|
||||
)
|
||||
}
|
||||
|
||||
private def decodeV2(config: protobuf.LedgerConfiguration): Either[String, Configuration] =
|
||||
for {
|
||||
tm <-
|
||||
if (config.hasTimeModel) {
|
||||
decodeTimeModel(config.getTimeModel)
|
||||
} else {
|
||||
Left("Missing time model")
|
||||
}
|
||||
maxDeduplicationTime <-
|
||||
if (config.hasMaxDeduplicationTime) {
|
||||
val duration = parseDuration(config.getMaxDeduplicationTime)
|
||||
if (duration.isNegative) {
|
||||
Left("requirement failed: Negative maximum command time to live")
|
||||
} else {
|
||||
Right(duration)
|
||||
}
|
||||
} else {
|
||||
Left("Missing maximum command time to live")
|
||||
}
|
||||
} yield {
|
||||
Configuration(
|
||||
generation = config.getGeneration,
|
||||
timeModel = tm,
|
||||
maxDeduplicationTime = maxDeduplicationTime,
|
||||
)
|
||||
}
|
||||
|
||||
private def decodeTimeModel(tm: protobuf.LedgerTimeModel): Either[String, LedgerTimeModel] =
|
||||
LedgerTimeModel(
|
||||
avgTransactionLatency = parseDuration(tm.getAvgTransactionLatency),
|
||||
minSkew = parseDuration(tm.getMinSkew),
|
||||
maxSkew = parseDuration(tm.getMaxSkew),
|
||||
).toEither.left.map(e => s"decodeTimeModel: ${e.getMessage}")
|
||||
|
||||
private def parseDuration(dur: com.google.protobuf.Duration): Duration =
|
||||
Duration.ofSeconds(dur.getSeconds, dur.getNanos.toLong)
|
||||
|
||||
private def buildDuration(dur: Duration): com.google.protobuf.Duration =
|
||||
com.google.protobuf.Duration.newBuilder
|
||||
.setSeconds(dur.getSeconds)
|
||||
.setNanos(dur.getNano)
|
||||
.build
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,226 @@
|
||||
// Copyright (c) 2021 Digital Asset (Switzerland) GmbH and/or its affiliates. All rights reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package com.daml.ledger.configuration
|
||||
|
||||
import com.daml.ledger.configuration.ConfigurationSpec._
|
||||
import com.daml.ledger.participant.state.protobuf.{ledger_configuration => protobuf}
|
||||
import org.scalatest.matchers.should.Matchers
|
||||
import org.scalatest.wordspec.AnyWordSpec
|
||||
|
||||
import scala.compat.java8.DurationConverters._
|
||||
import scala.concurrent.duration.{DurationInt, FiniteDuration}
|
||||
import org.scalatest.prop.TableDrivenPropertyChecks._
|
||||
|
||||
class ConfigurationSpec extends AnyWordSpec with Matchers {
|
||||
"a ledger configuration" when {
|
||||
"decoding a v1 protobuf" should {
|
||||
"decode a valid protobuf" in {
|
||||
val configurationBytes = protobuf.LedgerConfiguration
|
||||
.of(
|
||||
version = 1,
|
||||
generation = 7,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(1.minute.toProtobuf),
|
||||
minSkew = Some(30.seconds.toProtobuf),
|
||||
maxSkew = Some(2.minutes.toProtobuf),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = None,
|
||||
)
|
||||
.toByteArray
|
||||
|
||||
val configuration = Configuration.decode(configurationBytes)
|
||||
|
||||
configuration should be(
|
||||
Right(
|
||||
Configuration(
|
||||
generation = 7,
|
||||
timeModel = LedgerTimeModel(
|
||||
avgTransactionLatency = 1.minute.toJava,
|
||||
minSkew = 30.seconds.toJava,
|
||||
maxSkew = 2.minutes.toJava,
|
||||
).get,
|
||||
maxDeduplicationTime = 1.day.toJava,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
"reject a missing time model" in {
|
||||
val configurationBytes = protobuf.LedgerConfiguration
|
||||
.of(
|
||||
version = 1,
|
||||
generation = 2,
|
||||
timeModel = None,
|
||||
maxDeduplicationTime = None,
|
||||
)
|
||||
.toByteArray
|
||||
|
||||
val configuration = Configuration.decode(configurationBytes)
|
||||
|
||||
configuration should be(Left("Missing time model"))
|
||||
}
|
||||
}
|
||||
|
||||
"decoding a v2 protobuf" should {
|
||||
"decode a valid protobuf" in {
|
||||
val configurationBytes = protobuf.LedgerConfiguration
|
||||
.of(
|
||||
version = 2,
|
||||
generation = 3,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(30.seconds.toProtobuf),
|
||||
minSkew = Some(20.seconds.toProtobuf),
|
||||
maxSkew = Some(5.minutes.toProtobuf),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = Some(6.hours.toProtobuf),
|
||||
)
|
||||
.toByteArray
|
||||
|
||||
val configuration = Configuration.decode(configurationBytes)
|
||||
|
||||
configuration should be(
|
||||
Right(
|
||||
Configuration(
|
||||
generation = 3,
|
||||
timeModel = LedgerTimeModel(
|
||||
avgTransactionLatency = 30.seconds.toJava,
|
||||
minSkew = 20.seconds.toJava,
|
||||
maxSkew = 5.minutes.toJava,
|
||||
).get,
|
||||
maxDeduplicationTime = 6.hours.toJava,
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val rejections = Table(
|
||||
("error message", "protobuf"),
|
||||
(
|
||||
"Missing time model",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 4,
|
||||
timeModel = None,
|
||||
maxDeduplicationTime = Some(1.day.toProtobuf),
|
||||
),
|
||||
),
|
||||
(
|
||||
"Missing maximum command time to live",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 1,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
minSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
maxSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = None,
|
||||
),
|
||||
),
|
||||
(
|
||||
"decodeTimeModel: requirement failed: Negative average transaction latency",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 1,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some((-5).seconds.toProtobuf),
|
||||
minSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
maxSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
),
|
||||
),
|
||||
(
|
||||
"decodeTimeModel: requirement failed: Negative min skew",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 1,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
minSkew = Some((-30).seconds.toProtobuf),
|
||||
maxSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
),
|
||||
),
|
||||
(
|
||||
"decodeTimeModel: requirement failed: Negative max skew",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 1,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
minSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
maxSkew = Some((-10).seconds.toProtobuf),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
),
|
||||
),
|
||||
(
|
||||
"requirement failed: Negative maximum command time to live",
|
||||
protobuf.LedgerConfiguration.of(
|
||||
version = 2,
|
||||
generation = 1,
|
||||
timeModel = Some(
|
||||
protobuf.LedgerTimeModel.of(
|
||||
avgTransactionLatency = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
minSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
maxSkew = Some(com.google.protobuf.duration.Duration.defaultInstance),
|
||||
)
|
||||
),
|
||||
maxDeduplicationTime = Some((-1).day.toProtobuf),
|
||||
),
|
||||
),
|
||||
)
|
||||
|
||||
"reject an invalid protobuf" in {
|
||||
forAll(rejections) { (errorMessage, protobuf) =>
|
||||
val configurationBytes = protobuf.toByteArray
|
||||
|
||||
val configuration = Configuration.decode(configurationBytes)
|
||||
|
||||
configuration should be(Left(errorMessage))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"decoding a protobuf with an invalid version" should {
|
||||
"reject the protobuf" in {
|
||||
val configurationBytes = protobuf.LedgerConfiguration
|
||||
.of(
|
||||
version = 3,
|
||||
generation = 0,
|
||||
timeModel = None,
|
||||
maxDeduplicationTime = None,
|
||||
)
|
||||
.toByteArray
|
||||
|
||||
val configuration = Configuration.decode(configurationBytes)
|
||||
|
||||
configuration should be(Left("Unknown version: 3"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object ConfigurationSpec {
|
||||
implicit class Converter(duration: FiniteDuration) {
|
||||
def toProtobuf: com.google.protobuf.duration.Duration = {
|
||||
val javaDuration = duration.toJava
|
||||
new com.google.protobuf.duration.Duration(javaDuration.getSeconds, javaDuration.getNano)
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user