Remove the need for reflect for testing config

* This also makes some minor newline changes to output.
* This also changes the behavior of nil objects to compare equal to
  other nil objects. This follows the pattern of builtin Go types.

Change-Id: I10d99bbd5251db594a302ca65cd21917804d6a20
This commit is contained in:
Ian Howell 2019-11-06 16:34:52 -06:00
parent 40f79cb9dc
commit 70f49926ba
14 changed files with 136 additions and 104 deletions

View File

@ -103,7 +103,7 @@ func getClusters(out io.Writer, rootSettings *environment.AirshipCTLSettings) er
fmt.Fprint(out, "No clusters found in the configuration.\n") fmt.Fprint(out, "No clusters found in the configuration.\n")
} }
for _, cluster := range clusters { for _, cluster := range clusters {
fmt.Fprintf(out, "%s", cluster.PrettyString()) fmt.Fprintf(out, "%s\n", cluster.PrettyString())
} }
return nil return nil
} }

View File

@ -75,7 +75,7 @@ func TestGetAllClusters(t *testing.T) {
clusters, err := conf.GetClusters() clusters, err := conf.GetClusters()
require.NoError(t, err) require.NoError(t, err)
for _, cluster := range clusters { for _, cluster := range clusters {
expected += fmt.Sprintf("%s", cluster.PrettyString()) expected += fmt.Sprintf("%s\n", cluster.PrettyString())
} }
test := getClusterTest{ test := getClusterTest{

View File

@ -497,7 +497,7 @@ func (c *Config) Purge() error {
func (c *Config) Equal(d *Config) bool { func (c *Config) Equal(d *Config) bool {
if d == nil { if d == nil {
return false return d == c
} }
clusterEq := reflect.DeepEqual(c.Clusters, d.Clusters) clusterEq := reflect.DeepEqual(c.Clusters, d.Clusters)
authInfoEq := reflect.DeepEqual(c.AuthInfos, d.AuthInfos) authInfoEq := reflect.DeepEqual(c.AuthInfos, d.AuthInfos)
@ -512,7 +512,7 @@ func (c *Config) Equal(d *Config) bool {
// Cluster functions // Cluster functions
func (c *Cluster) Equal(d *Cluster) bool { func (c *Cluster) Equal(d *Cluster) bool {
if d == nil { if d == nil {
return false return d == c
} }
return c.NameInKubeconf == d.NameInKubeconf && return c.NameInKubeconf == d.NameInKubeconf &&
c.Bootstrap == d.Bootstrap c.Bootstrap == d.Bootstrap
@ -536,8 +536,8 @@ func (c *Cluster) PrettyString() string {
clusterName := NewClusterComplexName() clusterName := NewClusterComplexName()
clusterName.FromName(c.NameInKubeconf) clusterName.FromName(c.NameInKubeconf)
return fmt.Sprintf("Cluster: %s\n%s:\n%s\n", return fmt.Sprintf("Cluster: %s\n%s:\n%s",
clusterName.ClusterName(), clusterName.ClusterType(), c.String()) clusterName.ClusterName(), clusterName.ClusterType(), c)
} }
func (c *Cluster) KubeCluster() *kubeconfig.Cluster { func (c *Cluster) KubeCluster() *kubeconfig.Cluster {
@ -550,11 +550,12 @@ func (c *Cluster) SetKubeCluster(kc *kubeconfig.Cluster) {
// Context functions // Context functions
func (c *Context) Equal(d *Context) bool { func (c *Context) Equal(d *Context) bool {
if d == nil { if d == nil {
return false return d == c
} }
return c.NameInKubeconf == d.NameInKubeconf && return c.NameInKubeconf == d.NameInKubeconf &&
c.Manifest == d.Manifest c.Manifest == d.Manifest
} }
func (c *Context) String() string { func (c *Context) String() string {
yaml, err := yaml.Marshal(&c) yaml, err := yaml.Marshal(&c)
if err != nil { if err != nil {
@ -566,7 +567,7 @@ func (c *Context) String() string {
// AuthInfo functions // AuthInfo functions
func (c *AuthInfo) Equal(d *AuthInfo) bool { func (c *AuthInfo) Equal(d *AuthInfo) bool {
if d == nil { if d == nil {
return false return d == c
} }
return c == d return c == d
} }
@ -582,11 +583,12 @@ func (c *AuthInfo) String() string {
// Manifest functions // Manifest functions
func (m *Manifest) Equal(n *Manifest) bool { func (m *Manifest) Equal(n *Manifest) bool {
if n == nil { if n == nil {
return false return n == m
} }
repositoryEq := reflect.DeepEqual(m.Repositories, n.Repositories) repositoryEq := reflect.DeepEqual(m.Repositories, n.Repositories)
return repositoryEq && m.TargetPath == n.TargetPath return repositoryEq && m.TargetPath == n.TargetPath
} }
func (m *Manifest) String() string { func (m *Manifest) String() string {
yaml, err := yaml.Marshal(&m) yaml, err := yaml.Marshal(&m)
if err != nil { if err != nil {
@ -598,11 +600,16 @@ func (m *Manifest) String() string {
// Repository functions // Repository functions
func (r *Repository) Equal(s *Repository) bool { func (r *Repository) Equal(s *Repository) bool {
if s == nil { if s == nil {
return false return r == s
} }
url := (r.Url == nil && s.Url == nil) || var urlMatches bool
(r.Url != nil && s.Url != nil && r.Url.String() == s.Url.String()) if r.Url != nil && s.Url != nil {
return url && urlMatches = (r.Url.String() == s.Url.String())
} else {
// this catches cases where one or both are nil
urlMatches = (r.Url == s.Url)
}
return urlMatches &&
r.Username == s.Username && r.Username == s.Username &&
r.TargetPath == s.TargetPath r.TargetPath == s.TargetPath
} }
@ -616,8 +623,10 @@ func (r *Repository) String() string {
// Modules functions // Modules functions
func (m *Modules) Equal(n *Modules) bool { func (m *Modules) Equal(n *Modules) bool {
if n == nil {
return n != nil && m.Dummy == n.Dummy return n == m
}
return m.Dummy == n.Dummy
} }
func (m *Modules) String() string { func (m *Modules) String() string {
yaml, err := yaml.Marshal(&m) yaml, err := yaml.Marshal(&m)

View File

@ -17,109 +17,133 @@ limitations under the License.
package config package config
import ( import (
"io/ioutil" "fmt"
"os" "os"
"path/filepath" "path/filepath"
"reflect"
"strings"
"testing" "testing"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
"k8s.io/client-go/tools/clientcmd" "k8s.io/client-go/tools/clientcmd"
"opendev.org/airship/airshipctl/testutil"
) )
// Testing related constants
var AirshipStructs = [...]reflect.Value{
reflect.ValueOf(DummyConfig()),
reflect.ValueOf(DummyCluster()),
reflect.ValueOf(DummyContext()),
reflect.ValueOf(DummyManifest()),
reflect.ValueOf(DummyAuthInfo()),
reflect.ValueOf(DummyRepository()),
reflect.ValueOf(DummyModules()),
}
// I can probable reflect to generate this two slices, instead based on the 1st one
// Exercise left for later -- YES I will remove this comment in the next patchset
var AirshipStructsEqual = [...]reflect.Value{
reflect.ValueOf(DummyConfig()),
reflect.ValueOf(DummyCluster()),
reflect.ValueOf(DummyContext()),
reflect.ValueOf(DummyManifest()),
reflect.ValueOf(DummyAuthInfo()),
reflect.ValueOf(DummyRepository()),
reflect.ValueOf(DummyModules()),
}
var AirshipStructsDiff = [...]reflect.Value{
reflect.ValueOf(NewConfig()),
reflect.ValueOf(NewCluster()),
reflect.ValueOf(NewContext()),
reflect.ValueOf(NewManifest()),
reflect.ValueOf(NewAuthInfo()),
reflect.ValueOf(NewRepository()),
reflect.ValueOf(NewModules()),
}
// Test to complete min coverage
func TestString(t *testing.T) { func TestString(t *testing.T) {
for s := range AirshipStructs { fSys := testutil.SetupTestFs(t, "testdata")
airStruct := AirshipStructs[s]
airStringMethod := airStruct.MethodByName("String")
yaml := airStringMethod.Call([]reflect.Value{})
require.NotNil(t, yaml)
structName := strings.Split(airStruct.Type().String(), ".") tests := []struct {
expectedFile := filepath.Join(testDataDir, "GoldenString", structName[1]+testMimeType) name string
expectedData, err := ioutil.ReadFile(expectedFile) stringer fmt.Stringer
assert.Nil(t, err) }{
require.EqualValues(t, string(expectedData), yaml[0].String()) {
name: "config",
stringer: DummyConfig(),
},
{
name: "context",
stringer: DummyContext(),
},
{
name: "cluster",
stringer: DummyCluster(),
},
{
name: "authinfo",
stringer: DummyAuthInfo(),
},
{
name: "manifest",
stringer: DummyManifest(),
},
{
name: "modules",
stringer: DummyModules(),
},
{
name: "repository",
stringer: DummyRepository(),
},
}
for _, tt := range tests {
tt := tt
t.Run(tt.name, func(t *testing.T) {
filename := fmt.Sprintf("/%s-string.yaml", tt.name)
data, err := fSys.ReadFile(filename)
require.NoError(t, err)
assert.Equal(t, string(data), tt.stringer.String())
})
} }
} }
func TestPrettyString(t *testing.T) { func TestPrettyString(t *testing.T) {
conf := InitConfig(t) fSys := testutil.SetupTestFs(t, "testdata")
cluster, err := conf.GetCluster("def", Ephemeral) data, err := fSys.ReadFile("/prettycluster-string.yaml")
require.NoError(t, err) require.NoError(t, err)
expectedFile := filepath.Join(testDataDir, "GoldenString", "PrettyCluster.yaml")
expectedData, err := ioutil.ReadFile(expectedFile)
assert.Nil(t, err)
assert.EqualValues(t, cluster.PrettyString(), string(expectedData))
cluster := DummyCluster()
assert.EqualValues(t, cluster.PrettyString(), string(data))
} }
func TestEqual(t *testing.T) { func TestEqual(t *testing.T) {
for s := range AirshipStructs { t.Run("config-equal", func(t *testing.T) {
airStruct := AirshipStructs[s] testConfig1 := NewConfig()
airStringMethod := airStruct.MethodByName("Equal") testConfig2 := NewConfig()
args := []reflect.Value{AirshipStructsEqual[s]} testConfig2.Kind = "Different"
eq := airStringMethod.Call(args) assert.True(t, testConfig1.Equal(testConfig1))
assert.NotNilf(t, eq, "Equal for %v failed to return response to Equal . ", airStruct.Type().String()) assert.False(t, testConfig1.Equal(testConfig2))
require.Truef(t, eq[0].Bool(), "Equal for %v failed to return true for equal values ", airStruct.Type().String()) assert.False(t, testConfig1.Equal(nil))
})
// Lets test Equals against nil struct t.Run("cluster-equal", func(t *testing.T) {
args = []reflect.Value{reflect.New(airStruct.Type()).Elem()} testCluster1 := &Cluster{NameInKubeconf: "same"}
nileq := airStringMethod.Call(args) testCluster2 := &Cluster{NameInKubeconf: "different"}
assert.NotNil(t, nileq, "Equal for %v failed to return response to Equal . ", airStruct.Type().String()) assert.True(t, testCluster1.Equal(testCluster1))
require.Falsef(t, nileq[0].Bool(), assert.False(t, testCluster1.Equal(testCluster2))
"Equal for %v failed to return false when comparing against nil value ", airStruct.Type().String()) assert.False(t, testCluster1.Equal(nil))
})
// Ignore False Equals test for AuthInfo for now t.Run("context-equal", func(t *testing.T) {
if airStruct.Type().String() == "*config.AuthInfo" { testContext1 := &Context{NameInKubeconf: "same"}
continue testContext2 := &Context{NameInKubeconf: "different"}
} assert.True(t, testContext1.Equal(testContext1))
// Lets test that equal returns false when they are diff assert.False(t, testContext1.Equal(testContext2))
args = []reflect.Value{AirshipStructsDiff[s]} assert.False(t, testContext1.Equal(nil))
neq := airStringMethod.Call(args) })
assert.NotNil(t, neq, "Equal for %v failed to return response to Equal . ", airStruct.Type().String())
require.Falsef(t, neq[0].Bool(),
"Equal for %v failed to return false for different values ", airStruct.Type().String())
} // TODO(howell): this needs to be fleshed out when the AuthInfo type is finished
t.Run("authinfo-equal", func(t *testing.T) {
testAuthInfo1 := &AuthInfo{}
assert.True(t, testAuthInfo1.Equal(testAuthInfo1))
assert.False(t, testAuthInfo1.Equal(nil))
})
t.Run("manifest-equal", func(t *testing.T) {
testManifest1 := &Manifest{TargetPath: "same"}
testManifest2 := &Manifest{TargetPath: "different"}
assert.True(t, testManifest1.Equal(testManifest1))
assert.False(t, testManifest1.Equal(testManifest2))
assert.False(t, testManifest1.Equal(nil))
})
t.Run("repository-equal", func(t *testing.T) {
testRepository1 := &Repository{TargetPath: "same"}
testRepository2 := &Repository{TargetPath: "different"}
assert.True(t, testRepository1.Equal(testRepository1))
assert.False(t, testRepository1.Equal(testRepository2))
assert.False(t, testRepository1.Equal(nil))
})
// TODO(howell): this needs to be fleshed out when the Modules type is finished
t.Run("modules-equal", func(t *testing.T) {
testModules1 := &Modules{Dummy: "same"}
testModules2 := &Modules{Dummy: "different"}
assert.True(t, testModules1.Equal(testModules1))
assert.False(t, testModules1.Equal(testModules2))
assert.False(t, testModules1.Equal(nil))
})
} }
func TestLoadConfig(t *testing.T) { func TestLoadConfig(t *testing.T) {

View File

@ -1,9 +0,0 @@
Cluster: def
ephemeral:
bootstrap-info: ""
cluster-kubeconf: def_ephemeral
LocationOfOrigin: ../../pkg/config/testdata/kubeconfig.yaml
insecure-skip-tls-verify: true
server: http://5.6.7.8

View File

@ -0,0 +1,8 @@
Cluster: dummycluster
target:
bootstrap-info: dummy_bootstrap
cluster-kubeconf: dummycluster_target
LocationOfOrigin: ""
certificate-authority: dummy_ca
server: http://dummy.server