package resource

import (
	"testing"

	"github.com/pulumi/pulumi/sdk/v3/go/common/util/deepcopy"
	"github.com/stretchr/testify/assert"
)

func TestPropertyPath(t *testing.T) {
	t.Parallel()

	makeValue := func() PropertyValue {
		return NewProperty(NewPropertyMapFromMap(map[string]interface{}{
			"root": map[string]interface{}{
				"nested": map[string]interface{}{
					"array": []interface{}{
						map[string]interface{}{
							"double": []interface{}{
								nil,
								true,
							},
						},
					},
				},
				"double": map[string]interface{}{
					"nest": true,
				},
				"array": []interface{}{
					map[string]interface{}{
						"nested": true,
					},
					true,
				},
				"array2": []interface{}{
					[]interface{}{
						nil,
						map[string]interface{}{
							"nested": true,
						},
					},
				},
				`key with "escaped" quotes`: true,
				"key with a .":              true,
			},
			`root key with "escaped" quotes`: map[string]interface{}{
				"nested": true,
			},
			"root key with a .": []interface{}{
				nil,
				true,
			},
		}))
	}

	cases := []struct {
		path     string
		parsed   PropertyPath
		expected string
	}{
		{
			"root",
			PropertyPath{"root"},
			"root",
		},
		{
			"root.nested",
			PropertyPath{"root", "nested"},
			"root.nested",
		},
		{
			`root["nested"]`,
			PropertyPath{"root", "nested"},
			`root.nested`,
		},
		{
			"root.double.nest",
			PropertyPath{"root", "double", "nest"},
			"root.double.nest",
		},
		{
			`root["double"].nest`,
			PropertyPath{"root", "double", "nest"},
			`root.double.nest`,
		},
		{
			`root["double"]["nest"]`,
			PropertyPath{"root", "double", "nest"},
			`root.double.nest`,
		},
		{
			"root.array[0]",
			PropertyPath{"root", "array", 0},
			"root.array[0]",
		},
		{
			"root.array[1]",
			PropertyPath{"root", "array", 1},
			"root.array[1]",
		},
		{
			"root.array[0].nested",
			PropertyPath{"root", "array", 0, "nested"},
			"root.array[0].nested",
		},
		{
			"root.array2[0][1].nested",
			PropertyPath{"root", "array2", 0, 1, "nested"},
			"root.array2[0][1].nested",
		},
		{
			"root.nested.array[0].double[1]",
			PropertyPath{"root", "nested", "array", 0, "double", 1},
			"root.nested.array[0].double[1]",
		},
		{
			`root["key with \"escaped\" quotes"]`,
			PropertyPath{"root", `key with "escaped" quotes`},
			`root["key with \"escaped\" quotes"]`,
		},
		{
			`root["key with a ."]`,
			PropertyPath{"root", "key with a ."},
			`root["key with a ."]`,
		},
		{
			`["root key with \"escaped\" quotes"].nested`,
			PropertyPath{`root key with "escaped" quotes`, "nested"},
			`["root key with \"escaped\" quotes"].nested`,
		},
		{
			`["root key with a ."][1]`,
			PropertyPath{"root key with a .", 1},
			`["root key with a ."][1]`,
		},
		// The following two cases are regressions for https://github.com/pulumi/pulumi/issues/14439. Ideally
		// these would be a syntax error, but it seems providers have been emitting paths of this style and so
		// we need to keep supporting them.
		{
			`root.array.[1]`,
			PropertyPath{"root", "array", 1},
			`root.array[1]`,
		},
		{
			`root.["key with a ."]`,
			PropertyPath{"root", "key with a ."},
			`root["key with a ."]`,
		},
	}

	for _, c := range cases {
		c := c
		t.Run(c.path, func(t *testing.T) {
			t.Parallel()

			parsed, err := ParsePropertyPath(c.path)
			assert.NoError(t, err)
			assert.Equal(t, c.parsed, parsed)
			assert.Equal(t, c.expected, parsed.String())

			value := makeValue()

			v, ok := parsed.Get(value)
			assert.True(t, ok)
			assert.False(t, v.IsNull())

			ok = parsed.Delete(value)
			assert.True(t, ok)

			ok = parsed.Set(value, v)
			assert.True(t, ok)

			u, ok := parsed.Get(value)
			assert.True(t, ok)
			assert.Equal(t, v, u)

			vv := PropertyValue{}
			vv, ok = parsed.Add(vv, v)
			assert.True(t, ok)

			u, ok = parsed.Get(vv)
			assert.True(t, ok)
			assert.Equal(t, v, u)
		})
	}

	simpleCases := []struct {
		path     string
		expected PropertyPath
	}{
		{
			`root["*"].field[1]`,
			PropertyPath{"root", "*", "field", 1},
		},
		{
			`root[*].field[2]`,
			PropertyPath{"root", "*", "field", 2},
		},
		{
			`root[3].*[3]`,
			PropertyPath{"root", 3, "*", 3},
		},
		{
			`*.bar`,
			PropertyPath{"*", "bar"},
		},
	}

	t.Run("Simple", func(t *testing.T) {
		t.Parallel()

		for _, c := range simpleCases {
			c := c
			t.Run(c.path, func(t *testing.T) {
				t.Parallel()

				parsed, err := ParsePropertyPath(c.path)
				assert.NoError(t, err)
				assert.Equal(t, c.expected, parsed)
			})
		}
	})

	negativeCases := []string{
		// Syntax errors
		"root[",
		`root["nested]`,
		`root."double".nest`,
		"root.array[abc]",
		"root.",

		// Missing values
		"root[1]",
		"root.nested.array[100]",
		"root.nested.array.bar",
		"foo",
	}

	//nolint:paralleltest // false positive because range var isn't used directly in t.Run(name) arg
	for _, c := range negativeCases {
		c := c
		t.Run(c, func(t *testing.T) {
			t.Parallel()

			parsed, err := ParsePropertyPath(c)
			if err == nil {
				value := makeValue()

				v, ok := parsed.Get(value)
				assert.False(t, ok)
				assert.True(t, v.IsNull())
			}
		})
	}

	negativeCasesStrict := []string{
		// Syntax erros
		`root.array.[1]`,
		`root.["key with a ."]`,
	}
	//nolint:paralleltest // false positive because range var isn't used directly in t.Run(name) arg
	for _, c := range negativeCasesStrict {
		c := c
		t.Run(c, func(t *testing.T) {
			t.Parallel()

			_, err := ParsePropertyPathStrict(c)
			assert.NotNil(t, err)
		})
	}
}

