Merge pull request #441 from sparkprime/expandy_tests

Write some new unit tests for expandybird
pull/445/head
Dave Cunningham 9 years ago
commit e65b6beed3

@ -58,6 +58,11 @@ func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.Expans
chartInv := request.ChartInvocation chartInv := request.ChartInvocation
chartFile := request.Chart.Chartfile chartFile := request.Chart.Chartfile
chartMembers := request.Chart.Members chartMembers := request.Chart.Members
if chartInv.Type != chartFile.Name {
return nil, fmt.Errorf("Request chart invocation does not match provided chart")
}
schemaName := chartInv.Type + ".schema" schemaName := chartInv.Type + ".schema"
if chartFile.Expander == nil { if chartFile.Expander == nil {
@ -89,7 +94,7 @@ func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.Expans
message := fmt.Sprintf("The entrypoint in the chart.yaml cannot be found: %s", chartFile.Expander.Entrypoint) message := fmt.Sprintf("The entrypoint in the chart.yaml cannot be found: %s", chartFile.Expander.Entrypoint)
return nil, fmt.Errorf("%s: %s", chartInv.Name, message) return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
} }
if schemaIndex == -1 { if chartFile.Schema != "" && schemaIndex == -1 {
message := fmt.Sprintf("The schema in the chart.yaml cannot be found: %s", chartFile.Schema) message := fmt.Sprintf("The schema in the chart.yaml cannot be found: %s", chartFile.Schema)
return nil, fmt.Errorf("%s: %s", chartInv.Name, message) return nil, fmt.Errorf("%s: %s", chartInv.Name, message)
} }

