-
bryanl authored
Signed-off-by:
bryanl <bryanliles@gmail.com>
Unverifiedacf99637
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
params_test.go 14.38 KiB
// Copyright 2018 The ksonnet authors
//
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package params
import (
"io"
"testing"
"github.com/google/go-jsonnet/ast"
"github.com/ksonnet/ksonnet-lib/ksonnet-gen/astext"
nm "github.com/ksonnet/ksonnet-lib/ksonnet-gen/nodemaker"
"github.com/ksonnet/ksonnet/pkg/util/test"
"github.com/pkg/errors"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// withParamConfig allows tests to change the params package settings without
// affecting other tests. It resets the following variables:
// * findValuesFn
// * jsonnetFieldIDFn
// * jsonnetFindObjectFn
// * jsonnetParseFn
// * jsonnetPrinterFn
// * jsonnetSetFn
// * nmKVFromMapFn
// * updateFn
func withParamConfig(t *testing.T, fn func()) {
ogConvertObjectToMapFn := convertObjectToMapFn
ogJsonnetFieldIDFn := jsonnetFieldIDFn
ogJsonnetFindObjectFn := jsonnetFindObjectFn
ogJsonnetParseFn := jsonnetParseFn
ogJsonnetPrinterFn := jsonnetPrinterFn
ogJsonnetSetFn := jsonnetSetFn
ogNmKVFromMapFn := nmKVFromMapFn
ogUpdateFn := updateFn
defer func() {
convertObjectToMapFn = ogConvertObjectToMapFn
jsonnetFieldIDFn = ogJsonnetFieldIDFn
jsonnetFindObjectFn = ogJsonnetFindObjectFn
jsonnetParseFn = ogJsonnetParseFn
jsonnetPrinterFn = ogJsonnetPrinterFn
jsonnetSetFn = ogJsonnetSetFn
nmKVFromMapFn = ogNmKVFromMapFn
updateFn = ogUpdateFn
}()
fn()
}
func Test_SetInObject(t *testing.T) {
withParamConfig(t, func() {
cases := []struct {
name string
paramsData string
root string
componentName string
fieldPath []string
value interface{}
updateFn func([]string, string, map[string]interface{}) (string, error)
isErr bool
}{
{
name: "update existing field",
paramsData: test.ReadTestData(t, "params.libsonnet"),
root: "components",
componentName: "guestbook-ui",
fieldPath: []string{"containerPort"},
value: 8080,
updateFn: func(sl []string, paramsData string, props map[string]interface{}) (string, error) {
assert.Equal(t, []string{"components", "guestbook-ui"}, sl)
m := map[string]interface{}{
"containerPort": 8080,
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"replicas": 1,
"servicePort": 80,
"type": "ClusterIP",
}
assert.Equal(t, m, props)
return paramsData, nil
},
},
{
name: "set nested field",
paramsData: test.ReadTestData(t, "params.libsonnet"),
root: "components",
componentName: "guestbook-ui",
fieldPath: []string{"nested", "field"},
value: "set",
updateFn: func(sl []string, paramsData string, props map[string]interface{}) (string, error) {
assert.Equal(t, []string{"components", "guestbook-ui"}, sl)
m := map[string]interface{}{
"containerPort": 80,
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"replicas": 1,
"servicePort": 80,
"type": "ClusterIP",
"nested": map[string]interface{}{
"field": "set",
},
}
assert.Equal(t, m, props)
return paramsData, nil
},
},
{
name: "set component global style",
paramsData: test.ReadTestData(t, "params.libsonnet"),
root: "global",
fieldPath: []string{"shared"},
value: "value",
updateFn: func(sl []string, paramsData string, props map[string]interface{}) (string, error) {
assert.Equal(t, []string{"global"}, sl)
m := map[string]interface{}{
"shared": "value",
"restart": false,
}
assert.Equal(t, m, props)
return paramsData, nil
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
updateFn = tc.updateFn
_, err := SetInObject(tc.fieldPath, tc.paramsData, tc.componentName, tc.value, tc.root)
if err != nil {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
})
}
func Test_DeleteFromObject(t *testing.T) {
withParamConfig(t, func() {
cases := []struct {
name string
paramsData string
root string
componentName string
fieldPath []string
updateFn func([]string, string, map[string]interface{}) (string, error)
isErr bool
}{
{
name: "delete existing field",
paramsData: test.ReadTestData(t, "params.libsonnet"),
root: "components",
componentName: "guestbook-ui",
fieldPath: []string{"containerPort"},
updateFn: func(sl []string, paramsData string, props map[string]interface{}) (string, error) {
assert.Equal(t, []string{"components", "guestbook-ui"}, sl)
m := map[string]interface{}{
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.1",
"name": "guestbook-ui",
"replicas": 1,
"servicePort": 80,
"type": "ClusterIP",
}
assert.Equal(t, m, props)
return paramsData, nil
},
},
{
name: "delete from global component param",
paramsData: test.ReadTestData(t, "params.libsonnet"),
root: "global",
fieldPath: []string{"restart"},
updateFn: func(sl []string, paramsData string, props map[string]interface{}) (string, error) {
assert.Equal(t, []string{"global"}, sl)
m := map[string]interface{}{}
assert.Equal(t, m, props)
return paramsData, nil
},
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
updateFn = tc.updateFn
_, err := DeleteFromObject(tc.fieldPath, tc.paramsData, tc.componentName, tc.root)
if err != nil {
require.Error(t, err)
return
}
require.NoError(t, err)
})
}
})
}
func Test_update(t *testing.T) {
cases := []struct {
name string
init func()
paramSource string
expected string
path []string
params map[string]interface{}
isErr bool
}{
{
name: "update params - functional",
paramSource: test.ReadTestData(t, "params.libsonnet"),
expected: test.ReadTestData(t, "updated.libsonnet"),
path: []string{"components", "guestbook-ui"},
params: map[string]interface{}{
"containerPort": 80,
"image": "gcr.io/heptio-images/ks-guestbook-demo:0.2",
"name": "guestbook-ui",
"replicas": 5,
"servicePort": 80,
"type": "NodePort",
},
},
{
name: "invalid source",
init: func() {
jsonnetParseFn = func(string, string) (*astext.Object, error) {
return nil, errors.New("failed")
}
},
isErr: true,
},
{
name: "invalid params",
init: func() {
jsonnetParseFn = func(string, string) (*astext.Object, error) {
return &astext.Object{}, nil
}
nmKVFromMapFn = func(map[string]interface{}) (*nm.Object, error) {
return nil, errors.New("failed")
}
},
isErr: true,
},
{
name: "unable to set in jsonnet",
init: func() {
jsonnetParseFn = func(string, string) (*astext.Object, error) {
return &astext.Object{}, nil
}
nmKVFromMapFn = func(map[string]interface{}) (*nm.Object, error) {
return &nm.Object{}, nil
}
jsonnetSetFn = func(*astext.Object, []string, ast.Node) error {
return errors.New("failed")
}
},
isErr: true,
},
{
name: "unable to print",
init: func() {
jsonnetParseFn = func(string, string) (*astext.Object, error) {
return &astext.Object{}, nil
}
nmKVFromMapFn = func(map[string]interface{}) (*nm.Object, error) {
return &nm.Object{}, nil
}
jsonnetSetFn = func(*astext.Object, []string, ast.Node) error {
return nil
}
jsonnetPrinterFn = func(io.Writer, ast.Node) error {
return errors.New("failed")
}
},
isErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
withParamConfig(t, func() {
if tc.init != nil {
tc.init()
}
got, err := update(tc.path, tc.paramSource, tc.params)
if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.expected, got)
})
})
}
}
func TestToMap(t *testing.T) {
cases := []struct {
name string
init func()
paramsData string
componentName string
expected map[string]interface{}
isErr bool
}{
{
name: "convert component params to a map - functional",
paramsData: test.ReadTestData(t, "nested-params.libsonnet"),
componentName: "guestbook-ui",
expected: map[string]interface{}{
"int": 80,
"float": 0.1,
"string": "string",
"string-key": "string-key",
"m": map[string]interface{}{
"a": "a",
"b": map[string]interface{}{
"c": "c",
},
},
"list": []interface{}{"one", "two", "three"},
},
},
{
name: "convert all component params to a map - functional",
paramsData: test.ReadTestData(t, "nested-params.libsonnet"),
expected: map[string]interface{}{
"guestbook-ui": map[string]interface{}{
"int": 80,
"float": 0.1,
"string": "string",
"string-key": "string-key",
"m": map[string]interface{}{
"a": "a",
"b": map[string]interface{}{
"c": "c",
},
},
"list": []interface{}{"one", "two", "three"},
},
"name": "name",
},
},
{
name: "component param is not an object - functional",
paramsData: test.ReadTestData(t, "nested-params.libsonnet"),
componentName: "name",
isErr: true,
},
{
name: "unable to convert object to map",
paramsData: test.ReadTestData(t, "nested-params.libsonnet"),
init: func() {
convertObjectToMapFn = func(*astext.Object) (map[string]interface{}, error) {
return nil, errors.New("failed")
}
},
isErr: true,
},
{
name: "invalid source",
init: func() {
jsonnetParseFn = func(string, string) (*astext.Object, error) {
return nil, errors.New("failed")
}
},
isErr: true,
},
{
name: "unsupported value in param object",
paramsData: test.ReadTestData(t, "nested-params.libsonnet"),
componentName: "guestbook-ui",
init: func() {
convertObjectToMapFn = func(*astext.Object) (map[string]interface{}, error) {
return nil, errors.New("failed")
}
},
isErr: true,
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
withParamConfig(t, func() {
if tc.init != nil {
tc.init()
}
got, err := ToMap(tc.componentName, tc.paramsData, "components")
if tc.isErr {
require.Error(t, err)
return
}
require.NoError(t, err)
assert.Equal(t, tc.expected, got)
})
})
}
}
func TestDecodeValue(t *testing.T) {
cases := []struct {
name string
val string
expected interface{}
isErr bool
}{
{
name: "blank",
val: "",
isErr: true,
},
{
name: "float",
val: "0.9",
expected: 0.9,
},
{
name: "int",
val: "9",
expected: 9,
},
{
name: "0",
val: "0",
expected: 0,
},
{
name: "bool true",
val: "True",
expected: true,
},
{
name: "bool false",
val: "false",
expected: false,
},
{
name: "array string",
val: `["a", "b", "c"]`,
expected: []interface{}{"a", "b", "c"},
},
{
name: "broken array",
val: `["a", "b", "c"`,
isErr: true,
},
{
name: "array float",
val: `[1,2,3]`,
expected: []interface{}{1.0, 2.0, 3.0},
},
{
name: "map",
val: `{"a": "1", "b": "2"}`,
expected: map[string]interface{}{
"a": "1",
"b": "2",
},
},
{
name: "broken map",
val: `{"a": "1", "b": "2"`,
isErr: true,
},
{
name: "nested map",
val: `{"a": "1", "b": "2", "c": {"d": "3"}}`,
expected: map[string]interface{}{
"a": "1",
"b": "2",
"c": map[string]interface{}{
"d": "3",
},
},
},
{
name: "string",
val: "foo",
expected: "foo",
},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
v, err := DecodeValue(tc.val)
if tc.isErr {
require.Error(t, err)
} else {
require.Equal(t, tc.expected, v)
}
})
}
}
func Test_mergeMaps(t *testing.T) {
m1 := map[string]interface{}{
"apiVersion": "apiextensions.k8s.io/v1beta1",
"kind": "CustomResourceDefinition",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"app": "cert-manager",
"chart": "cert-manager-0.2.2",
"heritage": "Tiller",
"release": "cert-manager",
},
"name": "certificates.certmanager.k8s.io",
},
"spec": map[string]interface{}{
"version": "v1",
"group": "certmanager.k8s.io",
"names": map[string]interface{}{
"kind": "Certificate",
"plural": "certificates",
},
"scope": "Namespaced",
},
}
m2 := map[string]interface{}{
"spec": map[string]interface{}{
"version": "v2",
},
}
expected := map[string]interface{}{
"apiVersion": "apiextensions.k8s.io/v1beta1",
"kind": "CustomResourceDefinition",
"metadata": map[string]interface{}{
"labels": map[string]interface{}{
"app": "cert-manager",
"chart": "cert-manager-0.2.2",
"heritage": "Tiller",
"release": "cert-manager",
},
"name": "certificates.certmanager.k8s.io",
},
"spec": map[string]interface{}{
"version": "v2",
"group": "certmanager.k8s.io",
"names": map[string]interface{}{
"kind": "Certificate",
"plural": "certificates",
},
"scope": "Namespaced",
},
}
err := mergeMaps(m1, m2, nil)
require.NoError(t, err)
require.Equal(t, expected, m1)
}
func Test_mergeMaps_simple(t *testing.T) {
m1 := map[string]interface{}{
"a": 1,
"b": 2,
}
m2 := map[string]interface{}{
"b": 4,
}
expected := map[string]interface{}{
"a": 1,
"b": 4,
}
err := mergeMaps(m1, m2, nil)
require.NoError(t, err)
require.Equal(t, expected, m1)
}