diff --git a/ledger/participant-state/kvutils/src/test/suite/scala/com/daml/ledger/participant/state/kvutils/ConversionsSpec.scala b/ledger/participant-state/kvutils/src/test/suite/scala/com/daml/ledger/participant/state/kvutils/ConversionsSpec.scala index 2a8198493fac..8e3f55643334 100644 --- a/ledger/participant-state/kvutils/src/test/suite/scala/com/daml/ledger/participant/state/kvutils/ConversionsSpec.scala +++ b/ledger/participant-state/kvutils/src/test/suite/scala/com/daml/ledger/participant/state/kvutils/ConversionsSpec.scala @@ -4,8 +4,6 @@ package com.daml.ledger.participant.state.kvutils import com.daml.error.ValueSwitch - -import java.time.{Duration, Instant} import com.daml.ledger.api.DeduplicationPeriod import com.daml.ledger.configuration.LedgerTimeModel import com.daml.ledger.participant.state.kvutils.Conversions._ @@ -48,6 +46,7 @@ import org.scalatest.matchers.should.Matchers import org.scalatest.prop.TableDrivenPropertyChecks.{Table, forAll} import org.scalatest.wordspec.AnyWordSpec +import java.time.{Duration, Instant} import scala.annotation.nowarn import scala.collection.immutable.{ListMap, ListSet} import scala.collection.mutable @@ -121,60 +120,51 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { val submitterInfo = DamlSubmitterInfo.newBuilder().build() val now = Instant.now - "convert rejection to proto models and back to expected grpc code" in { + "convert rejection to proto models and back to expected grpc v1 code" in { forAll( - Table[ValueSwitch[Status], Rejection, Code, Map[String, String]]( - ("Error Version", "Rejection", "Expected Code", "Expected Additional Details"), + Table[Rejection, Code, Map[String, String]]( + ("Rejection", "Expected Code", "Expected Additional Details"), ( - v1ErrorSwitch, Rejection.ValidationFailure(Error.Package(Error.Package.Internal("ERROR", "ERROR"))), Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.InternallyInconsistentTransaction.InconsistentKeys, Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.InternallyInconsistentTransaction.DuplicateKeys, Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.ExternallyInconsistentTransaction.InconsistentContracts, Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.ExternallyInconsistentTransaction.InconsistentKeys, Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.ExternallyInconsistentTransaction.DuplicateKeys, Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.MissingInputState(DamlStateKey.getDefaultInstance), Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.InvalidParticipantState(Err.InternalError("error")), Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.InvalidParticipantState( Err.ArchiveDecodingFailed(Ref.PackageId.assertFromString("id"), "reason") ), @@ -182,49 +172,41 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { Map("package_id" -> "id"), ), ( - v1ErrorSwitch, Rejection.InvalidParticipantState(Err.MissingDivulgedContractInstance("id")), Code.INVALID_ARGUMENT, Map("contract_id" -> "id"), ), ( - v1ErrorSwitch, Rejection.RecordTimeOutOfRange(now, now), Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.LedgerTimeOutOfRange(LedgerTimeModel.OutOfRange(now, now, now)), Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.CausalMonotonicityViolated, Code.ABORTED, Map.empty, ), ( - v1ErrorSwitch, Rejection.SubmittingPartyNotKnownOnLedger(Ref.Party.assertFromString("party")), Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.PartiesNotKnownOnLedger(Seq.empty), Code.INVALID_ARGUMENT, Map.empty, ), ( - v1ErrorSwitch, Rejection.MissingInputState(partyStateKey("party")), Code.ABORTED, Map("key" -> "party: \"party\"\n"), ), ( - v1ErrorSwitch, Rejection.RecordTimeOutOfRange(Instant.EPOCH, Instant.EPOCH), Code.ABORTED, Map( @@ -233,67 +215,76 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { ), ), ( - v1ErrorSwitch, Rejection.SubmittingPartyNotKnownOnLedger(party0), Code.INVALID_ARGUMENT, Map("submitter_party" -> party0), ), ( - v1ErrorSwitch, Rejection.PartiesNotKnownOnLedger(Iterable(party0, party1)), Code.INVALID_ARGUMENT, Map("parties" -> s"""[\"$party0\",\"$party1\"]"""), ), + ) + ) { (rejection, expectedCode, expectedAdditionalDetails) => + checkErrors( + v1ErrorSwitch, + submitterInfo, + rejection, + expectedCode, + expectedAdditionalDetails, + ) + } + } + + "convert rejection to proto models and back to expected grpc v2 code" in { + forAll( + Table[Rejection, Code, Map[String, String]]( + ( + "Rejection", + "Expected Code", + "Expected Additional Details", + ), ( - v2ErrorSwitch, Rejection.ValidationFailure(Error.Package(Error.Package.Internal("ERROR", "ERROR"))), Code.INTERNAL, Map.empty, ), ( - v2ErrorSwitch, Rejection.InternallyInconsistentTransaction.InconsistentKeys, Code.INTERNAL, Map.empty, ), ( - v2ErrorSwitch, Rejection.InternallyInconsistentTransaction.DuplicateKeys, Code.INTERNAL, Map.empty, ), ( - v2ErrorSwitch, Rejection.ExternallyInconsistentTransaction.InconsistentContracts, Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.ExternallyInconsistentTransaction.InconsistentKeys, Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.ExternallyInconsistentTransaction.DuplicateKeys, Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.MissingInputState(DamlStateKey.getDefaultInstance), Code.INTERNAL, Map.empty, ), ( - v2ErrorSwitch, Rejection.InvalidParticipantState(Err.InternalError("error")), Code.INTERNAL, Map.empty, ), ( - v2ErrorSwitch, Rejection.InvalidParticipantState( Err.ArchiveDecodingFailed(Ref.PackageId.assertFromString("id"), "reason") ), @@ -301,49 +292,41 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { Map("package_id" -> "id"), ), ( - v2ErrorSwitch, Rejection.InvalidParticipantState(Err.MissingDivulgedContractInstance("id")), Code.INTERNAL, Map("contract_id" -> "id"), ), ( - v2ErrorSwitch, Rejection.RecordTimeOutOfRange(now, now), Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.LedgerTimeOutOfRange(LedgerTimeModel.OutOfRange(now, now, now)), Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.CausalMonotonicityViolated, Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.SubmittingPartyNotKnownOnLedger(Ref.Party.assertFromString("party")), Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.PartiesNotKnownOnLedger(Seq.empty), Code.FAILED_PRECONDITION, Map.empty, ), ( - v2ErrorSwitch, Rejection.MissingInputState(partyStateKey("party")), Code.INTERNAL, Map("key" -> "party: \"party\"\n"), ), ( - v2ErrorSwitch, Rejection.RecordTimeOutOfRange(Instant.EPOCH, Instant.EPOCH), Code.FAILED_PRECONDITION, Map( @@ -352,32 +335,24 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { ), ), ( - v2ErrorSwitch, Rejection.SubmittingPartyNotKnownOnLedger(party0), Code.FAILED_PRECONDITION, Map("submitter_party" -> party0), ), ( - v2ErrorSwitch, Rejection.PartiesNotKnownOnLedger(Iterable(party0, party1)), Code.FAILED_PRECONDITION, Map("parties" -> s"""[\"$party0\",\"$party1\"]"""), ), ) - ) { (errorVersionSwitch, rejection, expectedCode, expectedAdditionalDetails) => - val encodedEntry = Conversions - .encodeTransactionRejectionEntry( - submitterInfo, - rejection, - ) - .build() - val finalReason = Conversions - .decodeTransactionRejectionEntry(encodedEntry, errorVersionSwitch) - .value - finalReason.code shouldBe expectedCode.value() - finalReason.definiteAnswer shouldBe false - val actualDetails = finalReasonToDetails(finalReason) - actualDetails should contain allElementsOf (expectedAdditionalDetails) + ) { (rejection, expectedCode, expectedAdditionalDetails) => + checkErrors( + v2ErrorSwitch, + submitterInfo, + rejection, + expectedCode, + expectedAdditionalDetails, + ) } } @@ -555,6 +530,28 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { } } + private def checkErrors( + errorVersionSwitch: ValueSwitch[Status], + submitterInfo: DamlSubmitterInfo, + rejection: Rejection, + expectedCode: Code, + expectedAdditionalDetails: Map[String, String], + ) = { + val encodedEntry = Conversions + .encodeTransactionRejectionEntry( + submitterInfo, + rejection, + ) + .build() + val finalReason = Conversions + .decodeTransactionRejectionEntry(encodedEntry, errorVersionSwitch) + .value + finalReason.code shouldBe expectedCode.value() + finalReason.definiteAnswer shouldBe false + val actualDetails = finalReasonToDetails(finalReason) + actualDetails should contain allElementsOf (expectedAdditionalDetails) + } + private def newDisclosureEntry(node: NodeId, parties: List[String]) = DisclosureEntry.newBuilder .setNodeId(node.index.toString) @@ -625,12 +622,8 @@ class ConversionsSpec extends AnyWordSpec with Matchers with OptionValues { ) .build - private lazy val v1ErrorSwitch = new ValueSwitch[Status](enableSelfServiceErrorCodes = false) { - override def toString: String = "1" - } - private lazy val v2ErrorSwitch = new ValueSwitch[Status](enableSelfServiceErrorCodes = true) { - override def toString: String = "2" - } + private lazy val v1ErrorSwitch = new ValueSwitch[Status](enableSelfServiceErrorCodes = false) + private lazy val v2ErrorSwitch = new ValueSwitch[Status](enableSelfServiceErrorCodes = true) private[this] val txVersion = TransactionVersion.StableVersions.max