Add missing tests for kubectl describe commands (#136461)

* Add missing tests for kubectl describe commands

* fix linter error
This commit is contained in:
kfess 2026-01-28 20:07:52 +09:00 committed by GitHub
parent 091d147008
commit 3a0744ce6d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -33,12 +33,14 @@ import (
autoscalingv1 "k8s.io/api/autoscaling/v1"
autoscalingv2 "k8s.io/api/autoscaling/v2"
batchv1 "k8s.io/api/batch/v1"
certificatesv1 "k8s.io/api/certificates/v1"
coordinationv1 "k8s.io/api/coordination/v1"
corev1 "k8s.io/api/core/v1"
discoveryv1 "k8s.io/api/discovery/v1"
networkingv1 "k8s.io/api/networking/v1"
networkingv1beta1 "k8s.io/api/networking/v1beta1"
policyv1 "k8s.io/api/policy/v1"
rbacv1 "k8s.io/api/rbac/v1"
schedulingv1 "k8s.io/api/scheduling/v1"
storagev1 "k8s.io/api/storage/v1"
apiequality "k8s.io/apimachinery/pkg/api/equality"
@ -7254,3 +7256,686 @@ func TestSmartLabelFor(t *testing.T) {
})
}
}
func TestDescribeCronJob(t *testing.T) {
testCases := []struct {
name string
cronJob *batchv1.CronJob
expects []string
}{
{
name: "standard cronjob",
cronJob: &batchv1.CronJob{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
},
Spec: batchv1.CronJobSpec{
Schedule: "*/5 * * * *",
ConcurrencyPolicy: batchv1.ForbidConcurrent,
Suspend: ptr.To(false),
SuccessfulJobsHistoryLimit: ptr.To[int32](3),
FailedJobsHistoryLimit: ptr.To[int32](1),
StartingDeadlineSeconds: ptr.To[int64](200),
JobTemplate: batchv1.JobTemplateSpec{
Spec: batchv1.JobSpec{
Selector: &metav1.LabelSelector{},
Parallelism: ptr.To[int32](4),
Completions: ptr.To[int32](5),
ActiveDeadlineSeconds: ptr.To[int64](400),
Template: corev1.PodTemplateSpec{
Spec: corev1.PodSpec{},
},
},
},
},
},
expects: []string{
"Name: bar",
"Namespace: foo",
"Schedule: */5 * * * *",
"Concurrency Policy: Forbid",
"Suspend: False",
"Starting Deadline Seconds: 200",
"Successful Job History Limit: 3",
"Failed Job History Limit: 1",
"Starting Deadline Seconds: 200s",
"Parallelism: 4",
"Completions: 5",
"Active Deadline Seconds: 400s",
"Last Schedule Time: <unset>",
"Active Jobs: <none>",
"Events: <none>",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.cronJob)
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := CronJobDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestDescribeReplicaSet(t *testing.T) {
testCases := []struct {
name string
replicaSet *appsv1.ReplicaSet
pods []corev1.Pod
expects []string
}{
{
name: "replica set with one pod and one condition",
replicaSet: &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
UID: "test-uid",
},
Spec: appsv1.ReplicaSetSpec{
Replicas: ptr.To[int32](1),
},
Status: appsv1.ReplicaSetStatus{
Replicas: 1,
ReadyReplicas: 1,
AvailableReplicas: 1,
Conditions: []appsv1.ReplicaSetCondition{
{
Type: appsv1.ReplicaSetReplicaFailure,
Status: corev1.ConditionTrue,
Reason: "FailedCreate",
},
},
},
},
pods: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodRunning},
},
},
expects: []string{
"Name: bar",
"Namespace: foo",
"Replicas: 1 current / 1 desired",
"Pods Status: 1 Running / 0 Waiting / 0 Succeeded / 0 Failed",
"Conditions:",
"Type Status Reason",
"---- ------ ------",
"ReplicaFailure True FailedCreate",
},
},
{
name: "replica set with multiple pods",
replicaSet: &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
UID: "test-uid",
},
Spec: appsv1.ReplicaSetSpec{
Replicas: ptr.To[int32](2),
},
Status: appsv1.ReplicaSetStatus{
Replicas: 2,
ReadyReplicas: 2,
AvailableReplicas: 2,
},
},
pods: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodRunning},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod2",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodRunning},
},
},
expects: []string{
"Name: bar",
"Namespace: foo",
"Replicas: 2 current / 2 desired",
"Pods Status: 2 Running / 0 Waiting / 0 Succeeded / 0 Failed",
},
},
{
name: "replica set with different pod statuses",
replicaSet: &appsv1.ReplicaSet{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
UID: "test-uid",
},
Spec: appsv1.ReplicaSetSpec{
Replicas: ptr.To[int32](4),
},
Status: appsv1.ReplicaSetStatus{
Replicas: 4,
ReadyReplicas: 4,
AvailableReplicas: 4,
},
},
pods: []corev1.Pod{
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod1",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodRunning},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod2",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodPending},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod3",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodSucceeded},
},
{
ObjectMeta: metav1.ObjectMeta{
Name: "pod4",
Namespace: "foo",
OwnerReferences: []metav1.OwnerReference{
{
UID: "test-uid",
Controller: ptr.To(true),
},
},
},
Status: corev1.PodStatus{Phase: corev1.PodFailed},
},
},
expects: []string{
"Name: bar",
"Namespace: foo",
"Replicas: 4 current / 4 desired",
"Pods Status: 1 Running / 1 Waiting / 1 Succeeded / 1 Failed",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
objects := []runtime.Object{testCase.replicaSet}
for _, pod := range testCase.pods {
objects = append(objects, &pod)
}
fake := fake.NewClientset(objects...)
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := ReplicaSetDescriber{c}
out, err := d.Describe(testCase.replicaSet.Namespace, testCase.replicaSet.Name, DescriberSettings{ShowEvents: true})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestDescribeRole(t *testing.T) {
testCases := []struct {
name string
role *rbacv1.Role
expects []string
}{
{
name: "role with resource rules",
role: &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"pods"},
Verbs: []string{"get", "watch", "list"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
"pods [] [] [get watch list]",
},
},
{
name: "role with resource names",
role: &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"configmaps"},
ResourceNames: []string{"my-configmap"},
Verbs: []string{"update", "get"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
"configmaps [] [my-configmap] [update get]",
},
},
{
name: "non-resource urls",
role: &rbacv1.Role{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
},
Rules: []rbacv1.PolicyRule{
{
NonResourceURLs: []string{"/healthz", "/version"},
Verbs: []string{"get"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
" [/healthz] [] [get]",
" [/version] [] [get]",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.role)
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := RoleDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestDescribeRoleBinding(t *testing.T) {
testCases := []struct {
name string
binding *rbacv1.RoleBinding
expects []string
}{
{
name: "role binding with subjects",
binding: &rbacv1.RoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
Namespace: "foo",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.UserKind,
Name: "alice",
Namespace: "foo",
},
},
RoleRef: rbacv1.RoleRef{
Kind: "Role",
Name: "my-role",
},
},
expects: []string{
"Name: bar",
"Role:",
"Kind: Role",
"Name: my-role",
"Subjects:",
"Kind Name Namespace",
"---- ---- ---------",
"User alice foo",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.binding)
c := &describeClient{T: t, Namespace: "foo", Interface: fake}
d := RoleBindingDescriber{c}
out, err := d.Describe("foo", "bar", DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestDescribeClusterRole(t *testing.T) {
testCases := []struct {
name string
role *rbacv1.ClusterRole
expects []string
}{
{
name: "cluster role with resource rules",
role: &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{""},
Resources: []string{"secrets"},
Verbs: []string{"get", "watch", "list"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
"secrets [] [] [get watch list]",
},
},
{
name: "cluster role with resource names",
role: &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Rules: []rbacv1.PolicyRule{
{
APIGroups: []string{"rbac.authorization.k8s.io"},
Resources: []string{"clusterroles"},
ResourceNames: []string{"bind"},
Verbs: []string{"delete", "get"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
"clusterroles.rbac.authorization.k8s.io [] [bind] [delete get]",
},
},
{
name: "non-resource urls",
role: &rbacv1.ClusterRole{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Rules: []rbacv1.PolicyRule{
{
NonResourceURLs: []string{"/metrics", "/logs"},
Verbs: []string{"get"},
},
},
},
expects: []string{
"Name: bar",
"PolicyRule:",
"Resources Non-Resource URLs Resource Names Verbs",
"--------- ----------------- -------------- -----",
" [/logs] [] [get]",
" [/metrics] [] [get]",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.role)
c := &describeClient{T: t, Interface: fake}
d := ClusterRoleDescriber{c}
out, err := d.Describe("", "bar", DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestDescribeClusterRoleBinding(t *testing.T) {
testCases := []struct {
name string
binding *rbacv1.ClusterRoleBinding
expects []string
}{
{
name: "cluster role binding with cluster-scoped subject",
binding: &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.GroupKind,
Name: "dev-team",
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: "my-cluster-role",
},
},
expects: []string{
"Name: bar",
"Role:",
"Kind: ClusterRole",
"Name: my-cluster-role",
"Subjects:",
"Kind Name Namespace",
"---- ---- ---------",
"Group dev-team ",
},
},
{
name: "cluster role binding with namespace-scoped subject",
binding: &rbacv1.ClusterRoleBinding{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Subjects: []rbacv1.Subject{
{
Kind: rbacv1.ServiceAccountKind,
Name: "sa-name",
Namespace: "sa-namespace",
},
},
RoleRef: rbacv1.RoleRef{
Kind: "ClusterRole",
Name: "my-cluster-role",
},
},
expects: []string{
"Name: bar",
"Role:",
"Kind: ClusterRole",
"Name: my-cluster-role",
"Subjects:",
"Kind Name Namespace",
"---- ---- ---------",
"ServiceAccount sa-name sa-namespace",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.binding)
c := &describeClient{T: t, Interface: fake}
d := ClusterRoleBindingDescriber{c}
out, err := d.Describe("", "bar", DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}
func TestCertificateSigningRequestDescriber(t *testing.T) {
dummyCSRPEM := `-----BEGIN CERTIFICATE REQUEST-----
MIICVTCCAT0CAQAwEDEOMAwGA1UEAwwFYWxpY2UwggEiMA0GCSqGSIb3DQEBAQUA
A4IBDwAwggEKAoIBAQDPqga01w90b6SZEVvM11IONKSW2tOMtDE+ustAH9b8B8wb
iExQKse0zzGB4N9lKCWBkH8BpcNpJ6RzNNcs7SldejEy7xCCEFJw4/KPy8PrbZly
TcPI3i5PZ8UJPimUVHtZIfNePSo8A4/j5l+AnHO/K2sBi6A2QqOYizga+VH7sfr9
c80azgPiNYFMm7eKOTFJZ/HCfcPQHWnyrk9vsL/cSL7WgsjtKulR8SIYwHAsJOh2
REtJ6AX0FErmft1xiecFhrHFuY9op+XsiB8hku8kcGTJQ2TKl/lu9eBHWleZ/Y9R
vta3YxaXB07oS8FhmQeUcutNkCBxX3JNBO/9d2CBAgMBAAGgADANBgkqhkiG9w0B
AQsFAAOCAQEAv4R6Hh++vq3hwLfhYORe6WplwElE6AvtPB0v7OVwxbAE5FEaFerB
VIoiW44gRWsN/+be6D4IV+ZxIDI+JlHL8smc7nFVGo+uDj4QMeMHD29Bne+er1Nm
wULDTL6nWWGUM+fXVeULEhYDhZQ0am1BqMU+foc3wRm4/coPIkZfhG8o2OPmZN+D
aj7eYCTT+56driz9oVNawWF1ObeR4GOV6ba0aTqNm2JgT4fj6ePTD22b3cMZs81d
wzxacvSgqBb0HR10MjB5l2Vys7GOhWXnDadQaO09XCua1++3GWPOAAdoOtLiWuj4
IY5vBsBP6cycvKRL1XZ43uF9e2zE2GiHBw==
-----END CERTIFICATE REQUEST-----`
testCases := []struct {
name string
csr *certificatesv1.CertificateSigningRequest
expects []string
}{
{
name: "basic certificate signing request",
csr: &certificatesv1.CertificateSigningRequest{
ObjectMeta: metav1.ObjectMeta{
Name: "bar",
},
Spec: certificatesv1.CertificateSigningRequestSpec{
SignerName: "kubernetes.io/kube-apiserver-client",
Request: []byte(dummyCSRPEM),
Usages: []certificatesv1.KeyUsage{certificatesv1.UsageClientAuth, certificatesv1.UsageDigitalSignature},
Username: "alice",
Groups: []string{"system:authenticated"},
},
Status: certificatesv1.CertificateSigningRequestStatus{
Conditions: []certificatesv1.CertificateSigningRequestCondition{
{
Type: certificatesv1.CertificateApproved,
Reason: "Approved by admin",
Message: "This CSR was approved by the admin.",
},
},
},
},
expects: []string{
"Name: bar",
"Signer: kubernetes.io/kube-apiserver-client",
"Requesting User: alice",
"Status: Approved",
"Common Name: alice",
},
},
}
for _, testCase := range testCases {
t.Run(testCase.name, func(t *testing.T) {
fake := fake.NewClientset(testCase.csr)
c := &describeClient{T: t, Interface: fake}
d := CertificateSigningRequestDescriber{c}
out, err := d.Describe("", "bar", DescriberSettings{})
if err != nil {
t.Errorf("unexpected error: %v", err)
}
for _, expect := range testCase.expects {
if !strings.Contains(out, expect) {
t.Errorf("expected to find %q in output: %q", expect, out)
}
}
})
}
}