mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-15 11:46:06 +00:00
Add Unit test cases for pkg/cmd/util/output (#6359)
Signed-off-by: Daniel Jiang <jiangd@vmware.com>
This commit is contained in:
@@ -98,3 +98,9 @@ func (b *PodVolumeBackupBuilder) Volume(volume string) *PodVolumeBackupBuilder {
|
||||
b.object.Spec.Volume = volume
|
||||
return b
|
||||
}
|
||||
|
||||
// UploaderType sets the type of uploader to use for this PodVolumeBackup.
|
||||
func (b *PodVolumeBackupBuilder) UploaderType(uploaderType string) *PodVolumeBackupBuilder {
|
||||
b.object.Spec.UploaderType = uploaderType
|
||||
return b
|
||||
}
|
||||
|
||||
@@ -48,8 +48,8 @@ func (v *VolumeSnapshotContentBuilder) Result() *snapshotv1api.VolumeSnapshotCon
|
||||
}
|
||||
|
||||
// Status initiates VolumeSnapshotContent's status.
|
||||
func (v *VolumeSnapshotContentBuilder) Status() *VolumeSnapshotContentBuilder {
|
||||
v.object.Status = &snapshotv1api.VolumeSnapshotContentStatus{}
|
||||
func (v *VolumeSnapshotContentBuilder) Status(status *snapshotv1api.VolumeSnapshotContentStatus) *VolumeSnapshotContentBuilder {
|
||||
v.object.Status = status
|
||||
return v
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,291 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
"time"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
"github.com/stretchr/testify/assert"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
"github.com/vmware-tanzu/velero/pkg/features"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
)
|
||||
|
||||
func TestDescribeResourcePolicies(t *testing.T) {
|
||||
input := &v1.TypedLocalObjectReference{
|
||||
Kind: "configmap",
|
||||
Name: "test-resource-policy",
|
||||
}
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
DescribeResourcePolicies(d, input)
|
||||
d.out.Flush()
|
||||
expect := `Resource policies:
|
||||
Type: configmap
|
||||
Name: test-resource-policy
|
||||
`
|
||||
assert.Equal(t, expect, d.buf.String())
|
||||
}
|
||||
|
||||
func TestDescribeBackupSpec(t *testing.T) {
|
||||
input1 := builder.ForBackup("test-ns", "test-backup-1").
|
||||
IncludedNamespaces("inc-ns-1", "inc-ns-2").
|
||||
ExcludedNamespaces("exc-ns-1", "exc-ns-2").
|
||||
IncludedResources("inc-res-1", "inc-res-2").
|
||||
ExcludedResources("exc-res-1", "exc-res-2").
|
||||
StorageLocation("backup-location").
|
||||
TTL(72 * time.Hour).
|
||||
CSISnapshotTimeout(10 * time.Minute).
|
||||
DataMover("mover").
|
||||
Result().Spec
|
||||
|
||||
expect1 := `Namespaces:
|
||||
Included: inc-ns-1, inc-ns-2
|
||||
Excluded: exc-ns-1, exc-ns-2
|
||||
|
||||
Resources:
|
||||
Included: inc-res-1, inc-res-2
|
||||
Excluded: exc-res-1, exc-res-2
|
||||
Cluster-scoped: auto
|
||||
|
||||
Label selector: <none>
|
||||
|
||||
Storage Location: backup-location
|
||||
|
||||
Velero-Native Snapshot PVs: auto
|
||||
Snapshot Move Data: auto
|
||||
Data Mover: mover
|
||||
|
||||
TTL: 72h0m0s
|
||||
|
||||
CSISnapshotTimeout: 10m0s
|
||||
ItemOperationTimeout: 0s
|
||||
|
||||
Hooks: <none>
|
||||
`
|
||||
|
||||
input2 := builder.ForBackup("test-ns", "test-backup-2").
|
||||
IncludedNamespaces("inc-ns-1", "inc-ns-2").
|
||||
ExcludedNamespaces("exc-ns-1", "exc-ns-2").
|
||||
IncludedClusterScopedResources("inc-cluster-res-1", "inc-cluster-res-2").
|
||||
IncludedNamespaceScopedResources("inc-ns-res-1", "inc-ns-res-2").
|
||||
ExcludedClusterScopedResources("exc-cluster-res-1", "exc-cluster-res-2").
|
||||
ExcludedNamespaceScopedResources("exc-ns-res-1", "exc-ns-res-2").
|
||||
StorageLocation("backup-location").
|
||||
TTL(72 * time.Hour).
|
||||
CSISnapshotTimeout(10 * time.Minute).
|
||||
DataMover("mover").
|
||||
Result().Spec
|
||||
|
||||
expect2 := `Namespaces:
|
||||
Included: inc-ns-1, inc-ns-2
|
||||
Excluded: exc-ns-1, exc-ns-2
|
||||
|
||||
Resources:
|
||||
Included cluster-scoped: inc-cluster-res-1, inc-cluster-res-2
|
||||
Excluded cluster-scoped: exc-cluster-res-1, exc-cluster-res-2
|
||||
Included namespace-scoped: inc-ns-res-1, inc-ns-res-2
|
||||
Excluded namespace-scoped: exc-ns-res-1, exc-ns-res-2
|
||||
|
||||
Label selector: <none>
|
||||
|
||||
Storage Location: backup-location
|
||||
|
||||
Velero-Native Snapshot PVs: auto
|
||||
Snapshot Move Data: auto
|
||||
Data Mover: mover
|
||||
|
||||
TTL: 72h0m0s
|
||||
|
||||
CSISnapshotTimeout: 10m0s
|
||||
ItemOperationTimeout: 0s
|
||||
|
||||
Hooks: <none>
|
||||
`
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
input velerov1api.BackupSpec
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "old resource filter",
|
||||
input: input1,
|
||||
expect: expect1,
|
||||
},
|
||||
{
|
||||
name: "new resource filter",
|
||||
input: input2,
|
||||
expect: expect2,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
DescribeBackupSpec(d, tc.input)
|
||||
d.out.Flush()
|
||||
assert.Equal(tt, tc.expect, d.buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeSnapshot(t *testing.T) {
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
describeSnapshot(d, "pv-1", "snapshot-1", "ebs", "us-east-2", nil)
|
||||
expect1 := ` pv-1:
|
||||
Snapshot ID: snapshot-1
|
||||
Type: ebs
|
||||
Availability Zone: us-east-2
|
||||
IOPS: <N/A>
|
||||
`
|
||||
d.out.Flush()
|
||||
assert.Equal(t, expect1, d.buf.String())
|
||||
}
|
||||
|
||||
func TestDescribePodVolumeBackups(t *testing.T) {
|
||||
pvb1 := builder.ForPodVolumeBackup("test-ns", "test-pvb1").
|
||||
BackupStorageLocation("backup-location").
|
||||
UploaderType("kopia").
|
||||
Phase(velerov1api.PodVolumeBackupPhaseCompleted).
|
||||
BackupStorageLocation("bsl-1").
|
||||
Volume("vol-1").
|
||||
PodName("pod-1").
|
||||
PodNamespace("pod-ns-1").
|
||||
SnapshotID("snap-1").Result()
|
||||
pvb2 := builder.ForPodVolumeBackup("test-ns1", "test-pvb2").
|
||||
BackupStorageLocation("backup-location").
|
||||
UploaderType("kopia").
|
||||
Phase(velerov1api.PodVolumeBackupPhaseCompleted).
|
||||
BackupStorageLocation("bsl-1").
|
||||
Volume("vol-2").
|
||||
PodName("pod-2").
|
||||
PodNamespace("pod-ns-1").
|
||||
SnapshotID("snap-2").Result()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
inputPVBList []velerov1api.PodVolumeBackup
|
||||
inputDetails bool
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
inputPVBList: []velerov1api.PodVolumeBackup{},
|
||||
inputDetails: true,
|
||||
expect: ``,
|
||||
},
|
||||
{
|
||||
name: "2 completed pvbs no details",
|
||||
inputPVBList: []velerov1api.PodVolumeBackup{*pvb1, *pvb2},
|
||||
inputDetails: false,
|
||||
expect: `kopia Backups (specify --details for more information):
|
||||
Completed: 2
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "2 completed pvbs with details",
|
||||
inputPVBList: []velerov1api.PodVolumeBackup{*pvb1, *pvb2},
|
||||
inputDetails: true,
|
||||
expect: `kopia Backups:
|
||||
Completed:
|
||||
pod-ns-1/pod-1: vol-1
|
||||
pod-ns-1/pod-2: vol-2
|
||||
`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
DescribePodVolumeBackups(d, tc.inputPVBList, tc.inputDetails)
|
||||
d.out.Flush()
|
||||
assert.Equal(tt, tc.expect, d.buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeCSIVolumeSnapshots(t *testing.T) {
|
||||
features.Enable(velerov1api.CSIFeatureFlag)
|
||||
defer func() {
|
||||
features.Disable(velerov1api.CSIFeatureFlag)
|
||||
}()
|
||||
handle := "handle-1"
|
||||
readyToUse := true
|
||||
size := int64(1024)
|
||||
vsc1 := builder.ForVolumeSnapshotContent("vsc-1").
|
||||
Status(&snapshotv1api.VolumeSnapshotContentStatus{
|
||||
SnapshotHandle: &handle,
|
||||
ReadyToUse: &readyToUse,
|
||||
RestoreSize: &size,
|
||||
}).Result()
|
||||
testcases := []struct {
|
||||
name string
|
||||
inputVSCList []snapshotv1api.VolumeSnapshotContent
|
||||
inputDetails bool
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{},
|
||||
inputDetails: false,
|
||||
expect: `CSI Volume Snapshots: <none included>
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "1 vsc no details",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{*vsc1},
|
||||
inputDetails: false,
|
||||
expect: `CSI Volume Snapshots: 1 included (specify --details for more information)
|
||||
`,
|
||||
},
|
||||
{
|
||||
name: "1 vsc with details",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{*vsc1},
|
||||
inputDetails: true,
|
||||
expect: `CSI Volume Snapshots:
|
||||
Snapshot Content Name: vsc-1
|
||||
Storage Snapshot ID: handle-1
|
||||
Snapshot Size (bytes): 1024
|
||||
Ready to use: true
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
d := &Describer{
|
||||
Prefix: "",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 2, ' ', 0)
|
||||
DescribeCSIVolumeSnapshots(d, tc.inputDetails, tc.inputVSCList)
|
||||
d.out.Flush()
|
||||
assert.Equal(tt, tc.expect, d.buf.String())
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -437,14 +437,13 @@ func DescribeCSIVolumeSnapshotsInSF(d *StructuredDescriber, details bool, volume
|
||||
// the field of 'CSI Volume Snapshots Details' displays the content of CSI Volume Snapshots
|
||||
if !details {
|
||||
CSIVolumeSnapshotsInfo["CSIVolumeSnapshotsCount"] = len(volumeSnapshotContents)
|
||||
return
|
||||
} else {
|
||||
vscDetails := make(map[string]interface{})
|
||||
for _, vsc := range volumeSnapshotContents {
|
||||
DescribeVSCInSF(details, vsc, vscDetails)
|
||||
}
|
||||
CSIVolumeSnapshotsInfo["CSIVolumeSnapshotsDetails"] = vscDetails
|
||||
}
|
||||
|
||||
vscDetails := make(map[string]interface{})
|
||||
for _, vsc := range volumeSnapshotContents {
|
||||
DescribeVSCInSF(details, vsc, vscDetails)
|
||||
}
|
||||
CSIVolumeSnapshotsInfo["CSIVolumeSnapshotsDetails"] = vscDetails
|
||||
d.Describe("CSIVolumeSnapshots", CSIVolumeSnapshotsInfo)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,252 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
v1 "k8s.io/api/core/v1"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/features"
|
||||
"github.com/vmware-tanzu/velero/pkg/util/results"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
|
||||
snapshotv1api "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1"
|
||||
)
|
||||
|
||||
func TestDescribeBackupInSF(t *testing.T) {
|
||||
sd := &StructuredDescriber{
|
||||
output: make(map[string]interface{}),
|
||||
format: "",
|
||||
}
|
||||
backupBuilder1 := builder.ForBackup("test-ns", "test-backup")
|
||||
backupBuilder1.IncludedNamespaces("inc-ns-1", "inc-ns-2").
|
||||
ExcludedNamespaces("exc-ns-1", "exc-ns-2").
|
||||
IncludedResources("inc-res-1", "inc-res-2").
|
||||
ExcludedResources("exc-res-1", "exc-res-2").
|
||||
StorageLocation("backup-location").
|
||||
TTL(72 * time.Hour).
|
||||
CSISnapshotTimeout(10 * time.Minute).
|
||||
DataMover("mover")
|
||||
|
||||
expect1 := map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"namespaces": map[string]interface{}{
|
||||
"included": "inc-ns-1, inc-ns-2",
|
||||
"excluded": "exc-ns-1, exc-ns-2",
|
||||
},
|
||||
"resources": map[string]string{
|
||||
"included": "inc-res-1, inc-res-2",
|
||||
"excluded": "exc-res-1, exc-res-2",
|
||||
"clusterScoped": "auto",
|
||||
},
|
||||
"dataMover": "mover",
|
||||
"labelSelector": emptyDisplay,
|
||||
"storageLocation": "backup-location",
|
||||
"veleroNativeSnapshotPVs": "auto",
|
||||
"TTL": "72h0m0s",
|
||||
"CSISnapshotTimeout": "10m0s",
|
||||
"veleroSnapshotMoveData": "auto",
|
||||
},
|
||||
}
|
||||
DescribeBackupSpecInSF(sd, backupBuilder1.Result().Spec)
|
||||
assert.True(t, reflect.DeepEqual(sd.output, expect1))
|
||||
|
||||
backupBuilder2 := builder.ForBackup("test-ns-2", "test-backup-2")
|
||||
backupBuilder2.StorageLocation("backup-location")
|
||||
expect2 := map[string]interface{}{
|
||||
"spec": map[string]interface{}{
|
||||
"namespaces": map[string]interface{}{
|
||||
"included": "*",
|
||||
"excluded": emptyDisplay,
|
||||
},
|
||||
"resources": map[string]string{
|
||||
"included": "*",
|
||||
"excluded": emptyDisplay,
|
||||
"clusterScoped": "auto",
|
||||
},
|
||||
"dataMover": emptyDisplay,
|
||||
"labelSelector": emptyDisplay,
|
||||
"storageLocation": "backup-location",
|
||||
"veleroNativeSnapshotPVs": "auto",
|
||||
"TTL": "0s",
|
||||
"CSISnapshotTimeout": "0s",
|
||||
"veleroSnapshotMoveData": "auto",
|
||||
},
|
||||
}
|
||||
DescribeBackupSpecInSF(sd, backupBuilder2.Result().Spec)
|
||||
assert.True(t, reflect.DeepEqual(sd.output, expect2))
|
||||
}
|
||||
|
||||
func TestDescribePodVolumeBackupsInSF(t *testing.T) {
|
||||
pvbBuilder1 := builder.ForPodVolumeBackup("test-ns1", "test-pvb1")
|
||||
pvb1 := pvbBuilder1.BackupStorageLocation("backup-location").
|
||||
UploaderType("kopia").
|
||||
Phase(velerov1api.PodVolumeBackupPhaseCompleted).
|
||||
BackupStorageLocation("bsl-1").
|
||||
Volume("vol-1").
|
||||
PodName("pod-1").
|
||||
PodNamespace("pod-ns-1").
|
||||
SnapshotID("snap-1").Result()
|
||||
|
||||
pvbBuilder2 := builder.ForPodVolumeBackup("test-ns1", "test-pvb2")
|
||||
pvb2 := pvbBuilder2.BackupStorageLocation("backup-location").
|
||||
UploaderType("kopia").
|
||||
Phase(velerov1api.PodVolumeBackupPhaseCompleted).
|
||||
BackupStorageLocation("bsl-1").
|
||||
Volume("vol-2").
|
||||
PodName("pod-2").
|
||||
PodNamespace("pod-ns-1").
|
||||
SnapshotID("snap-2").Result()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
inputPVBList []velerov1api.PodVolumeBackup
|
||||
inputDetails bool
|
||||
expect map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
inputPVBList: []velerov1api.PodVolumeBackup{},
|
||||
inputDetails: false,
|
||||
expect: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "2 completed pvbs",
|
||||
inputPVBList: []velerov1api.PodVolumeBackup{*pvb1, *pvb2},
|
||||
inputDetails: true,
|
||||
expect: map[string]interface{}{
|
||||
"podVolumeBackups": map[string]interface{}{
|
||||
"podVolumeBackupsDetails": map[string]interface{}{
|
||||
"Completed": []map[string]string{
|
||||
{"pod-ns-1/pod-1": "vol-1"},
|
||||
{"pod-ns-1/pod-2": "vol-2"},
|
||||
},
|
||||
},
|
||||
"type": "kopia",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
sd := &StructuredDescriber{
|
||||
output: make(map[string]interface{}),
|
||||
format: "",
|
||||
}
|
||||
DescribePodVolumeBackupsInSF(sd, tc.inputPVBList, tc.inputDetails)
|
||||
assert.True(tt, reflect.DeepEqual(sd.output, tc.expect))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeCSIVolumeSnapshotsInSF(t *testing.T) {
|
||||
features.Enable(velerov1api.CSIFeatureFlag)
|
||||
defer func() {
|
||||
features.Disable(velerov1api.CSIFeatureFlag)
|
||||
}()
|
||||
|
||||
vscBuilder1 := builder.ForVolumeSnapshotContent("vsc-1")
|
||||
handle := "handle-1"
|
||||
readyToUse := true
|
||||
size := int64(1024)
|
||||
vsc1 := vscBuilder1.Status(&snapshotv1api.VolumeSnapshotContentStatus{
|
||||
SnapshotHandle: &handle,
|
||||
ReadyToUse: &readyToUse,
|
||||
RestoreSize: &size,
|
||||
}).Result()
|
||||
|
||||
testcases := []struct {
|
||||
name string
|
||||
inputVSCList []snapshotv1api.VolumeSnapshotContent
|
||||
inputDetails bool
|
||||
expect map[string]interface{}
|
||||
}{
|
||||
{
|
||||
name: "empty list",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{},
|
||||
inputDetails: false,
|
||||
expect: map[string]interface{}{},
|
||||
},
|
||||
{
|
||||
name: "1 vsc no detail",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{*vsc1},
|
||||
inputDetails: false,
|
||||
expect: map[string]interface{}{
|
||||
"CSIVolumeSnapshots": map[string]interface{}{
|
||||
"CSIVolumeSnapshotsCount": 1,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "1 vsc with detail",
|
||||
inputVSCList: []snapshotv1api.VolumeSnapshotContent{*vsc1},
|
||||
inputDetails: true,
|
||||
expect: map[string]interface{}{
|
||||
"CSIVolumeSnapshots": map[string]interface{}{
|
||||
"CSIVolumeSnapshotsDetails": map[string]interface{}{
|
||||
"vsc-1": map[string]interface{}{
|
||||
"readyToUse": true,
|
||||
"snapshotSize(bytes)": int64(1024),
|
||||
"storageSnapshotID": "handle-1",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
sd := &StructuredDescriber{
|
||||
output: make(map[string]interface{}),
|
||||
format: "",
|
||||
}
|
||||
DescribeCSIVolumeSnapshotsInSF(sd, tc.inputDetails, tc.inputVSCList)
|
||||
assert.True(tt, reflect.DeepEqual(sd.output, tc.expect))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescribeResourcePoliciesInSF(t *testing.T) {
|
||||
input := &v1.TypedLocalObjectReference{
|
||||
Kind: "configmap",
|
||||
Name: "resource-policy-1",
|
||||
}
|
||||
expect := map[string]interface{}{
|
||||
"resourcePolicies": map[string]interface{}{
|
||||
"type": "configmap",
|
||||
"name": "resource-policy-1",
|
||||
},
|
||||
}
|
||||
sd := &StructuredDescriber{
|
||||
output: make(map[string]interface{}),
|
||||
format: "",
|
||||
}
|
||||
DescribeResourcePoliciesInSF(sd, input)
|
||||
assert.True(t, reflect.DeepEqual(sd.output, expect))
|
||||
}
|
||||
|
||||
func TestDescribeBackupResultInSF(t *testing.T) {
|
||||
input := results.Result{
|
||||
Velero: []string{"msg-1", "msg-2"},
|
||||
Cluster: []string{"cluster-1", "cluster-2"},
|
||||
Namespaces: map[string][]string{
|
||||
"ns-1": {"ns-1-msg-1", "ns-1-msg-2"},
|
||||
},
|
||||
}
|
||||
got := map[string]interface{}{}
|
||||
expect := map[string]interface{}{
|
||||
"velero": []string{"msg-1", "msg-2"},
|
||||
"cluster": []string{"cluster-1", "cluster-2"},
|
||||
"namespace": map[string][]string{
|
||||
"ns-1": {"ns-1-msg-1", "ns-1-msg-2"},
|
||||
},
|
||||
}
|
||||
describeResultInSF(got, input)
|
||||
assert.True(t, reflect.DeepEqual(got, expect))
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"testing"
|
||||
"text/tabwriter"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/stretchr/testify/assert"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
func TestBoolPointerString(t *testing.T) {
|
||||
trueStr := "true"
|
||||
falseStr := "FALSE"
|
||||
nilStr := "nul"
|
||||
truee := true
|
||||
falsee := false
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *bool
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "nil",
|
||||
input: nil,
|
||||
expect: nilStr,
|
||||
},
|
||||
{
|
||||
name: "true",
|
||||
input: &truee,
|
||||
expect: trueStr,
|
||||
},
|
||||
{
|
||||
name: "false",
|
||||
input: &falsee,
|
||||
expect: falseStr,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
got := BoolPointerString(tc.input, falseStr, trueStr, nilStr)
|
||||
assert.Equal(t, tc.expect, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDescriber_DescribeMetadata(t *testing.T) {
|
||||
input := metav1.ObjectMeta{
|
||||
Name: "test",
|
||||
Namespace: "test-ns",
|
||||
Labels: map[string]string{
|
||||
"test-key2": "v2",
|
||||
"test-key0": "v0",
|
||||
},
|
||||
Annotations: nil,
|
||||
}
|
||||
expect := new(bytes.Buffer)
|
||||
d := &Describer{
|
||||
Prefix: "pref-",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 0, ' ', 0)
|
||||
fmt.Fprintf(expect, "pref-Name: %s\n", color.New(color.Bold).SprintFunc()("test"))
|
||||
fmt.Fprintf(expect, "pref-Namespace: %s\n", "test-ns")
|
||||
fmt.Fprintf(expect, "pref-Labels: %s\n", "pref-test-key0=v0")
|
||||
fmt.Fprintf(expect, "pref- test-key2=v2\n")
|
||||
fmt.Fprintf(expect, "pref-Annotations:%s\n", "pref-<none>")
|
||||
d.DescribeMetadata(input)
|
||||
d.out.Flush()
|
||||
assert.Equal(t, expect.String(), d.buf.String())
|
||||
}
|
||||
|
||||
func TestDescriber_DescribeSlice(t *testing.T) {
|
||||
input := []string{"a", "b", "c"}
|
||||
expect := new(bytes.Buffer)
|
||||
d := &Describer{
|
||||
Prefix: "pref-",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d.out.Init(d.buf, 0, 8, 0, ' ', 0)
|
||||
fmt.Fprintf(expect, "pref-test:pref-a\n")
|
||||
fmt.Fprintf(expect, "pref- b\n")
|
||||
fmt.Fprintf(expect, "pref- c\n")
|
||||
d.DescribeSlice(4, "test", input)
|
||||
d.out.Flush()
|
||||
assert.Equal(t, expect.String(), d.buf.String())
|
||||
var input2 []string
|
||||
expect2 := new(bytes.Buffer)
|
||||
d2 := &Describer{
|
||||
Prefix: "pref-",
|
||||
out: &tabwriter.Writer{},
|
||||
buf: &bytes.Buffer{},
|
||||
}
|
||||
d2.out.Init(d2.buf, 0, 4, 0, ' ', 0)
|
||||
fmt.Fprintf(expect2, "pref-test:pref-<none>\n")
|
||||
d2.DescribeSlice(4, "test", input2)
|
||||
d2.out.Flush()
|
||||
assert.Equal(t, expect2.String(), d2.buf.String())
|
||||
}
|
||||
|
||||
func TestStructuredDescriber_JSONEncode(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
inputMap map[string]interface{}
|
||||
expect string
|
||||
}{
|
||||
{
|
||||
name: "invalid json",
|
||||
inputMap: map[string]interface{}{},
|
||||
expect: "{}\n",
|
||||
},
|
||||
{
|
||||
name: "valid json",
|
||||
inputMap: map[string]interface{}{"k1": "v1"},
|
||||
expect: `{
|
||||
"k1": "v1"
|
||||
}
|
||||
`,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(tt *testing.T) {
|
||||
d := &StructuredDescriber{
|
||||
output: tc.inputMap,
|
||||
}
|
||||
got := d.JSONEncode()
|
||||
assert.Equal(tt, tc.expect, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,382 @@
|
||||
package output
|
||||
|
||||
import (
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
|
||||
velerov1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestBindFlags(t *testing.T) {
|
||||
cmd := &cobra.Command{}
|
||||
BindFlags(cmd.Flags())
|
||||
assert.NotNil(t, cmd.Flags().Lookup("output"))
|
||||
assert.NotNil(t, cmd.Flags().Lookup("label-columns"))
|
||||
assert.NotNil(t, cmd.Flags().Lookup("show-labels"))
|
||||
assert.Nil(t, cmd.Flags().Lookup("not-exist"))
|
||||
}
|
||||
|
||||
func TestBindFlagsSimple(t *testing.T) {
|
||||
cmd := &cobra.Command{}
|
||||
BindFlagsSimple(cmd.Flags())
|
||||
assert.NotNil(t, cmd.Flags().Lookup("output"))
|
||||
assert.Nil(t, cmd.Flags().Lookup("label-columns"))
|
||||
assert.Nil(t, cmd.Flags().Lookup("show-labels"))
|
||||
}
|
||||
|
||||
func TestClearOutputFlagDefault(t *testing.T) {
|
||||
cmd := &cobra.Command{}
|
||||
ClearOutputFlagDefault(cmd)
|
||||
assert.Nil(t, cmd.Flags().Lookup("output"))
|
||||
BindFlags(cmd.Flags())
|
||||
cmd.Flags().Set("output", "json")
|
||||
ClearOutputFlagDefault(cmd)
|
||||
assert.Equal(t, "", cmd.Flags().Lookup("output").Value.String())
|
||||
}
|
||||
|
||||
func cmdWithFormat(use string, format string) *cobra.Command {
|
||||
cmd := &cobra.Command{
|
||||
Use: use,
|
||||
}
|
||||
BindFlags(cmd.Flags())
|
||||
cmd.Flags().Set("output", format)
|
||||
return cmd
|
||||
}
|
||||
|
||||
func TestValidateFlags(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *cobra.Command
|
||||
hasErr bool
|
||||
}{
|
||||
{
|
||||
name: "unknown format",
|
||||
input: cmdWithFormat("whatever", "unknown"),
|
||||
hasErr: true,
|
||||
},
|
||||
{
|
||||
name: "json format",
|
||||
input: cmdWithFormat("whatever", "json"),
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "yaml format",
|
||||
input: cmdWithFormat("whatever", "yaml"),
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "empty format",
|
||||
input: cmdWithFormat("whatever", ""),
|
||||
hasErr: false,
|
||||
},
|
||||
{
|
||||
name: "install with table format",
|
||||
input: cmdWithFormat("install", "table"),
|
||||
hasErr: true,
|
||||
},
|
||||
{
|
||||
name: "other with table format",
|
||||
input: cmdWithFormat("other", "table"),
|
||||
hasErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
err := ValidateFlags(tc.input)
|
||||
if tc.hasErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPrintWithFormat(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}
|
||||
hasErr bool
|
||||
printed bool
|
||||
}{
|
||||
{
|
||||
name: "empty format",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", ""),
|
||||
},
|
||||
hasErr: false,
|
||||
printed: false,
|
||||
},
|
||||
{
|
||||
name: "json format backup",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "json"),
|
||||
obj: &velerov1.Backup{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.Backup{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "json format backup list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "json"),
|
||||
obj: &velerov1.BackupList{
|
||||
Items: []velerov1.Backup{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.BackupList{
|
||||
Items: []velerov1.Backup{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.Backup{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format restore list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.RestoreList{
|
||||
Items: []velerov1.Restore{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format restore",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.Restore{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format schedule list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.ScheduleList{
|
||||
Items: []velerov1.Schedule{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format schedule",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.Schedule{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup repository list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.BackupRepositoryList{
|
||||
Items: []velerov1.BackupRepository{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup repository",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.BackupRepository{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup location list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.BackupStorageLocationList{
|
||||
Items: []velerov1.BackupStorageLocation{
|
||||
{
|
||||
Spec: velerov1.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1.StorageType{
|
||||
ObjectStorage: &velerov1.ObjectStorageLocation{
|
||||
Bucket: "bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format backup location",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.BackupStorageLocation{
|
||||
Spec: velerov1.BackupStorageLocationSpec{
|
||||
Provider: "aws",
|
||||
StorageType: velerov1.StorageType{
|
||||
ObjectStorage: &velerov1.ObjectStorageLocation{
|
||||
Bucket: "bucket",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format volume snapshot location list",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.VolumeSnapshotLocationList{
|
||||
Items: []velerov1.VolumeSnapshotLocation{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format volume snapshot location",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.VolumeSnapshotLocation{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format volume snapshot location",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.VolumeSnapshotLocation{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
{
|
||||
name: "table format plugin list via server status",
|
||||
input: struct {
|
||||
cmd *cobra.Command
|
||||
obj runtime.Object
|
||||
}{
|
||||
cmd: cmdWithFormat("describe", "table"),
|
||||
obj: &velerov1.ServerStatusRequest{},
|
||||
},
|
||||
hasErr: false,
|
||||
printed: true,
|
||||
},
|
||||
}
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
p, err := PrintWithFormat(tc.input.cmd, tc.input.obj)
|
||||
if tc.hasErr {
|
||||
assert.Error(t, err)
|
||||
} else {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
assert.Equal(t, tc.printed, p)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user