Change:
Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, }, } <del> oldParallelism := int32(10) newParallelism := int32(11) oldJob := &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "10", }, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: &oldParallelism, }, Status: batch.JobStatus{ Active: 11, }, } newJob := &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "9", }, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: &newParallelism, }, Status: batch.JobStatus{ Active: 12, }, } </del> <ins> cases := map[string]struct { oldJob            *batch.Job job               *batch.Job wantWarningsCount int32 }{ "generation 0 for both": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      0, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, </ins> <del> StatusStrategy.PrepareForUpdate(ctx, newJob, oldJob) if newJob.Status.Active != 12 { t.Errorf("Job status updates must allow changes to job status") } if *newJob.Spec.Parallelism != 10 { t.Errorf("Job status updates must now allow changes to job spec") </del> <ins> oldJob: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      0, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, }, "generation 1 for new; force WarningsOnUpdate to check PodTemplate for updates": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      1, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, oldJob: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      0, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, }, "force validation failure in pod template": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      1, }, Spec: batch.JobSpec{ Selector: validSelector, Template: api.PodTemplateSpec{ Spec: api.PodSpec{Volumes: []api.Volume{{Name: "volume-name"}, {Name: "volume-name"}}}, }, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, oldJob: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Generation:      0, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, wantWarningsCount: 1, }, } for val, tc := range cases { t.Run(val, func(t *testing.T) { gotWarnings := Strategy.WarningsOnUpdate(ctx, tc.job, tc.oldJob) if len(gotWarnings) != int(tc.wantWarningsCount) { t.Errorf("got warning length of %d but expected %d", len(gotWarnings), tc.wantWarningsCount) } }) } } func TestJobStrategy_WarningsOnCreate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() theUID := types.UID("1a2b3c4d5e6f7g8h9i0k") validSelector := &metav1.LabelSelector{ MatchLabels: map[string]string{"a": "b"}, } validSpec := batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, }, }, } testcases := map[string]struct { job               *batch.Job wantWarningsCount int32 }{ "happy path job": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:      "myjob2", Namespace: metav1.NamespaceDefault, UID:       theUID, }, Spec: validSpec, }, }, "dns invalid name": { wantWarningsCount: 1, job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:      "my job2", Namespace: metav1.NamespaceDefault, UID:       theUID, }, Spec: validSpec, }, }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { gotWarnings := Strategy.WarningsOnCreate(ctx, tc.job) if len(gotWarnings) != int(tc.wantWarningsCount) { t.Errorf("got warning length of %d but expected %d", len(gotWarnings), tc.wantWarningsCount) } }) } } func TestJobStrategy_Validate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() theUID := types.UID("1a2b3c4d5e6f7g8h9i0k") validSelector := &metav1.LabelSelector{ MatchLabels: map[string]string{"a": "b"}, } validPodSpec := api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, } validObjectMeta := metav1.ObjectMeta{ Name:      "myjob2", Namespace: metav1.NamespaceDefault, UID:       theUID, } testcases := map[string]struct { job              *batch.Job wantJob          *batch.Job wantWarningCount int32 }{ "valid job with labels in pod template": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: validPodSpec, }}, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}}, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: validPodSpec, }}, }, }, "no labels in job": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ Spec: validPodSpec, }}, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}}, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"job-name": "myjob2", "controller-uid": string(theUID)}, }, Spec: validPodSpec, }}, }, }, "labels exist": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)}, }, Spec: validPodSpec, }}, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}}, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)}, }, Spec: validPodSpec, }}, }, }, "manual selector; do not generate labels": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: validSelector, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: validPodSpec, }, Completions:    pointer.Int32Ptr(2), ManualSelector: pointer.BoolPtr(true), }, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: validSelector, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: validPodSpec, }, Completions:    pointer.Int32Ptr(2), ManualSelector: pointer.BoolPtr(true), }, }, }, "valid job with extended configuration": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: validPodSpec, }, Completions:             pointer.Int32Ptr(2), Suspend:                 pointer.BoolPtr(true), TTLSecondsAfterFinished: pointer.Int32Ptr(0), CompletionMode:          completionModePtr(batch.IndexedCompletion), }, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}}, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)}, }, Spec: validPodSpec, }, Completions:             pointer.Int32Ptr(2), Suspend:                 pointer.BoolPtr(true), TTLSecondsAfterFinished: pointer.Int32Ptr(0), CompletionMode:          completionModePtr(batch.IndexedCompletion), }, }, }, "fail validation due to invalid volume spec": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: nil, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, Volumes:       []api.Volume{{Name: "volume-name"}}, }, }, }, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector: &metav1.LabelSelector{MatchLabels: map[string]string{"controller-uid": string(theUID)}}, Template: api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: map[string]string{"a": "b", "job-name": "myjob2", "controller-uid": string(theUID)}, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, Volumes:       []api.Volume{{Name: "volume-name"}}, }, }, }, }, wantWarningCount: 1, }, } for name, tc := range testcases { t.Run(name, func(t *testing.T) { errs := Strategy.Validate(ctx, tc.job) if len(errs) != int(tc.wantWarningCount) { t.Errorf("want warnings %d but got %d", tc.wantWarningCount, len(errs)) } if diff := cmp.Diff(tc.wantJob, tc.job); diff != "" { t.Errorf("Unexpected job (-want,+got):n%s", diff) } }) } } func TestStrategy_ResetFields(t *testing.T) { resetFields := Strategy.GetResetFields() if len(resetFields) != 1 { t.Error("ResetFields should have 1 element") } } func TestJobStatusStrategy_ResetFields(t *testing.T) { resetFields := StatusStrategy.GetResetFields() if len(resetFields) != 1 { t.Error("ResetFields should have 1 element") } } func TestStatusStrategy_PrepareForUpdate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() validSelector := &metav1.LabelSelector{ MatchLabels: map[string]string{"a": "b"}, } validPodTemplateSpec := api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, }, } validObjectMeta := metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "10", } cases := map[string]struct { job     *batch.Job newJob  *batch.Job wantJob *batch.Job }{ "job must allow status updates": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, Status: batch.JobStatus{ Active: 11, }, }, newJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, Status: batch.JobStatus{ Active: 12, }, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, Status: batch.JobStatus{ Active: 12, }, }, }, "parallelism changes not allowed": { job: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(3), }, }, newJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, }, wantJob: &batch.Job{ ObjectMeta: validObjectMeta, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(3), }, }, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { StatusStrategy.PrepareForUpdate(ctx, tc.newJob, tc.job) if diff := cmp.Diff(tc.wantJob, tc.newJob); diff != "" { t.Errorf("Unexpected job (-want,+got):n%s", diff) } }) } } func TestStatusStrategy_ValidateUpdate(t *testing.T) { ctx := genericapirequest.NewDefaultContext() validSelector := &metav1.LabelSelector{ MatchLabels: map[string]string{"a": "b"}, } validPodTemplateSpec := api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, }, } cases := map[string]struct { job     *batch.Job newJob  *batch.Job wantJob *batch.Job }{ "incoming resource version on update should not be mutated": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "10", }, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, }, newJob: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "9", }, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, }, wantJob: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "9", }, Spec: batch.JobSpec{ Selector:    validSelector, Template:    validPodTemplateSpec, Parallelism: pointer.Int32(4), }, }, }, } for name, tc := range cases { t.Run(name, func(t *testing.T) { errs := StatusStrategy.ValidateUpdate(ctx, tc.newJob, tc.job) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) } if diff := cmp.Diff(tc.wantJob, tc.newJob); diff != "" { t.Errorf("Unexpected job (-want,+got):n%s", diff) } }) } } func TestJobStrategy_GetAttrs(t *testing.T) { validSelector := &metav1.LabelSelector{ MatchLabels: map[string]string{"a": "b"}, } validPodTemplateSpec := api.PodTemplateSpec{ ObjectMeta: metav1.ObjectMeta{ Labels: validSelector.MatchLabels, }, Spec: api.PodSpec{ RestartPolicy: api.RestartPolicyOnFailure, DNSPolicy:     api.DNSClusterFirst, Containers:    []api.Container{{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent", TerminationMessagePolicy: api.TerminationMessageReadFile}}, }, </ins> } <del> errs := StatusStrategy.ValidateUpdate(ctx, newJob, oldJob) if len(errs) != 0 { t.Errorf("Unexpected error %v", errs) </del> <ins> cases := map[string]struct { job          *batch.Job wantErr      string nonJobObject *api.Pod }{ "valid job with no labels": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, }, "valid job with a label": { job: &batch.Job{ ObjectMeta: metav1.ObjectMeta{ Name:            "myjob", Namespace:       metav1.NamespaceDefault, ResourceVersion: "0", Labels:          map[string]string{"a": "b"}, }, Spec: batch.JobSpec{ Selector:       validSelector, Template:       validPodTemplateSpec, ManualSelector: pointer.BoolPtr(true), Parallelism:    pointer.Int32Ptr(1), }, }, }, "pod instead": { job:          nil, nonJobObject: &api.Pod{}, wantErr:      "given object is not a job.", }, </ins> } <del> if newJob.ResourceVersion != "9" { t.Errorf("Incoming resource version on update should not be mutated") </del> <ins> for name, tc := range cases { t.Run(name, func(t *testing.T) { if tc.job == nil { _, _, err := GetAttrs(tc.nonJobObject) if diff := cmp.Diff(tc.wantErr, err.Error()); diff != "" { t.Errorf("Unexpected errors (-want,+got):n%s", diff) } } else { gotLabels, _, err := GetAttrs(tc.job) if err != nil { t.Errorf("Error %s supposed to be nil", err.Error()) } if diff := cmp.Diff(labels.Set(tc.job.ObjectMeta.Labels), gotLabels); diff != "" { t.Errorf("Unexpected attrs (-want,+got):n%s", diff) } } }) </ins> } } <del> func TestSelectableFieldLabelConversions(t *testing.T) { </del> <ins> func TestJobToSelectiableFields(t *testing.T) { </ins> apitesting.TestSelectableFieldLabelConversionsOfKind(t, "batch/v1", "Job",

Discussion:
The gets very long testing mulitple functions in such as , and . These checks should be performed in dedicated Test functions using table for different scenarios. Target functions and are being as part of The remaining checks should be migrated to these functions. Also, a test function should be to migrate the remaining tests.
This issue is currently awaiting triage. If a SIG or subproject determines this is a relevant issue, they will accept it by applying the label and provide further guidance. The label can be by org members by writing in a comment. <detailsInstructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
/sig apps
/assign
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
/remove-lifecycle stale
Any progress on this? I'm working on and I noticed that has relatively low coverage so I was going to work on getting the coverage up.
I created that is somewhat related to this.
no progess. go ahead please
/assign