@ -16,168 +16,691 @@ limitations under the License.
package expander package expander
/*
import ( import (
"fmt" "fmt"
"io"
"io/ioutil"
"os"
"path"
"reflect" "reflect"
"runtime"
"strings" "strings"
"testing" "testing"
"github.com/kubernetes/helm/pkg/chart"
"github.com/kubernetes/helm/pkg/common" "github.com/kubernetes/helm/pkg/common"
"github.com/kubernetes/helm/pkg/util"
) )
var importFileNames = []string{
"../test/replicatedservice.py",
}
var validFileName = "../test/ValidContent.yaml"
var outputFileName = "../test/ExpectedOutput.yaml"
var archiveFileName = "../test/TestArchive.tar"
var expanderName = "../../../expansion/expansion.py" var expanderName = "../../../expansion/expansion.py"
type ExpanderTestCase struct { type testCase struct {
Description string Description string
TemplateFileName string Request *common.ExpansionRequest
ImportFileNames []string ExpectedResponse *common.ExpansionResponse
ExpectedError string ExpectedError string
} }
func (etc *ExpanderTestCase) GetTemplate(t *testing.T) *common.Template { // content provides an easy way to provide file content verbatim in tests.
template, err := util.NewTemplateFromFileNames(etc.TemplateFileName, etc.ImportFileNames) func content(lines []string) []byte {
if err != nil { return []byte(strings.Join(lines, "\n") + "\n")
t.Fatalf("cannot create template for test case '%s': %s", etc.Description, err) }
}
return template // funcName returns the name of the calling function.
func funcName() string {
pc, _, _, _ := runtime.Caller(1)
return runtime.FuncForPC(pc).Name()
} }
func GetOutputString(t *testing.T, description string) string { func testExpansion(t *testing.T, req *common.ExpansionRequest,
output, err := ioutil.ReadFile(outputFileName) expResponse *common.ExpansionResponse, expError string) {
backend := NewExpander(expanderName)
response, err := backend.ExpandChart(req)
if err != nil { if err != nil {
t.Fatalf("cannot read output file for test case '%s': %s", description, err) message := err.Error()
if expResponse != nil || !strings.Contains(message, expError) {
t.Fatalf("unexpected error: %s\n", message)
}
} else {
if expResponse == nil {
t.Fatalf("expected error did not occur: %s\n", expError)
}
if !reflect.DeepEqual(response, expResponse) {
message := fmt.Sprintf(
"want:\n%s\nhave:\n%s\n", expResponse, response)
t.Fatalf("output mismatch:\n%s\n", message)
}
} }
}
return string(output) var pyExpander = &chart.Expander{
Name: "ExpandyBird",
Entrypoint: "templates/main.py",
} }
func expandAndVerifyOutput(t *testing.T, actualOutput, description string) { var jinjaExpander = &chart.Expander{
actualResult, err := NewExpansionResult(actualOutput) Name: "ExpandyBird",
if err != nil { Entrypoint: "templates/main.jinja",
t.Fatalf("error in test case '%s': %s\n", description, err) }
}
expectedOutput := GetOutputString(t, description) func TestEmptyJinja(t *testing.T) {
expectedResult, err := NewExpansionResult(expectedOutput) testExpansion(
if err != nil { t,
t.Fatalf("error in test case '%s': %s\n", description, err) &common.ExpansionRequest{
} ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{"resources:"}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{},
},
"", // Error
)
}
if !reflect.DeepEqual(actualResult, expectedResult) { func TestEmptyPython(t *testing.T) {
message := fmt.Sprintf("want:\n%s\nhave:\n%s\n", expectedOutput, actualOutput) testExpansion(
t.Fatalf("error in test case '%s':\n%s\n", description, message) t,
} &common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: pyExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.py",
Content: content([]string{
"def GenerateConfig(ctx):",
" return 'resources:'",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{},
},
"", // Error
)
} }
func testExpandTemplateFromFile(t *testing.T, fileName, baseName string, importFileNames []string, func TestSimpleJinja(t *testing.T) {
constructor func(string, io.Reader, []string) (*common.Template, error)) { testExpansion(
file, err := os.Open(fileName) t,
if err != nil { &common.ExpansionRequest{
t.Fatalf("cannot open file %s: %s", fileName, err) ChartInvocation: &common.Resource{
} Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- name: foo",
" type: bar",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"type": "bar",
},
},
},
"", // Error
)
}
template, err := constructor(baseName, file, importFileNames) func TestSimplePython(t *testing.T) {
if err != nil { testExpansion(
t.Fatalf("cannot create template from file %s: %s", fileName, err) t,
} &common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: pyExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.py",
Content: content([]string{
"def GenerateConfig(ctx):",
" return '''resources:",
"- name: foo",
" type: bar",
"'''",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"type": "bar",
},
},
},
"", // Error
)
}
backend := NewExpander(expanderName) func TestPropertiesJinja(t *testing.T) {
actualOutput, err := backend.ExpandTemplate(template) testExpansion(
if err != nil { t,
t.Fatalf("cannot expand template from file %s: %s", fileName, err) &common.ExpansionRequest{
} ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
Properties: map[string]interface{}{
"prop1": 3.0,
"prop2": "foo",
},
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- name: foo",
" type: {{ properties.prop2 }}",
" properties:",
" something: {{ properties.prop1 }}",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"properties": map[string]interface{}{
"something": 3.0,
},
"type": "foo",
},
},
},
"", // Error
)
}
description := fmt.Sprintf("test expand template from file: %s", fileName) func TestPropertiesPython(t *testing.T) {
expandAndVerifyOutput(t, actualOutput, description) testExpansion(
} t,
&common.ExpansionRequest{
func TestExpandTemplateFromReader(t *testing.T) { ChartInvocation: &common.Resource{
baseName := path.Base(validFileName) Name: "test_invocation",
testExpandTemplateFromFile(t, validFileName, baseName, importFileNames, util.NewTemplateFromReader) Type: funcName(),
} Properties: map[string]interface{}{
"prop1": 3.0,
func TestExpandTemplateFromArchive(t *testing.T) { "prop2": "foo",
baseName := path.Base(validFileName) },
testExpandTemplateFromFile(t, archiveFileName, baseName, nil, util.NewTemplateFromArchive) },
} Chart: &chart.Content{
Chartfile: &chart.Chartfile{
var ExpanderTestCases = []ExpanderTestCase{ Name: funcName(),
{ Expander: pyExpander,
"expect error for invalid file name", },
"../test/InvalidFileName.yaml", Members: []*chart.Member{
importFileNames, {
"ExpansionError: Exception", Path: "templates/main.py",
}, Content: content([]string{
{ "def GenerateConfig(ctx):",
"expect error for invalid property", " return '''resources:",
"../test/InvalidProperty.yaml", "- name: foo",
importFileNames, " type: %(prop2)s",
"ExpansionError: Exception", " properties:",
}, " something: %(prop1)s",
{ "''' % ctx.properties",
"expect error for malformed content", }),
"../test/MalformedContent.yaml", },
importFileNames, },
"ExpansionError: Error parsing YAML: mapping values are not allowed here", },
}, },
{ &common.ExpansionResponse{
"expect error for missing imports", Resources: []interface{}{
"../test/MissingImports.yaml", map[string]interface{}{
importFileNames, "name": "foo",
"ExpansionError: Exception", "properties": map[string]interface{}{
}, "something": 3.0,
{ },
"expect error for missing resource name", "type": "foo",
"../test/MissingResourceName.yaml", },
importFileNames, },
"ExpansionError: Resource does not have a name", },
}, "", // Error
{ )
"expect error for missing type name", }
"../test/MissingTypeName.yaml",
importFileNames, func TestMultiFileJinja(t *testing.T) {
"ExpansionError: Resource does not have type defined", testExpansion(
}, t,
{ &common.ExpansionRequest{
"expect success", ChartInvocation: &common.Resource{
validFileName, Name: "test_invocation",
importFileNames, Type: funcName(),
"", },
}, Chart: &chart.Content{
} Chartfile: &chart.Chartfile{
Name: funcName(),
func TestExpandTemplate(t *testing.T) { Expander: jinjaExpander,
backend := NewExpander(expanderName) },
for _, etc := range ExpanderTestCases { Members: []*chart.Member{
template := etc.GetTemplate(t) {
actualOutput, err := backend.ExpandTemplate(template) Path: "templates/main.jinja",
if err != nil { Content: content([]string{"{% include 'templates/secondary.jinja' %}"}),
message := err.Error() },
if !strings.Contains(message, etc.ExpectedError) { {
t.Fatalf("error in test case '%s': %s\n", etc.Description, message) Path: "templates/secondary.jinja",
} Content: content([]string{
} else { "resources:",
if etc.ExpectedError != "" { "- name: foo",
t.Fatalf("expected error did not occur in test case '%s': %s\n", " type: bar",
etc.Description, etc.ExpectedError) }),
} },
},
expandAndVerifyOutput(t, actualOutput, etc.Description) },
} },
} &common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"type": "bar",
},
},
},
"", // Error
)
}
var schemaContent = content([]string{
`{`,
` "required": ["prop1", "prop2"],`,
` "additionalProperties": false,`,
` "properties": {`,
` "prop1": {`,
` "description": "Nice description.",`,
` "type": "integer"`,
` },`,
` "prop2": {`,
` "description": "Nice description.",`,
` "type": "string"`,
` }`,
` }`,
`}`,
})
func TestSchema(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
Properties: map[string]interface{}{
"prop1": 3.0,
"prop2": "foo",
},
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
Schema: "Schema.yaml",
},
Members: []*chart.Member{
{
Path: "Schema.yaml",
Content: schemaContent,
},
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- name: foo",
" type: {{ properties.prop2 }}",
" properties:",
" something: {{ properties.prop1 }}",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"properties": map[string]interface{}{
"something": 3.0,
},
"type": "foo",
},
},
},
"", // Error
)
}
func TestSchemaFail(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
Properties: map[string]interface{}{
"prop1": 3.0,
"prop3": "foo",
},
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
Schema: "Schema.yaml",
},
Members: []*chart.Member{
{
Path: "Schema.yaml",
Content: schemaContent,
},
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- name: foo",
" type: {{ properties.prop2 }}",
" properties:",
" something: {{ properties.prop1 }}",
}),
},
},
},
},
nil, // Response.
"Invalid properties for",
)
}
func TestMultiFileJinjaMissing(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{"{% include 'templates/secondary.jinja' %}"}),
},
},
},
},
nil, // Response
"TemplateNotFound: templates/secondary.jinja",
)
}
func TestMultiFilePython(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: pyExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.py",
Content: content([]string{
"from templates import second",
"import templates.third",
"def GenerateConfig(ctx):",
" t2 = second.Gen()",
" t3 = templates.third.Gen()",
" return t2",
}),
},
{
Path: "templates/second.py",
Content: content([]string{
"def Gen():",
" return '''resources:",
"- name: foo",
" type: bar",
"'''",
}),
},
{
Path: "templates/third.py",
Content: content([]string{
"def Gen():",
" return '''resources:",
"- name: foo",
" type: bar",
"'''",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"type": "bar",
},
},
},
"", // Error
)
}
func TestMultiFilePythonMissing(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: pyExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.py",
Content: content([]string{
"from templates import second",
}),
},
},
},
},
nil, // Response
"cannot import name second", // Error
)
}
func TestWrongChartName(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: "WrongName",
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{"resources:"}),
},
},
},
},
nil, // Response
"Request chart invocation does not match provided chart",
)
}
func TestEntrypointNotFound(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{},
},
},
nil, // Response
"The entrypoint in the chart.yaml cannot be found",
)
}
func TestMalformedResource(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"fail",
}),
},
},
},
},
nil, // Response
"could not found expected ':'", // [sic]
)
}
func TestResourceNoName(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- type: bar",
}),
},
},
},
},
nil, // Response.
"Resource does not have a name",
)
}
func TestResourceNoType(t *testing.T) {
testExpansion(
t,
&common.ExpansionRequest{
ChartInvocation: &common.Resource{
Name: "test_invocation",
Type: funcName(),
},
Chart: &chart.Content{
Chartfile: &chart.Chartfile{
Name: funcName(),
Expander: jinjaExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.jinja",
Content: content([]string{
"resources:",
"- name: foo",
}),
},
},
},
},
nil, // Response.
"Resource does not have type defined",
)
} }
*/

Loading…
Cancel
Save