func TestPropertyPathContains(t *testing.T) {
	t.Parallel()

	cases := []struct {
		p1       PropertyPath
		p2       PropertyPath
		expected bool
	}{
		{
			PropertyPath{"root", "nested"},
			PropertyPath{"root"},
			false,
		},
		{
			PropertyPath{"root"},
			PropertyPath{"root", "nested"},
			true,
		},
		{
			PropertyPath{"root", 1},
			PropertyPath{"root"},
			false,
		},
		{
			PropertyPath{"root"},
			PropertyPath{"root", 1},
			true,
		},
		{
			PropertyPath{"root", "double", "nest1"},
			PropertyPath{"root", "double", "nest2"},
			false,
		},
		{
			PropertyPath{"root", "nest1", "double"},
			PropertyPath{"root", "nest2", "double"},
			false,
		},
		{
			PropertyPath{"root", "nest", "double"},
			PropertyPath{"root", "nest", "double"},
			true,
		},
		{
			PropertyPath{"root", 1, "double"},
			PropertyPath{"root", 1, "double"},
			true,
		},
		{
			PropertyPath{},
			PropertyPath{},
			true,
		},
		{
			PropertyPath{"root"},
			PropertyPath{},
			false,
		},
		{
			PropertyPath{},
			PropertyPath{"root"},
			true,
		},
		{
			PropertyPath{"foo", "bar", 1},
			PropertyPath{"foo", "bar", 1, "baz"},
			true,
		},
		{
			PropertyPath{"foo", "*", "baz"},
			PropertyPath{"foo", "bar", "baz", "bam"},
			true,
		},
		{
			PropertyPath{"*", "bar", "baz"},
			PropertyPath{"foo", "bar", "baz", "bam"},
			true,
		},
		{
			PropertyPath{"foo", "*", "baz"},
			PropertyPath{"foo", 1, "baz", "bam"},
			true,
		},
		{
			PropertyPath{"foo", 1, "*", "bam"},
			PropertyPath{"foo", 1, "baz", "bam"},
			true,
		},
		{
			PropertyPath{"*"},
			PropertyPath{"a", "b"},
			true,
		},
		{
			PropertyPath{"*"},
			PropertyPath{"a", 1},
			true,
		},
		{
			PropertyPath{"*"},
			PropertyPath{"a", 1, "b"},
			true,
		},
	}

	for _, tcase := range cases {
		res := tcase.p1.Contains(tcase.p2)
		assert.Equal(t, tcase.expected, res)
	}
}

