pulumi/pkg/compiler/binder_test.go
joeduffy 86219e781b Custom types, round 2
This checkin continues progress on marapongo/mu#9.  It's still not
complete, however we're getting there.  In particular, this includes:

* Rename of ComplexLiteral to SchemaLiteral, as it is used exclusively
  for schematized types.  Also includes a set of changes associated
  with this, like deep value conversion to `map[string]interface{}`.

* Binding of schema types included within a Stack.  This allows names in
  type references to be bound to those schema types during typechecking.
  This also includes binding schema properties, reusing all the existing
  property binding logic for stacks.  In this way, properties between
  stacks and custom schema types are one and the same, which is nice.

* Enforcement for custom schema constraints; this includes Pattern,
  MaxLength, MinLength, Maximum, and Minimum, as per the JSON Schema
  specification.
2016-12-06 20:51:05 -08:00

215 lines
7.9 KiB
Go

// Copyright 2016 Marapongo, Inc. All rights reserved.
package compiler
import (
"fmt"
"testing"
"github.com/stretchr/testify/assert"
"github.com/marapongo/mu/pkg/ast"
"github.com/marapongo/mu/pkg/diag"
"github.com/marapongo/mu/pkg/errors"
)
func TestBadMissingStackName(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__missing_stack_name")
// Check that the compiler complained about a missing Stack name.
d := errors.ErrorMissingStackName
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml", d.Message),
sink.ErrorMsgs()[0])
}
func TestBadStackSemVer1(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__stack_semver__1")
// Check that the compiler complained about an illegal semantic version.
d := errors.ErrorIllegalStackVersion
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "badbadbad", "No Major.Minor.Patch elements found")),
sink.ErrorMsgs()[0])
}
func TestBadStackSemVer2(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__stack_semver__2")
// Check that the compiler complained about an illegal semantic version.
d := errors.ErrorIllegalStackVersion
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, ">1.0.0",
"Invalid character(s) found in major number \">1\"")),
sink.ErrorMsgs()[0])
}
func TestBadDepSemVer1(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__dep_semver__1")
// Check that the compiler complained about an illegal semantic version.
d := errors.ErrorIllegalNameLikeSyntax
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep1@badbadbad",
"Illegal version spec: Could not get version from string: \"badbadbad\"")),
sink.ErrorMsgs()[0])
}
func TestBadDepSemVer2(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__dep_semver__2")
// Check that the compiler complained about an illegal semantic version.
d := errors.ErrorIllegalNameLikeSyntax
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep3@badbadbad",
"Illegal version spec: Could not get version from string: \"badbadbad\"")),
sink.ErrorMsgs()[0])
}
func TestBadDepSemVer3(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__dep_semver__3")
// Check that the compiler complained about an illegal semantic version.
d := errors.ErrorIllegalNameLikeSyntax
assert.Equal(t, 4, sink.Errors(), "expected an error for each bad semver")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep1@bad1",
"Illegal version spec: Could not parse Range \"bad1\": "+
"Could not parse comparator \"bad\" in \"bad1\"")),
sink.ErrorMsgs()[0])
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep2@0.0",
"Illegal version spec: Could not parse Range \"0.0\": "+
"Could not parse version \"0.0\" in \"0.0\": No Major.Minor.Patch elements found")),
sink.ErrorMsgs()[1])
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep3@bad3",
"Illegal version spec: Could not parse Range \"bad3\": "+
"Could not parse comparator \"bad\" in \"bad3\"")),
sink.ErrorMsgs()[2])
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "dep4@0.6.bad.ness.1",
"Illegal version spec: Could not parse Range \"0.6.bad.ness.1\": "+
"Could not parse version \"0.6.bad.ness.1\" in \"0.6.bad.ness.1\": "+
"Invalid character(s) found in patch number \"bad.ness.1\"")),
sink.ErrorMsgs()[3])
}
func TestBadSymbolAlreadyExists(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__symbol_already_exists")
// Check that the compiler complained about a duplicate symbol.
d := errors.ErrorSymbolAlreadyExists
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message, "foo")),
sink.ErrorMsgs()[0])
}
func TestBadTypeNotFound1(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__type_not_found__1")
// Check that the compiler complained about the type missisng.
d := errors.ErrorStackTypeNotFound
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message,
fmt.Sprintf("%v%vsomething/non/existent@%v",
ast.DefaultRefProto, ast.DefaultRefBase, ast.DefaultRefVersion))),
sink.ErrorMsgs()[0])
}
func TestBadTypeNotFound2(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__type_not_found__2")
// Check that the compiler complained about the type missisng.
d := errors.ErrorStackTypeNotFound
assert.Equal(t, 1, sink.Errors(), "expected a single error")
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID, "Mu.yaml",
fmt.Sprintf(d.Message,
fmt.Sprintf("%v%vsomething/non/existent@%v",
ast.DefaultRefProto, ast.DefaultRefBase, ast.DefaultRefVersion))),
sink.ErrorMsgs()[0])
}
func TestBadMissingProperties(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__properties", "bad__missing")
d := errors.ErrorMissingRequiredProperty
reqs := []string{"req_bool", "req_number", "req_service", "req_string"}
assert.Equal(t, len(reqs), sink.Errors(), "expected an error per property")
for i, req := range reqs {
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID,
fmt.Sprintf(d.Message, req)),
sink.ErrorMsgs()[i])
}
}
func TestBadUnrecognizedProperties(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__properties", "bad__unrecognized")
d := errors.ErrorUnrecognizedProperty
unks := []string{"unk_bool", "unk_number", "unk_service", "unk_string"}
assert.Equal(t, len(unks), sink.Errors(), "expected an error per property")
for i, unk := range unks {
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID,
fmt.Sprintf(d.Message, unk)),
sink.ErrorMsgs()[i])
}
}
func TestBadPropertyTypes(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "bad__properties", "bad__types")
d := errors.ErrorIncorrectType
exp := []string{"bool", "number", "service", "string"}
got := []string{"string", "string", "bool", "float64"}
assert.Equal(t, len(exp), sink.Errors(), "expected an error per property")
for i, ty := range exp {
assert.Equal(t,
fmt.Sprintf("%v: %v%v: %v\n",
diag.DefaultSinkErrorPrefix, diag.DefaultSinkIDPrefix, d.ID,
fmt.Sprintf(d.Message, ty, got[i])),
sink.ErrorMsgs()[i])
}
}
func TestGoodPredefTypes(t *testing.T) {
sink := buildNoCodegen("testdata", "binder", "good__predef_types")
// Check that no errors are found when using predefined stack types.
assert.Equal(t, 0, sink.Errors(), "expected no errors when binding to predef types")
}