LF: Exhaustive test for valueTranslator. (#10927)

* LF: Exhaustive test for value translator.

CHANGELOG_BEGIN
CHANGELOG_END

* cosmetic
This commit is contained in:
Remy 2021-09-20 16:21:45 +02:00 committed by GitHub
parent 409c0b4f60
commit ac02dbdeb9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 90 additions and 300 deletions

View File

@ -79,8 +79,8 @@ private[engine] final class ValueTranslator(
throw Error.Preprocessing.ValueNesting(value)
} else {
val newNesting = nesting + 1
def typeError(msg: String = s"mismatching type: $ty and value: $value0") =
throw Error.Preprocessing.TypeMismatch(ty, value0, msg)
def typeError(msg: String = s"mismatching type: ${ty0.pretty} and value: $value0") =
throw Error.Preprocessing.TypeMismatch(ty0, value0, msg)
val (ty1, tyArgs) = AstUtil.destructApp(ty0)
ty1 match {
case TBuiltin(bt) =>

View File

@ -27,7 +27,7 @@ class CommandPreprocessorSpec
import com.daml.lf.transaction.test.TransactionBuilder.Implicits.{defaultPackageId => _, _}
private implicit val defaultPackageId = defaultParserParameters.defaultPackageId
lazy val pkg =
private[this] val pkg =
p"""
module Mod {
@ -71,7 +71,7 @@ class CommandPreprocessorSpec
private[this] val compiledPackage = ConcurrentCompiledPackages()
assert(compiledPackage.addPackage(defaultPackageId, pkg) == ResultDone.Unit)
val valueParties = ValueList(FrontStack(ValueParty("Alice")))
private[this] val valueParties = ValueList(FrontStack(ValueParty("Alice")))
"preprocessCommand" should {

View File

@ -180,278 +180,6 @@ class EngineTest
}
"command translation" should {
"translate create commands argument including labels" in {
val id = Identifier(basicTestsPkgId, "BasicTests:Simple")
val command =
CreateCommand(id, ValueRecord(Some(id), ImmArray((Some[Name]("p"), ValueParty(party)))))
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"translate create commands argument without labels" in {
val id = Identifier(basicTestsPkgId, "BasicTests:Simple")
val command =
CreateCommand(id, ValueRecord(Some(id), ImmArray((None, ValueParty(party)))))
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"not translate create commands argument wrong label" in {
val id = Identifier(basicTestsPkgId, "BasicTests:Simple")
val command =
CreateCommand(
id,
ValueRecord(Some(id), ImmArray((Some[Name]("this_is_not_the_one"), ValueParty(party)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) { case Left(Error.Preprocessing(error)) =>
error shouldBe a[Error.Preprocessing.TypeMismatch]
}
}
"translate exercise commands argument including labels" in {
val originalCoid = toContractId("BasicTests:CallablePayout:1")
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command = ExerciseCommand(
templateId,
originalCoid,
"Transfer",
ValueRecord(None, ImmArray((Some[Name]("newReceiver"), ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"translate exercise commands argument without labels" in {
val originalCoid = toContractId("BasicTests:CallablePayout:1")
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command = ExerciseCommand(
templateId,
originalCoid,
"Transfer",
ValueRecord(None, ImmArray((None, ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"translate exercise-by-key commands with argument with labels" in {
val templateId = Identifier(basicTestsPkgId, "BasicTests:WithKey")
val command = ExerciseByKeyCommand(
templateId,
ValueRecord(None, ImmArray((None, ValueParty(alice)), (None, ValueInt64(42)))),
"SumToK",
ValueRecord(None, ImmArray((Some[Name]("n"), ValueInt64(5)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"translate exercise-by-key commands with argument without labels" in {
val templateId = Identifier(basicTestsPkgId, "BasicTests:WithKey")
val command = ExerciseByKeyCommand(
templateId,
ValueRecord(None, ImmArray((None, ValueParty(alice)), (None, ValueInt64(42)))),
"SumToK",
ValueRecord(None, ImmArray((None, ValueInt64(5)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"not translate exercise-by-key commands with argument with wrong labels" in {
val templateId = Identifier(basicTestsPkgId, "BasicTests:WithKey")
val command = ExerciseByKeyCommand(
templateId,
ValueRecord(None, ImmArray((None, ValueParty(alice)), (None, ValueInt64(42)))),
"SumToK",
ValueRecord(None, ImmArray((Some[Name]("WRONG"), ValueInt64(5)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) { case Left(Error.Preprocessing(error)) =>
error shouldBe a[Error.Preprocessing.TypeMismatch]
error.message should startWith("Missing record field 'n' for record")
}
}
"not translate exercise-by-key commands if the template specifies no key" in {
val templateId = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command = ExerciseByKeyCommand(
templateId,
ValueRecord(None, ImmArray((None, ValueParty(alice)), (None, ValueInt64(42)))),
"Transfer",
ValueRecord(None, ImmArray((None, ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) {
case Left(Error.Preprocessing(Error.Preprocessing.Lookup(language.LookupError(ref, _)))) =>
ref shouldBe a[language.Reference.TemplateKey]
}
}
"not translate exercise-by-key commands if the given key does not match the type specified in the template" in {
val templateId = Identifier(basicTestsPkgId, "BasicTests:WithKey")
val command = ExerciseByKeyCommand(
templateId,
ValueRecord(None, ImmArray((None, ValueInt64(42)), (None, ValueInt64(42)))),
"SumToK",
ValueRecord(None, ImmArray((None, ValueInt64(5)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) { case Left(Error.Preprocessing(error)) =>
error shouldBe a[Error.Preprocessing.TypeMismatch]
}
}
"translate create-and-exercise commands argument including labels" in {
val id = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command =
CreateAndExerciseCommand(
id,
ValueRecord(
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
ImmArray(
(Some[Ref.Name]("giver"), ValueParty(clara)),
(Some[Ref.Name]("receiver"), ValueParty(clara)),
),
),
"Transfer",
ValueRecord(None, ImmArray((Some[Name]("newReceiver"), ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"translate create-and-exercise commands argument without labels" in {
val id = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command =
CreateAndExerciseCommand(
id,
ValueRecord(
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
ImmArray((None, ValueParty(clara)), (None, ValueParty(clara))),
),
"Transfer",
ValueRecord(None, ImmArray((None, ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
res shouldBe a[Right[_, _]]
}
"not translate create-and-exercise commands argument wrong label in create arguments" in {
val id = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command =
CreateAndExerciseCommand(
id,
ValueRecord(
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
ImmArray(
(None, ValueParty(clara)),
(Some[Ref.Name]("this_is_not_the_one"), ValueParty(clara)),
),
),
"Transfer",
ValueRecord(None, ImmArray((None, ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) { case Left(Error.Preprocessing(error)) =>
error shouldBe a[Error.Preprocessing.TypeMismatch]
}
}
"not translate create-and-exercise commands argument wrong label in choice arguments" in {
val id = Identifier(basicTestsPkgId, "BasicTests:CallablePayout")
val command =
CreateAndExerciseCommand(
id,
ValueRecord(
Some(Identifier(basicTestsPkgId, "BasicTests:CallablePayout")),
ImmArray((None, ValueParty(clara)), (None, ValueParty(clara))),
),
"Transfer",
ValueRecord(None, ImmArray((Some[Name]("this_is_not_the_one"), ValueParty(clara)))),
)
val res = preprocessor
.preprocessCommands(ImmArray(command))
.consume(lookupContract, lookupPackage, lookupKey)
inside(res) { case Left(Error.Preprocessing(error)) =>
error shouldBe a[Error.Preprocessing.TypeMismatch]
}
}
"translate Optional values" in {
val (optionalPkgId, _, allOptionalPackages) =
loadPackage("daml-lf/tests/Optional.dar")
val translator =
new preprocessing.Preprocessor(
ConcurrentCompiledPackages(suffixLenientEngine.config.getCompilerConfig)
)
val id = Identifier(optionalPkgId, "Optional:Rec")
val someValue =
ValueRecord(
Some(id),
ImmArray(Some[Name]("recField") -> ValueOptional(Some(ValueText("foo")))),
)
val noneValue =
ValueRecord(Some(id), ImmArray(Some[Name]("recField") -> ValueOptional(None)))
val typ = TTyConApp(id, ImmArray.Empty)
translator
.translateValue(typ, someValue)
.consume(lookupContract, allOptionalPackages.get, lookupKey) shouldEqual
Right(SRecord(id, ImmArray("recField"), ArrayList(SOptional(Some(SText("foo"))))))
translator
.translateValue(typ, noneValue)
.consume(lookupContract, allOptionalPackages.get, lookupKey) shouldEqual
Right(SRecord(id, ImmArray("recField"), ArrayList(SOptional(None))))
}
"returns correct error when resuming" in {
val translator =
new preprocessing.Preprocessor(

View File

@ -11,39 +11,47 @@ import com.daml.lf.language.Util._
import com.daml.lf.speedy.SValue._
import com.daml.lf.value.Value
import com.daml.lf.value.Value._
import org.scalatest.Inside
import org.scalatest.matchers.should.Matchers
import org.scalatest.prop.TableDrivenPropertyChecks
import org.scalatest.wordspec.AnyWordSpec
import scala.util.{Failure, Success, Try}
class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenPropertyChecks {
class ValueTranslatorSpec
extends AnyWordSpec
with Inside
with Matchers
with TableDrivenPropertyChecks {
import Preprocessor.ArrayList
import com.daml.lf.testing.parser.Implicits._
import com.daml.lf.transaction.test.TransactionBuilder.Implicits.{defaultPackageId => _, _}
private implicit val defaultPackageId = defaultParserParameters.defaultPackageId
private[this] implicit val defaultPackageId: Ref.PackageId =
defaultParserParameters.defaultPackageId
val aCid =
private[this] val aCid =
ContractId.V1.assertBuild(
crypto.Hash.hashPrivateKey("a Contract ID"),
Bytes.assertFromString("00"),
)
lazy val pkg =
private[this] val pkg =
p"""
module Mod {
record @serializable Tuple (a: *) (b: *) = { x: a, y: b };
record @serializable Record = { field : Int64 };
variant @serializable Either (a: *) (b: *) = Left : a | Right : b;
enum Enum = value1 | value2;
enum Color = red | green | blue;
record Tricky (b: * -> *) = { x : b Unit };
record MyCons = { head : Int64, tail: Mod:MyList };
variant MyList = MyNil : Unit | MyCons: Mod:MyCons ;
record @serializable RecordRef = { owner: Party, cid: (ContractId Mod:Record) };
record @serializable Template = { field : Int64 };
record @serializable TemplateRef = { owner: Party, cid: (ContractId Mod:Template) };
}
"""
@ -85,7 +93,7 @@ class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenProp
// ValueNumeric(Numeric.assertFromString("9.000000000")),
(TParty, ValueParty("Alice"), SParty("Alice")),
(
TContractId(t"Mod:Record"),
TContractId(t"Mod:Template"),
ValueContractId(aCid),
SContractId(aCid),
),
@ -106,16 +114,16 @@ class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenProp
),
(TOptional(TText), ValueOptional(Some(ValueText("text"))), SOptional(Some(SText("text")))),
(
t"Mod:Record",
ValueRecord("", ImmArray("field" -> ValueInt64(33))),
SRecord("Mod:Record", ImmArray("field"), ArrayList(SInt64(33))),
t"Mod:Tuple Int64 Text",
ValueRecord("", ImmArray("x" -> ValueInt64(33), "y" -> ValueText("a"))),
SRecord("Mod:Tuple", ImmArray("x", "y"), ArrayList(SInt64(33), SText("a"))),
),
(
t"Mod:Either Text Int64",
ValueVariant("", "Left", ValueText("some test")),
SVariant("Mod:Either", "Left", 0, SText("some test")),
t"Mod:Either Int64 Text",
ValueVariant("", "Right", ValueText("some test")),
SVariant("Mod:Either", "Right", 1, SText("some test")),
),
(Ast.TTyCon("Mod:Enum"), ValueEnum("", "value1"), SEnum("Mod:Enum", "value1", 0)),
(Ast.TTyCon("Mod:Color"), ValueEnum("", "blue"), SEnum("Mod:Color", "blue", 2)),
(
Ast.TApp(Ast.TTyCon("Mod:Tricky"), Ast.TBuiltin(Ast.BTList)),
ValueRecord("", ImmArray("" -> ValueNil)),
@ -125,7 +133,61 @@ class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenProp
"succeeds on well type values" in {
forAll(testCases) { (typ, value, svalue) =>
unsafeTranslateValue(typ, value) shouldBe svalue
Try(unsafeTranslateValue(typ, value)) shouldBe Success(svalue)
}
}
"handle different representation of the same record" in {
val typ = t"Mod:Tuple Int64 Text"
val testCases = Table(
"record",
ValueRecord("Mod:Tuple", ImmArray("x" -> ValueInt64(33), "y" -> ValueText("a"))),
ValueRecord("Mod:Tuple", ImmArray("y" -> ValueText("a"), "x" -> ValueInt64(33))),
ValueRecord("", ImmArray("x" -> ValueInt64(33), "y" -> ValueText("a"))),
ValueRecord("", ImmArray("" -> ValueInt64(33), "" -> ValueText("a"))),
)
val svalue = SRecord("Mod:Tuple", ImmArray("x", "y"), ArrayList(SInt64(33), SText("a")))
forEvery(testCases)(testCase =>
Try(unsafeTranslateValue(typ, testCase)) shouldBe Success(svalue)
)
}
"handle different representation of the same variant" in {
val typ = t"Mod:Either Text Int64"
val testCases = Table(
"variant",
ValueVariant("Mod:Either", "Left", ValueText("some test")),
ValueVariant("", "Left", ValueText("some test")),
)
val svalue = SVariant("Mod:Either", "Left", 0, SText("some test"))
forEvery(testCases)(value => Try(unsafeTranslateValue(typ, value)) shouldBe Success(svalue))
}
"handle different representation of the same enum" in {
val typ = t"Mod:Color"
val testCases = Table("enum", ValueEnum("Mod:Color", "green"), ValueEnum("", "green"))
val svalue = SEnum("Mod:Color", "green", 1)
forEvery(testCases)(value => Try(unsafeTranslateValue(typ, value)) shouldBe Success(svalue))
}
"return proper mismatch error" in {
val res = Try(
unsafeTranslateValue(
t"Mod:Tuple Int64 Text",
ValueRecord(
"",
ImmArray(
"x" -> ValueInt64(33),
"y" -> ValueParty("Alice"), // Here the field has type Party instead of Text
),
),
)
)
inside(res) { case Failure(Error.Preprocessing.TypeMismatch(typ, value, _)) =>
typ shouldBe t"Text"
value shouldBe ValueParty("Alice")
}
}
@ -161,17 +223,17 @@ class ValueTranslatorSpec extends AnyWordSpec with Matchers with TableDrivenProp
val cid = ValueContractId(culprit)
Table[Ast.Type, Value](
("type" -> "value"),
t"ContractId Mod:Record" -> cid,
TList(t"ContractId Mod:Record") -> ValueList(FrontStack(cid)),
TTextMap(t"ContractId Mod:Record") -> ValueTextMap(SortedLookupList(Map("0" -> cid))),
TGenMap(TInt64, t"ContractId Mod:Record") -> ValueGenMap(ImmArray(ValueInt64(1) -> cid)),
TGenMap(t"ContractId Mod:Record", TInt64) -> ValueGenMap(ImmArray(cid -> ValueInt64(0))),
TOptional(t"ContractId Mod:Record") -> ValueOptional(Some(cid)),
Ast.TTyCon("Mod:RecordRef") -> ValueRecord(
t"ContractId Mod:Template" -> cid,
TList(t"ContractId Mod:Template") -> ValueList(FrontStack(cid)),
TTextMap(t"ContractId Mod:Template") -> ValueTextMap(SortedLookupList(Map("0" -> cid))),
TGenMap(TInt64, t"ContractId Mod:Template") -> ValueGenMap(ImmArray(ValueInt64(1) -> cid)),
TGenMap(t"ContractId Mod:Template", TInt64) -> ValueGenMap(ImmArray(cid -> ValueInt64(0))),
TOptional(t"ContractId Mod:Template") -> ValueOptional(Some(cid)),
t"Mod:TemplateRef" -> ValueRecord(
"",
ImmArray("" -> ValueParty("Alice"), "" -> cid),
),
TTyConApp("Mod:Either", ImmArray(t"ContractId Mod:Record", TInt64)) -> ValueVariant(
TTyConApp("Mod:Either", ImmArray(t"ContractId Mod:Template", TInt64)) -> ValueVariant(
"",
"Left",
cid,