/*
Copyright 2021 The Kubernetes 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 fieldmanager

import (
	"reflect"
	"testing"
	"time"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/runtime/schema"
	"sigs.k8s.io/structured-merge-diff/v4/fieldpath"
)

func TestTransformManagedFieldsToSubresource(t *testing.T) {
	testTime, _ := time.ParseInLocation("2006-Jan-02", "2013-Feb-03", time.Local)
	managedFieldTime := v1.NewTime(testTime)

	tests := []struct {
		desc     string
		input    []metav1.ManagedFieldsEntry
		expected []metav1.ManagedFieldsEntry
	}{
		{
			desc: "filter one entry and transform it into a subresource entry",
			input: []metav1.ManagedFieldsEntry{
				{
					Manager:    "manager-1",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:another-field":{}}}`)},
				},
				{
					Manager:    "manager-2",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Time:       &managedFieldTime,
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "manager-2",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "autoscaling/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Time:       &managedFieldTime,
				},
			},
		},
		{
			desc: "transform all entries",
			input: []metav1.ManagedFieldsEntry{
				{
					Manager:    "manager-1",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:    "manager-2",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:     "manager-3",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "manager-1",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "autoscaling/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:    "manager-2",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "autoscaling/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:     "manager-3",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "drops fields if the api version is unknown",
			input: []metav1.ManagedFieldsEntry{
				{
					Manager:    "manager-1",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v10",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
			},
			expected: nil,
		},
	}

	for _, test := range tests {
		handler := NewScaleHandler(
			test.input,
			schema.GroupVersion{Group: "apps", Version: "v1"},
			defaultMappings(),
		)
		subresourceEntries, err := handler.ToSubresource()
		if err != nil {
			t.Fatalf("test %q - expected no error but got %v", test.desc, err)
		}

		if !reflect.DeepEqual(subresourceEntries, test.expected) {
			t.Fatalf("test %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, subresourceEntries)
		}
	}
}

func TestTransformingManagedFieldsToParent(t *testing.T) {
	tests := []struct {
		desc        string
		parent      []metav1.ManagedFieldsEntry
		subresource []metav1.ManagedFieldsEntry
		expected    []metav1.ManagedFieldsEntry
	}{
		{
			desc: "different-managers: apply -> update",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "different-managers: apply -> apply",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
		},
		{
			desc: "different-managers: update -> update",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
		},
		{
			desc: "different-managers: update -> apply",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
		},
		{
			desc: "same manager: apply -> apply",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "same manager: update -> update",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "same manager: update -> apply",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
		},
		{
			desc: "same manager: apply -> update",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "subresource doesn't own the path anymore",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:status":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
		},
		{
			desc: "Subresource steals all the fields of the parent resource",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "apply without stealing",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "autoscaling/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{},"f:selector":{}}}`)},
				},
				{
					Manager:     "test",
					Operation:   metav1.ManagedFieldsOperationApply,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc: "drops the entry if the api version is unknown",
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
				{
					Manager:    "another-manager",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v10",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:    "scale",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "autoscaling/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "scale",
					Operation:  metav1.ManagedFieldsOperationUpdate,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
				},
			},
		},
	}

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			handler := NewScaleHandler(
				test.parent,
				schema.GroupVersion{Group: "apps", Version: "v1"},
				defaultMappings(),
			)
			parentEntries, err := handler.ToParent(test.subresource)
			if err != nil {
				t.Fatalf("test: %q - expected no error but got %v", test.desc, err)
			}
			if !reflect.DeepEqual(parentEntries, test.expected) {
				t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries)
			}
		})
	}
}

func TestTransformingManagedFieldsToParentMultiVersion(t *testing.T) {
	tests := []struct {
		desc         string
		groupVersion schema.GroupVersion
		mappings     ResourcePathMappings
		parent       []metav1.ManagedFieldsEntry
		subresource  []metav1.ManagedFieldsEntry
		expected     []metav1.ManagedFieldsEntry
	}{
		{
			desc:         "multi-version",
			groupVersion: schema.GroupVersion{Group: "apps", Version: "v1"},
			mappings: ResourcePathMappings{
				"apps/v1": fieldpath.MakePathOrDie("spec", "the-replicas"),
				"apps/v2": fieldpath.MakePathOrDie("spec", "not-the-replicas"),
			},
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{},"f:selector":{}}}`)},
				},
				{
					Manager:    "test-other",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v2",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:not-the-replicas":{},"f:selector":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:    "test-other",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "apps/v2",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "apps/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
		{
			desc:         "Custom resource without scale subresource, scaling a version with `scale`",
			groupVersion: schema.GroupVersion{Group: "mygroup", Version: "v1"},
			mappings: ResourcePathMappings{
				"mygroup/v1": fieldpath.MakePathOrDie("spec", "the-replicas"),
				"mygroup/v2": nil,
			},
			parent: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "mygroup/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{},"f:selector":{}}}`)},
				},
				{
					Manager:    "test-other",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "mygroup/v2",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:test-other":{}}}`)},
				},
			},
			subresource: []metav1.ManagedFieldsEntry{
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "autoscaling/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:replicas":{}}}`)},
					Subresource: "scale",
				},
			},
			expected: []metav1.ManagedFieldsEntry{
				{
					Manager:    "test",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "mygroup/v1",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:selector":{}}}`)},
				},
				{
					Manager:    "test-other",
					Operation:  metav1.ManagedFieldsOperationApply,
					APIVersion: "mygroup/v2",
					FieldsType: "FieldsV1",
					FieldsV1:   &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:test-other":{}}}`)},
				},
				{
					Manager:     "scale",
					Operation:   metav1.ManagedFieldsOperationUpdate,
					APIVersion:  "mygroup/v1",
					FieldsType:  "FieldsV1",
					FieldsV1:    &metav1.FieldsV1{Raw: []byte(`{"f:spec":{"f:the-replicas":{}}}`)},
					Subresource: "scale",
				},
			},
		},
	}

	for _, test := range tests {
		t.Run(test.desc, func(t *testing.T) {
			handler := NewScaleHandler(
				test.parent,
				test.groupVersion,
				test.mappings,
			)
			parentEntries, err := handler.ToParent(test.subresource)
			if err != nil {
				t.Fatalf("test: %q - expected no error but got %v", test.desc, err)
			}
			if !reflect.DeepEqual(parentEntries, test.expected) {
				t.Fatalf("test: %q - expected output to be:\n%v\n\nbut got:\n%v", test.desc, test.expected, parentEntries)
			}
		})
	}
}

func defaultMappings() ResourcePathMappings {
	return ResourcePathMappings{
		"apps/v1": fieldpath.MakePathOrDie("spec", "replicas"),
	}
}