func TestAddResizePropertyPath(t *testing.T) {
	t.Parallel()

	// Regression test for https://github.com/pulumi/pulumi/issues/5871:
	// Ensure that adding a new element beyond the size of an array will resize it.
	path, err := ParsePropertyPath("[1]")
	assert.NoError(t, err)
	_, ok := path.Add(NewProperty([]PropertyValue{}), NewProperty(42.0))
	assert.True(t, ok)
}

func TestReset(t *testing.T) {
	t.Parallel()

	cases := []struct {
		name     string
		path     PropertyPath
		old      PropertyMap
		new      PropertyMap
		expected *PropertyMap
	}{
		{
			"Missing key, not in object",
			PropertyPath{"missing"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(2.0)},
		},
		{
			"Missing key, not an object",
			PropertyPath{"root", "missing"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(2.0)},
		},
		{
			"Missing index, changed from an object",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(2.0)},
			nil,
		},
		{
			"Missing index, changed to an object",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{})},
		},
		{
			"Missing key along path, not in object",
			PropertyPath{"missing", "path"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(2.0)},
		},
		{
			"Missing key along path, not an object",
			PropertyPath{"root", "missing", "path"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(2.0)},
		},
		{
			"Missing index, not in array",
			PropertyPath{"array", 1},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			&PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
		},
		{
			"Missing index, not an array",
			PropertyPath{"root", 0},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(2.0)},
		},
		{
			"Missing index, changed from an array",
			PropertyPath{"root", 0},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(2.0)},
			nil,
		},
		{
			"Missing index, changed to an array",
			PropertyPath{"root", 0},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(2.0)})},
			nil,
		},
		{
			"Invalid index, not in array",
			PropertyPath{"array", -1},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			nil,
		},
		{
			"Invalid index, not an array",
			PropertyPath{"root", -1},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			nil,
		},
		{
			"Index out of bound in old",
			PropertyPath{"root", 1},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(2.0)})},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(3.0), NewProperty(4.0)})},
			nil,
		},
		{
			"Index out of bound in new",
			PropertyPath{"root", 1},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(1.0), NewProperty(2.0)})},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(3.0)})},
			nil,
		},
		{
			"Missing index along path",
			PropertyPath{"root", 0, "other"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
		},
		{
			"Index out of bound in old along path",
			PropertyPath{"root", 1, "nested"},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(1.0)}),
			})},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(3.0)}),
				NewProperty(PropertyMap{"nested": NewProperty(4.0)}),
			})},
			nil,
		},
		{
			"Index out of bound in new along path",
			PropertyPath{"root", 1, "nested"},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(1.0)}),
				NewProperty(PropertyMap{"nested": NewProperty(2.0)}),
			})},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(3.0)}),
			})},
			nil,
		},
		{
			"Single path element",
			PropertyPath{"root"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(1.0)},
		},
		{
			"Nested path element, changed",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
		},
		{
			"Nested path element, added",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{})},
		},
		{
			"Nested path element, removed",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
		},
		{
			"Nested path element, fully removed",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{},
			nil,
		},
		{
			"Nested path element, nested added",
			PropertyPath{"root", "nested"},
			PropertyMap{},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{})},
		},
		{
			"Nested path element, nested removed",
			PropertyPath{"root", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{},
			nil,
		},
		{
			"Array index",
			PropertyPath{"array", 0},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(2.0)})},
			&PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
		},
		{
			"Array index, along path",
			PropertyPath{"array", 0, "item"},
			PropertyMap{"array": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"item": NewProperty(1.0)}),
			})},
			PropertyMap{"array": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"item": NewProperty(2.0)}),
			})},
			&PropertyMap{"array": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"item": NewProperty(1.0)}),
			})},
		},
		{
			"Array index, added",
			PropertyPath{"array", 0},
			PropertyMap{"array": NewProperty([]PropertyValue{})},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(2.0)})},
			nil,
		},
		{
			"Array index, removed",
			PropertyPath{"array", 0},
			PropertyMap{"array": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"array": NewProperty([]PropertyValue{})},
			nil,
		},
		{
			"Single wildcard at root",
			PropertyPath{"*"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(2.0)},
			&PropertyMap{"root": NewProperty(1.0)},
		},
		{
			"Wildcard followed by path element",
			PropertyPath{"*", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
		},
		{
			"Wildcard in array followed by path element",
			PropertyPath{"root", "*", "nested"},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(1.0)}),
				NewProperty(PropertyMap{"nested": NewProperty(2.0)}),
			})},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(3.0)}),
				NewProperty(PropertyMap{"nested": NewProperty(4.0)}),
			})},
			&PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(PropertyMap{"nested": NewProperty(1.0)}),
				NewProperty(PropertyMap{"nested": NewProperty(2.0)}),
			})},
		},
		{
			"Nested wildcard",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
		},
		{
			"Nested wildcard in array",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(2.0),
			})},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(3.0),
				NewProperty(4.0),
			})},
			&PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(2.0),
			})},
		},
		{
			"Nested wildcard, added",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty(PropertyMap{})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{})},
		},
		{
			"Nested wildcard, removed",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
		},
		{
			"Nested wildcard, change of type (array)",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
		},
		{
			"Nested wildcard, change of type (object)",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(1.0)})},
			&PropertyMap{"root": NewProperty([]PropertyValue{NewProperty(1.0)})},
		},
		{
			"Nested wildcard, change of type (number)",
			PropertyPath{"root", "*"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			PropertyMap{"root": NewProperty(1.0)},
			nil,
		},
		{
			"Untraversable in old wildcard followed by path element",
			PropertyPath{"root", "*", "nested"},
			PropertyMap{"root": NewProperty(1.0)},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(2.0)})},
			nil,
		},
		{
			"Untraversable in new (not an object) wildcard followed by path element",
			PropertyPath{"root", "*", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(2.0)},
			nil,
		},
		{
			"Untraversable in new (missing) wildcard followed by path element",
			PropertyPath{"root", "*", "nested"},
			PropertyMap{"root": NewProperty(PropertyMap{"nested": NewProperty(1.0)})},
			PropertyMap{"root": NewProperty(PropertyMap{})},
			nil,
		},
		{
			"Nested object wildcard reset fails",
			PropertyPath{"root", "*", 0},
			PropertyMap{"root": NewProperty(PropertyMap{
				"passes": NewProperty(1.0),
				"fails":  NewProperty([]PropertyValue{NewProperty(1.0)}),
			})},
			PropertyMap{"root": NewProperty(PropertyMap{
				"passes": NewProperty(2.0),
				"fails":  NewProperty([]PropertyValue{}),
			})},
			nil,
		},
		{
			"Nested array wildcard reset fails",
			PropertyPath{"root", "*", 0},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty([]PropertyValue{NewProperty(1.0)}),
			})},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(2.0),
				NewProperty([]PropertyValue{}),
			})},
			nil,
		},
		{
			"Nested path, secret old",
			PropertyPath{"root", "secret"},
			PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(1.0)}))},
			PropertyMap{"root": NewProperty(PropertyMap{"secret": NewProperty(2.0)})},
			&PropertyMap{"root": NewProperty(PropertyMap{"secret": MakeSecret(NewProperty(1.0))})},
		},
		{
			"Nested path, secret new",
			PropertyPath{"root", "secret"},
			PropertyMap{"root": NewProperty(PropertyMap{"secret": NewProperty(1.0)})},
			PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(2.0)}))},
			&PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(1.0)}))},
		},
		{
			"Nested path, secret both",
			PropertyPath{"root", "secret"},
			PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(1.0)}))},
			PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(2.0)}))},
			&PropertyMap{"root": MakeSecret(NewProperty(PropertyMap{"secret": NewProperty(1.0)}))},
		},
		{
			"Nested array, secret old",
			PropertyPath{"root", 0},
			PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(2.0),
			}))},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(3.0),
				NewProperty(4.0),
			})},
			&PropertyMap{"root": NewProperty([]PropertyValue{
				MakeSecret(NewProperty(1.0)),
				NewProperty(4.0),
			})},
		},
		{
			"Nested array, secret new",
			PropertyPath{"root", 0},
			PropertyMap{"root": NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(2.0),
			})},
			PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(3.0),
				NewProperty(4.0),
			}))},
			&PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(4.0),
			}))},
		},
		{
			"Nested array, secret both",
			PropertyPath{"root", 0},
			PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(2.0),
			}))},
			PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(3.0),
				NewProperty(4.0),
			}))},
			&PropertyMap{"root": MakeSecret(NewProperty([]PropertyValue{
				NewProperty(1.0),
				NewProperty(4.0),
			}))},
		},
	}

	for _, tt := range cases {
		tt := tt
		t.Run(tt.name, func(t *testing.T) {
			t.Parallel()

			newCopy := deepcopy.Copy(tt.new).(PropertyMap)

			res := tt.path.Reset(tt.old, tt.new)
			if tt.expected == nil {
				assert.False(t, res)
				assert.Equal(t, newCopy, tt.new)
			} else {
				assert.True(t, res)
				assert.Equal(t, *tt.expected, tt.new)
			}
		})
	}
}