Commit 5ed1dc25 authored by Dave Cunningham's avatar Dave Cunningham

Write some new unit tests for expandybird

parent 69f31b9e
...@@ -58,6 +58,11 @@ func (e *expander) ExpandChart(request *common.ExpansionRequest) (*common.Expans ...@@ -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 ...@@ -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. ...@@ -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{
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: pyExpander,
},
Members: []*chart.Member{
{
Path: "templates/main.py",
Content: content([]string{
"def GenerateConfig(ctx):",
" return '''resources:",
"- name: foo",
" type: %(prop2)s",
" properties:",
" something: %(prop1)s",
"''' % ctx.properties",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"properties": map[string]interface{}{
"something": 3.0,
},
"type": "foo",
},
},
},
"", // Error
)
} }
func TestExpandTemplateFromReader(t *testing.T) { func TestMultiFileJinja(t *testing.T) {
baseName := path.Base(validFileName) testExpansion(
testExpandTemplateFromFile(t, validFileName, baseName, importFileNames, util.NewTemplateFromReader) 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' %}"}),
},
{
Path: "templates/secondary.jinja",
Content: content([]string{
"resources:",
"- name: foo",
" type: bar",
}),
},
},
},
},
&common.ExpansionResponse{
Resources: []interface{}{
map[string]interface{}{
"name": "foo",
"type": "bar",
},
},
},
"", // Error
)
} }
func TestExpandTemplateFromArchive(t *testing.T) { var schemaContent = content([]string{
baseName := path.Base(validFileName) `{`,
testExpandTemplateFromFile(t, archiveFileName, baseName, nil, util.NewTemplateFromArchive) ` "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
)
} }
var ExpanderTestCases = []ExpanderTestCase{ 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{
{ {
"expect error for invalid file name", Path: "Schema.yaml",
"../test/InvalidFileName.yaml", Content: schemaContent,
importFileNames,
"ExpansionError: Exception",
}, },
{ {
"expect error for invalid property", Path: "templates/main.jinja",
"../test/InvalidProperty.yaml", Content: content([]string{
importFileNames, "resources:",
"ExpansionError: Exception", "- 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{
{ {
"expect error for malformed content", Path: "templates/main.jinja",
"../test/MalformedContent.yaml", Content: content([]string{"{% include 'templates/secondary.jinja' %}"}),
importFileNames, },
"ExpansionError: Error parsing YAML: mapping values are not allowed here", },
}, },
},
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{
{ {
"expect error for missing imports", Path: "templates/main.py",
"../test/MissingImports.yaml", Content: content([]string{
importFileNames, "from templates import second",
"ExpansionError: Exception", "import templates.third",
"def GenerateConfig(ctx):",
" t2 = second.Gen()",
" t3 = templates.third.Gen()",
" return t2",
}),
}, },
{ {
"expect error for missing resource name", Path: "templates/second.py",
"../test/MissingResourceName.yaml", Content: content([]string{
importFileNames, "def Gen():",
"ExpansionError: Resource does not have a name", " return '''resources:",
"- name: foo",
" type: bar",
"'''",
}),
}, },
{ {
"expect error for missing type name", Path: "templates/third.py",
"../test/MissingTypeName.yaml", Content: content([]string{
importFileNames, "def Gen():",
"ExpansionError: Resource does not have type defined", " 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{
{ {
"expect success", Path: "templates/main.py",
validFileName, Content: content([]string{
importFileNames, "from templates import second",
"", }),
},
},
},
}, },
nil, // Response
"cannot import name second", // Error
)
} }
func TestExpandTemplate(t *testing.T) { func TestWrongChartName(t *testing.T) {
backend := NewExpander(expanderName) testExpansion(
for _, etc := range ExpanderTestCases { t,
template := etc.GetTemplate(t) &common.ExpansionRequest{
actualOutput, err := backend.ExpandTemplate(template) ChartInvocation: &common.Resource{
if err != nil { Name: "test_invocation",
message := err.Error() Type: funcName(),
if !strings.Contains(message, etc.ExpectedError) { },
t.Fatalf("error in test case '%s': %s\n", etc.Description, message) Chart: &chart.Content{
} Chartfile: &chart.Chartfile{
} else { Name: "WrongName",
if etc.ExpectedError != "" { Expander: jinjaExpander,
t.Fatalf("expected error did not occur in test case '%s': %s\n", },
etc.Description, etc.ExpectedError) Members: []*chart.Member{
} {
Path: "templates/main.jinja",
Content: content([]string{"resources:"}),
},
},
},
},
nil, // Response
"Request chart invocation does not match provided chart",
)
}
expandAndVerifyOutput(t, actualOutput, etc.Description) 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",
)
} }
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment