Full Mattermost server source with integrated Community Enterprise features. Includes vendor directory for offline/air-gapped builds. Structure: - enterprise-impl/: Enterprise feature implementations - enterprise-community/: Init files that register implementations - enterprise/: Bridge imports (community_imports.go) - vendor/: All dependencies for offline builds Build (online): go build ./cmd/mattermost Build (offline/air-gapped): go build -mod=vendor ./cmd/mattermost 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1661 lines
64 KiB
Go
1661 lines
64 KiB
Go
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
|
|
// See LICENSE.txt for license information.
|
|
|
|
package utils_test
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/mattermost/mattermost/server/public/model"
|
|
"github.com/mattermost/mattermost/server/v8/channels/utils"
|
|
)
|
|
|
|
// Test merging maps alone. This isolates the complexity of merging maps from merging maps recursively in
|
|
// a struct/ptr/etc.
|
|
// Remember that for our purposes, "merging" means replacing base with patch if patch is /anything/ other than nil.
|
|
func TestMergeWithMaps(t *testing.T) {
|
|
t.Run("merge maps where patch is longer", func(t *testing.T) {
|
|
m1 := map[string]int{"this": 1, "is": 2, "a map": 3}
|
|
m2 := map[string]int{"this": 1, "is": 3, "a second map": 3, "another key": 4}
|
|
|
|
expected := map[string]int{"this": 1, "is": 3, "a second map": 3, "another key": 4}
|
|
merged, err := mergeStringIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge maps where base is longer", func(t *testing.T) {
|
|
m1 := map[string]int{"this": 1, "is": 2, "a map": 3, "with": 4, "more keys": -12}
|
|
m2 := map[string]int{"this": 1, "is": 3, "a second map": 3}
|
|
expected := map[string]int{"this": 1, "is": 3, "a second map": 3}
|
|
|
|
merged, err := mergeStringIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge maps where base is empty", func(t *testing.T) {
|
|
m1 := make(map[string]int)
|
|
m2 := map[string]int{"this": 1, "is": 3, "a second map": 3, "another key": 4}
|
|
|
|
expected := map[string]int{"this": 1, "is": 3, "a second map": 3, "another key": 4}
|
|
merged, err := mergeStringIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge maps where patch is empty", func(t *testing.T) {
|
|
m1 := map[string]int{"this": 1, "is": 3, "a map": 3, "another key": 4}
|
|
var m2 map[string]int
|
|
expected := map[string]int{"this": 1, "is": 3, "a map": 3, "another key": 4}
|
|
|
|
merged, err := mergeStringIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]*int patch with different keys and values", func(t *testing.T) {
|
|
m1 := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(3)}
|
|
m2 := map[string]*int{"this": newPointer(2), "is": newPointer(3), "a key": newPointer(4)}
|
|
expected := map[string]*int{"this": newPointer(2), "is": newPointer(3), "a key": newPointer(4)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]*int patch has nil keys -- doesn't matter, maps overwrite completely", func(t *testing.T) {
|
|
m1 := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(3)}
|
|
m2 := map[string]*int{"this": newPointer(1), "is": nil, "a key": newPointer(3)}
|
|
expected := map[string]*int{"this": newPointer(1), "is": nil, "a key": newPointer(3)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]*int base has nil vals -- overwrite base with patch", func(t *testing.T) {
|
|
m1 := map[string]*int{"this": newPointer(1), "is": nil, "base key": newPointer(4)}
|
|
m2 := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(3)}
|
|
expected := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(3)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]*int base has nil vals -- but patch is nil, so keep base", func(t *testing.T) {
|
|
m1 := map[string]*int{"this": newPointer(1), "is": nil, "base key": newPointer(4)}
|
|
var m2 map[string]*int
|
|
expected := map[string]*int{"this": newPointer(1), "is": nil, "base key": newPointer(4)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]*int pointers are not copied - change in base do not affect merged", func(t *testing.T) {
|
|
// that should never happen, since patch overwrites completely
|
|
m1 := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(4)}
|
|
m2 := map[string]*int{"this": newPointer(1), "a key": newPointer(5)}
|
|
expected := map[string]*int{"this": newPointer(1), "a key": newPointer(5)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
*m1["this"] = 6
|
|
assert.Equal(t, 1, *merged["this"])
|
|
})
|
|
|
|
t.Run("merge map[string]*int pointers are not copied - change in patched do not affect merged", func(t *testing.T) {
|
|
m1 := map[string]*int{"this": newPointer(1), "is": newPointer(3), "a key": newPointer(4)}
|
|
m2 := map[string]*int{"this": newPointer(2), "a key": newPointer(5)}
|
|
expected := map[string]*int{"this": newPointer(2), "a key": newPointer(5)}
|
|
|
|
merged, err := mergeStringPtrIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
*m2["this"] = 6
|
|
assert.Equal(t, 2, *merged["this"])
|
|
})
|
|
|
|
t.Run("merge map[string][]int overwrite base with patch", func(t *testing.T) {
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string][]int nil in patch /does/ overwrite base", func(t *testing.T) {
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "is": nil}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "is": nil}
|
|
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string][]int nil in base is overwritten", func(t *testing.T) {
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": nil}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string][]int nil in base is overwritten even without matching key", func(t *testing.T) {
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": nil}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "new": {4, 5, 6}}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "new": {4, 5, 6}}
|
|
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string][]int slice is cloned - change in base does not affect merged", func(t *testing.T) {
|
|
// shouldn't, is patch clobbers
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
m1["this"][0] = 99
|
|
assert.Equal(t, 1, merged["this"][0])
|
|
})
|
|
|
|
t.Run("merge map[string][]int slice is cloned - change in patch does not affect merged", func(t *testing.T) {
|
|
m1 := map[string][]int{"this": {1, 2, 3}, "is": {4, 5, 6}}
|
|
m2 := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
expected := map[string][]int{"this": {1, 2, 3}, "new": {7, 8, 9}}
|
|
|
|
merged, err := mergeStringSliceIntMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
m2["new"][1] = 0
|
|
assert.Equal(t, 8, merged["new"][1])
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int", func(t *testing.T) {
|
|
m1 := map[string]map[string]*int{"this": {"second": newPointer(99)}, "base": {"level": newPointer(10)}}
|
|
m2 := map[string]map[string]*int{"this": {"second": newPointer(77)}, "patch": {"level": newPointer(15)}}
|
|
expected := map[string]map[string]*int{"this": {"second": newPointer(77)}, "patch": {"level": newPointer(15)}}
|
|
|
|
merged, err := mergeMapOfMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, patch has nil keys -- /do/ overwrite base with nil", func(t *testing.T) {
|
|
m1 := map[string]map[string]*int{"this": {"second": newPointer(99)}, "base": {"level": newPointer(10)}}
|
|
m2 := map[string]map[string]*int{"this": {"second": nil}, "base": nil, "patch": {"level": newPointer(15)}}
|
|
expected := map[string]map[string]*int{"this": {"second": nil}, "base": nil, "patch": {"level": newPointer(15)}}
|
|
|
|
merged, err := mergeMapOfMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, base has nil vals -- overwrite base with patch", func(t *testing.T) {
|
|
m1 := map[string]map[string]*int{"this": {"second": nil}, "base": nil}
|
|
m2 := map[string]map[string]*int{"this": {"second": newPointer(77)}, "base": {"level": newPointer(10)}, "patch": {"level": newPointer(15)}}
|
|
expected := map[string]map[string]*int{"this": {"second": newPointer(77)}, "base": {"level": newPointer(10)}, "patch": {"level": newPointer(15)}}
|
|
|
|
merged, err := mergeMapOfMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, pointers are not copied - change in base does not affect merged", func(t *testing.T) {
|
|
// shouldn't, if we're overwriting completely
|
|
m1 := map[string]map[string]*int{"this": {"second": newPointer(99)}, "base": {"level": newPointer(10)}, "are belong": {"to us": newPointer(23)}}
|
|
m2 := map[string]map[string]*int{"base": {"level": newPointer(10)}}
|
|
expected := map[string]map[string]*int{"base": {"level": newPointer(10)}}
|
|
|
|
merged, err := mergeMapOfMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
|
|
// test changing the map entry's referenced value
|
|
*m1["base"]["level"] = 347
|
|
assert.Equal(t, 10, *merged["base"]["level"])
|
|
|
|
// test replacing map entry
|
|
m1["base"]["level"] = newPointer(12)
|
|
assert.Equal(t, 10, *merged["base"]["level"])
|
|
|
|
// test replacing a referenced map
|
|
m1["base"] = map[string]*int{"third": newPointer(777)}
|
|
assert.Equal(t, 10, *merged["base"]["level"])
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, pointers are not copied - change in patch do not affect merged", func(t *testing.T) {
|
|
m1 := map[string]map[string]*int{"base": {"level": newPointer(15)}}
|
|
m2 := map[string]map[string]*int{"this": {"second": newPointer(99)}, "patch": {"level": newPointer(10)},
|
|
"are belong": {"to us": newPointer(23)}}
|
|
expected := map[string]map[string]*int{"this": {"second": newPointer(99)}, "patch": {"level": newPointer(10)},
|
|
"are belong": {"to us": newPointer(23)}}
|
|
|
|
merged, err := mergeMapOfMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
|
|
// test replacing a referenced map
|
|
m2["this"] = map[string]*int{"third": newPointer(777)}
|
|
assert.Equal(t, 99, *merged["this"]["second"])
|
|
|
|
// test replacing map entry
|
|
m2["patch"]["level"] = newPointer(12)
|
|
assert.Equal(t, 10, *merged["patch"]["level"])
|
|
|
|
// test changing the map entry's referenced value
|
|
*m2["are belong"]["to us"] = 347
|
|
assert.Equal(t, 23, *merged["are belong"]["to us"])
|
|
})
|
|
|
|
t.Run("merge map[string]any", func(t *testing.T) {
|
|
m1 := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"base": map[string]*int{"level": newPointer(10)}}
|
|
m2 := map[string]any{"this": map[string]*int{"second": newPointer(77)},
|
|
"patch": map[string]*int{"level": newPointer(15)}}
|
|
expected := map[string]any{"this": map[string]*int{"second": newPointer(77)},
|
|
"patch": map[string]*int{"level": newPointer(15)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]any, patch has nil keys -- /do/ overwrite base with nil", func(t *testing.T) {
|
|
m1 := map[string]any{"this": map[string]*int{"second": newPointer(99)}}
|
|
m2 := map[string]any{"this": map[string]*int{"second": nil}}
|
|
expected := map[string]any{"this": map[string]*int{"second": nil}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]any, patch has nil keys -- /do/ overwrite base with nil (more complex)", func(t *testing.T) {
|
|
m1 := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"base": map[string]*int{"level": newPointer(10)}}
|
|
m2 := map[string]any{"this": map[string]*int{"second": nil},
|
|
"base": nil, "patch": map[string]*int{"level": newPointer(15)}}
|
|
expected := map[string]any{"this": map[string]*int{"second": nil},
|
|
"base": nil, "patch": map[string]*int{"level": newPointer(15)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, base has nil vals -- overwrite base with patch", func(t *testing.T) {
|
|
m1 := map[string]any{"base": nil}
|
|
m2 := map[string]any{"base": map[string]*int{"level": newPointer(10)}}
|
|
expected := map[string]any{"base": map[string]*int{"level": newPointer(10)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]map[string]*int, base has nil vals -- overwrite base with patch (more complex)", func(t *testing.T) {
|
|
m1 := map[string]any{"this": map[string]*int{"second": nil}, "base": nil}
|
|
m2 := map[string]any{"this": map[string]*int{"second": newPointer(77)},
|
|
"base": map[string]*int{"level": newPointer(10)}, "patch": map[string]*int{"level": newPointer(15)}}
|
|
expected := map[string]any{"this": map[string]*int{"second": newPointer(77)},
|
|
"base": map[string]*int{"level": newPointer(10)}, "patch": map[string]*int{"level": newPointer(15)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("merge map[string]any, pointers are not copied - changes in base do not affect merged", func(t *testing.T) {
|
|
m1 := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"base": map[string]*int{"level": newPointer(10)}, "are belong": map[string]*int{"to us": newPointer(23)}}
|
|
m2 := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"base": map[string]*int{"level": newPointer(10)}, "are belong": map[string]*int{"to us": newPointer(23)}}
|
|
expected := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"base": map[string]*int{"level": newPointer(10)}, "are belong": map[string]*int{"to us": newPointer(23)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
|
|
// test replacing a referenced map
|
|
m1["this"] = map[string]*int{"third": newPointer(777)}
|
|
assert.Equal(t, 99, *merged["this"].(map[string]*int)["second"])
|
|
|
|
// test replacing map entry
|
|
m1["base"].(map[string]*int)["level"] = newPointer(12)
|
|
assert.Equal(t, 10, *merged["base"].(map[string]*int)["level"])
|
|
|
|
// test changing the map entry's referenced value
|
|
*m1["are belong"].(map[string]*int)["to us"] = 347
|
|
assert.Equal(t, 23, *merged["are belong"].(map[string]*int)["to us"])
|
|
})
|
|
|
|
t.Run("merge map[string]any, pointers are not copied - change in patch do not affect merged", func(t *testing.T) {
|
|
m1 := map[string]any{"base": map[string]*int{"level": newPointer(15)}}
|
|
m2 := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"patch": map[string]*int{"level": newPointer(10)}, "are belong": map[string]*int{"to us": newPointer(23)}}
|
|
expected := map[string]any{"this": map[string]*int{"second": newPointer(99)},
|
|
"patch": map[string]*int{"level": newPointer(10)}, "are belong": map[string]*int{"to us": newPointer(23)}}
|
|
|
|
merged, err := mergeInterfaceMap(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
|
|
// test replacing a referenced map
|
|
m2["this"] = map[string]*int{"third": newPointer(777)}
|
|
assert.Equal(t, 99, *merged["this"].(map[string]*int)["second"])
|
|
|
|
// test replacing map entry
|
|
m2["patch"].(map[string]*int)["level"] = newPointer(12)
|
|
assert.Equal(t, 10, *merged["patch"].(map[string]*int)["level"])
|
|
|
|
// test changing the map entry's referenced value
|
|
*m2["are belong"].(map[string]*int)["to us"] = 347
|
|
assert.Equal(t, 23, *merged["are belong"].(map[string]*int)["to us"])
|
|
})
|
|
}
|
|
|
|
// Test merging slices alone. This isolates the complexity of merging slices from merging slices
|
|
// recursively in a struct/ptr/etc.
|
|
func TestMergeWithSlices(t *testing.T) {
|
|
t.Run("patch overwrites base slice", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "be", "overwritten"}
|
|
m2 := []string{"this one", "will", "replace the other", "one", "and", "is", "longer"}
|
|
expected := []string{"this one", "will", "replace the other", "one", "and", "is", "longer"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("patch overwrites base even when base is longer", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "be", "overwritten", "but", "not", "this"}
|
|
m2 := []string{"this one", "will", "replace the other", "one"}
|
|
expected := []string{"this one", "will", "replace the other", "one"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("patch overwrites when base is empty slice", func(t *testing.T) {
|
|
m1 := []string{}
|
|
m2 := []string{"this one", "will", "replace the other", "one"}
|
|
expected := []string{"this one", "will", "replace the other", "one"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("patch overwrites when base is nil", func(t *testing.T) {
|
|
var m1 []string
|
|
m2 := []string{"this one", "will", "replace the other", "one"}
|
|
expected := []string{"this one", "will", "replace the other", "one"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("patch overwrites when patch is empty struct", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "be", "overwritten"}
|
|
m2 := []string{}
|
|
expected := []string{}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("use base where patch is nil", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "not", "be", "overwritten"}
|
|
var m2 []string
|
|
expected := []string{"this", "will", "not", "be", "overwritten"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("return nil where both are nil", func(t *testing.T) {
|
|
var m1 []string
|
|
var m2 []string
|
|
expected := []string(nil)
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("return empty struct where both are empty", func(t *testing.T) {
|
|
m1 := []string{}
|
|
m2 := []string{}
|
|
expected := []string{}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
})
|
|
|
|
t.Run("patch is nil, slice is not copied. change in base will not affect merged", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "not", "be", "overwritten"}
|
|
var m2 []string
|
|
expected := []string{"this", "will", "not", "be", "overwritten"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
m1[0] = "THAT"
|
|
assert.Equal(t, "this", merged[0])
|
|
})
|
|
|
|
t.Run("patch empty, slice is not copied. change in patch will not affect merged", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "not", "be", "overwritten"}
|
|
m2 := []string{}
|
|
expected := []string{}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
// of course this won't change merged, even if it did copy... but just in case.
|
|
m2 = append(m2, "test")
|
|
assert.Len(t, m2, 1)
|
|
assert.Empty(t, merged)
|
|
})
|
|
|
|
t.Run("slice is not copied. change in patch will not affect merged", func(t *testing.T) {
|
|
var m1 []string
|
|
m2 := []string{"this", "will", "not", "be", "overwritten"}
|
|
expected := []string{"this", "will", "not", "be", "overwritten"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
m2[0] = "THAT"
|
|
assert.Equal(t, "this", merged[0])
|
|
})
|
|
|
|
t.Run("base overwritten, slice is not copied. change in patch will not affect merged", func(t *testing.T) {
|
|
m1 := []string{"this", "will", "be", "overwritten"}
|
|
m2 := []string{"that", "overwrote", "it"}
|
|
expected := []string{"that", "overwrote", "it"}
|
|
|
|
merged, err := mergeStringSlices(m1, m2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, merged)
|
|
m2[0] = "THAT!!"
|
|
assert.Equal(t, "that", merged[0])
|
|
})
|
|
}
|
|
|
|
type evenSimpler struct {
|
|
B *bool
|
|
ES2 *evenSimpler2
|
|
}
|
|
|
|
func (e *evenSimpler) String() string {
|
|
if e == nil {
|
|
return "nil"
|
|
}
|
|
sb := "nil"
|
|
if e.B != nil {
|
|
sb = fmt.Sprintf("%t", *e.B)
|
|
}
|
|
return fmt.Sprintf("ES{B: %s, ES2: %s}", sb, e.ES2.String())
|
|
}
|
|
|
|
type evenSimpler2 struct {
|
|
S *string
|
|
}
|
|
|
|
func (e *evenSimpler2) String() string {
|
|
if e == nil {
|
|
return "nil"
|
|
}
|
|
var s string
|
|
if e.S == nil {
|
|
s = "nil"
|
|
} else {
|
|
s = *e.S
|
|
}
|
|
return fmt.Sprintf("ES2{S: %s}", s)
|
|
}
|
|
|
|
func TestMergeWithEvenSimpler(t *testing.T) {
|
|
t.Run("evenSimplerStruct: base nils are overwritten by patch", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), &evenSimpler2{nil}}
|
|
t2 := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
expected := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: patch nils are ignored", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
t2 := evenSimpler{nil, &evenSimpler2{nil}}
|
|
expected := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: can handle both nils, merged will have nil (not zero value)", func(t *testing.T) {
|
|
t1 := evenSimpler{nil, &evenSimpler2{nil}}
|
|
t2 := evenSimpler{nil, &evenSimpler2{nil}}
|
|
expected := evenSimpler{nil, &evenSimpler2{nil}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: can handle both nils (ptr to ptr), merged will have nil (not zero value)", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), nil}
|
|
t2 := evenSimpler{newPointer(true), nil}
|
|
expected := evenSimpler{newPointer(true), nil}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: base nils (ptr to ptr) are overwritten by patch", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), nil}
|
|
t2 := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
expected := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: base nils (ptr to ptr) are overwritten by patch, and not copied - changes in patch don't affect merged", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), nil}
|
|
t2 := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
expected := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
*t2.ES2.S = "new patch"
|
|
assert.Equal(t, "patch", *merged.ES2.S)
|
|
})
|
|
|
|
t.Run("evenSimplerStruct: patch nils (ptr to ptr) do not overwrite base, and are not copied - changes in base don't affect merged", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
t2 := evenSimpler{newPointer(false), nil}
|
|
expected := evenSimpler{newPointer(false), &evenSimpler2{newPointer("base")}}
|
|
|
|
merged, err := mergeEvenSimpler(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
*t1.ES2.S = "new base"
|
|
assert.Equal(t, "base", *merged.ES2.S)
|
|
})
|
|
}
|
|
|
|
type sliceStruct struct {
|
|
Sls []string
|
|
}
|
|
|
|
func TestMergeWithSliceStruct(t *testing.T) {
|
|
t.Run("patch nils are ignored - sliceStruct", func(t *testing.T) {
|
|
t1 := sliceStruct{[]string{"this", "is", "base"}}
|
|
t2 := sliceStruct{nil}
|
|
expected := sliceStruct{[]string{"this", "is", "base"}}
|
|
|
|
merged, err := mergeSliceStruct(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("base nils are overwritten by patch - sliceStruct", func(t *testing.T) {
|
|
t1 := sliceStruct{nil}
|
|
t2 := sliceStruct{[]string{"this", "is", "patch"}}
|
|
expected := sliceStruct{[]string{"this", "is", "patch"}}
|
|
|
|
merged, err := mergeSliceStruct(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("slices are not being copied or modified", func(t *testing.T) {
|
|
t1 := sliceStruct{[]string{"this", "is", "base"}}
|
|
t2 := sliceStruct{nil}
|
|
expected := sliceStruct{[]string{"this", "is", "base"}}
|
|
|
|
merged, err := mergeSliceStruct(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
|
|
// changes in base do not affect merged
|
|
t1.Sls[0] = "test0"
|
|
assert.Equal(t, "this", merged.Sls[0])
|
|
|
|
// changes in merged (on slice that was cloned from base) do not affect base
|
|
merged.Sls[1] = "test222"
|
|
assert.Equal(t, "is", t1.Sls[1])
|
|
})
|
|
|
|
t.Run("slices are not being copied or modified", func(t *testing.T) {
|
|
t1 := sliceStruct{nil}
|
|
t2 := sliceStruct{[]string{"this", "is", "patch"}}
|
|
expected := sliceStruct{[]string{"this", "is", "patch"}}
|
|
|
|
merged, err := mergeSliceStruct(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
|
|
// changes in patch do not affect merged
|
|
t2.Sls[0] = "test0"
|
|
assert.Equal(t, "this", merged.Sls[0])
|
|
|
|
// changes in merged (on slice that was cloned from patch) do not affect patch
|
|
merged.Sls[1] = "test222"
|
|
assert.Equal(t, "is", t2.Sls[1])
|
|
})
|
|
}
|
|
|
|
type mapPtr struct {
|
|
MP map[string]*evenSimpler2
|
|
}
|
|
|
|
func TestMergeWithMapPtr(t *testing.T) {
|
|
t.Run("patch nils overwrite - mapPtr - maps overwrite completely", func(t *testing.T) {
|
|
t1 := mapPtr{map[string]*evenSimpler2{"base key": {newPointer("base")}}}
|
|
t2 := mapPtr{map[string]*evenSimpler2{"base key": {nil}}}
|
|
expected := mapPtr{map[string]*evenSimpler2{"base key": {nil}}}
|
|
|
|
merged, err := mergeMapPtr(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("patch nil structs are ignored - mapPtr - maps overwrite ", func(t *testing.T) {
|
|
t1 := mapPtr{map[string]*evenSimpler2{"base key": {newPointer("base")}}}
|
|
t2 := mapPtr{map[string]*evenSimpler2{"base key": nil}}
|
|
expected := mapPtr{map[string]*evenSimpler2{"base key": nil}}
|
|
|
|
merged, err := mergeMapPtr(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
}
|
|
|
|
type mapPtrState struct {
|
|
MP map[string]*state
|
|
}
|
|
type state struct {
|
|
Enable bool
|
|
}
|
|
|
|
func TestMergeWithMapPtrState(t *testing.T) {
|
|
t.Run("inside structs, patch map overwrites completely - mapPtrState", func(t *testing.T) {
|
|
t1 := mapPtrState{map[string]*state{"base key": {true}}}
|
|
t2 := mapPtrState{map[string]*state{"base key": nil}}
|
|
expected := mapPtrState{map[string]*state{"base key": nil}}
|
|
|
|
merged, err := mergeMapPtrState(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("merge identical structs - simple", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
expected := simple{42, 0, nil, newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("base nils are overwritten by patch", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), nil,
|
|
[]int{1, 2, 3}, nil,
|
|
simple2{30, nil, nil},
|
|
nil}
|
|
t2 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
expected := simple{42, 0, nil, newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
}
|
|
|
|
type mapPtrState2 struct {
|
|
MP map[string]*state2
|
|
}
|
|
type state2 struct {
|
|
Enable bool
|
|
EPtr *bool
|
|
}
|
|
|
|
func TestMergeWithMapPtrState2(t *testing.T) {
|
|
t.Run("inside structs, maps overwrite completely - mapPtrState2", func(t *testing.T) {
|
|
t1 := mapPtrState2{map[string]*state2{"base key": {true, newPointer(true)}}}
|
|
t2 := mapPtrState2{map[string]*state2{"base key": {false, nil}}}
|
|
expected := mapPtrState2{map[string]*state2{"base key": {false, nil}}}
|
|
|
|
merged, err := mergeMapPtrState2(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("inside structs, maps overwrite completely - mapPtrState2 2", func(t *testing.T) {
|
|
t1 := mapPtrState2{map[string]*state2{"base key": {true, newPointer(true)}}} //
|
|
t2 := mapPtrState2{map[string]*state2{"base key": nil}}
|
|
expected := mapPtrState2{map[string]*state2{"base key": nil}}
|
|
|
|
merged, err := mergeMapPtrState2(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
}
|
|
|
|
type simple struct {
|
|
I int
|
|
f float64
|
|
fp *float64
|
|
IP *int
|
|
B *bool
|
|
Sli []int
|
|
Msi map[string]int
|
|
S2 simple2
|
|
S3 *simple2
|
|
}
|
|
|
|
type simple2 struct {
|
|
I int
|
|
S *string
|
|
Sls []string
|
|
}
|
|
|
|
func TestMergeWithSimpleStruct(t *testing.T) {
|
|
t.Run("patch nils are ignored", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test base"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{42, 42.2, newPointer[float64](932.2), nil, nil,
|
|
nil, nil,
|
|
simple2{30, nil, nil},
|
|
&simple2{42, nil, nil}}
|
|
expected := simple{42, 0, nil, newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test base"), []string{"test1", "test2"}},
|
|
&simple2{42, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("patch nilled structs are ignored", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test base"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test base"), []string{"test1", "test2"}},
|
|
nil}
|
|
expected := simple{42, 0, nil, newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test base"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("can handle both nils", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), nil,
|
|
[]int{1, 2, 3}, nil,
|
|
simple2{30, nil, nil},
|
|
nil}
|
|
t2 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), nil,
|
|
[]int{1, 2, 3}, nil,
|
|
simple2{30, nil, nil},
|
|
nil}
|
|
expected := simple{42, 0, nil, newPointer(45), nil,
|
|
[]int{1, 2, 3}, nil,
|
|
simple2{30, nil, nil},
|
|
nil}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("different base vals are overwritten by patch, and unexported fields are ignored", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(45), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{13, 53.1, newPointer[float64](932.2), newPointer(46), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test99"}},
|
|
&simple2{45, nil, []string{"test3", "test123", "test5"}}}
|
|
expected := simple{13, 0, nil, newPointer(46), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test99"}},
|
|
&simple2{45, newPointer("test2"), []string{"test3", "test123", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, t1, *merged)
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("pointers are not being copied or modified", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(99), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{13, 53.1, newPointer[float64](932.2), nil, newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, nil, []string{"test3", "test4", "test5"}}}
|
|
expected := simple{13, 0, nil, newPointer(99), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, t1, *merged)
|
|
assert.Equal(t, expected, *merged)
|
|
|
|
// changes in originals do not affect merged
|
|
*t1.S3.S = "testBASE"
|
|
assert.Equal(t, "test2", *merged.S3.S)
|
|
*t2.B = true
|
|
assert.Equal(t, false, *merged.B)
|
|
|
|
// changes in base do not affect patched
|
|
*t1.S2.S = "test from base"
|
|
assert.NotEqual(t, *t1.S2.S, *t2.S2.S)
|
|
|
|
// changes in merged (on pointers that were cloned from base or patch) do not affect base or patch
|
|
*merged.IP = 0
|
|
assert.Equal(t, 99, *t1.IP)
|
|
*merged.S2.S = "testMERGED"
|
|
assert.NotEqual(t, *t2.S2.S, *merged.S2.S)
|
|
})
|
|
|
|
t.Run("slices are not being copied or modified", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(99), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{13, 53.1, newPointer[float64](932.2), nil, newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), nil},
|
|
&simple2{45, nil, []string{"test3", "test4", "test99"}}}
|
|
expected := simple{13, 0, nil, newPointer(99), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, newPointer("test2"), []string{"test3", "test4", "test99"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, t1, *merged)
|
|
assert.Equal(t, expected, *merged)
|
|
|
|
// changes in base do not affect merged
|
|
t1.S2.Sls[0] = "test0"
|
|
assert.Equal(t, "test1", merged.S2.Sls[0])
|
|
|
|
// changes in patch do not affect merged
|
|
t2.S3.Sls[0] = "test0"
|
|
assert.Equal(t, "test3", merged.S3.Sls[0])
|
|
|
|
// changes in merged (on slice that was cloned from base) do not affect base
|
|
merged.S2.Sls[1] = "test222"
|
|
assert.Equal(t, "test2", t1.S2.Sls[1])
|
|
})
|
|
|
|
t.Run("maps are not being copied or modified: base -> merged", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(99), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{13, 53.1, newPointer[float64](932.2), nil, newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, nil, []string{"test3", "test4", "test99"}}}
|
|
expected := simple{13, 0, nil, newPointer(99), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, newPointer("test2"), []string{"test3", "test4", "test99"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, t1, *merged)
|
|
assert.Equal(t, expected, *merged)
|
|
|
|
// changes in originals do not affect merged
|
|
t1.Msi["key1"] = 3
|
|
assert.Equal(t, 1, merged.Msi["key1"])
|
|
t2.Msi["key5"] = 5
|
|
_, ok := merged.Msi["key5"]
|
|
assert.False(t, ok)
|
|
})
|
|
|
|
t.Run("patch map overwrites", func(t *testing.T) {
|
|
t1 := simple{42, 42.2, newPointer[float64](932.2), newPointer(99), newPointer(true),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 1, "key2": 2, "key4": 4},
|
|
simple2{30, newPointer("test"), []string{"test1", "test2"}},
|
|
&simple2{40, newPointer("test2"), []string{"test3", "test4", "test5"}}}
|
|
t2 := simple{13, 53.1, newPointer[float64](932.2), nil, newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 11, "key2": 2, "key3": 3},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, nil, []string{"test3", "test4", "test99"}}}
|
|
expected := simple{13, 0, nil, newPointer(99), newPointer(false),
|
|
[]int{1, 2, 3}, map[string]int{"key1": 11, "key2": 2, "key3": 3},
|
|
simple2{30, newPointer("testpatch"), []string{"test1", "test2"}},
|
|
&simple2{45, newPointer("test2"), []string{"test3", "test4", "test99"}}}
|
|
|
|
merged, err := mergeSimple(t1, t2)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
}
|
|
|
|
// The following are tests to see if multiply nested structs/maps/slice and pointers to structs/maps/slices
|
|
// will merge. Probably overkill, but if anything goes wrong here, it is best to isolate the problem and
|
|
// make a simplified test (like many of the above tests).
|
|
func TestMergeWithVeryComplexStruct(t *testing.T) {
|
|
t.Run("merge identical structs", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
|
|
t.Run("merge identical structs as pointers", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
merged, err := mergeTestStructsPtrs(&base, &patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
|
|
t.Run("different base vals are overwritten by patch", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
base.F = 1342.12
|
|
base.Struct1.Pi = newPointer(937)
|
|
base.Struct1p.UI = 734
|
|
base.Struct1.Struct2.Sli = []int{123123, 1243123}
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, base, *merged)
|
|
assert.Equal(t, patch, *merged)
|
|
})
|
|
|
|
t.Run("blank string in patch overwrites base string b/c empty string is not nil", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
patch.S = ""
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, base, *merged)
|
|
assert.Equal(t, patch, *merged)
|
|
})
|
|
|
|
t.Run("nil values in patch are ignored", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
patch.Pi = nil
|
|
patch.Struct1.Pi16 = nil
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, patch, *merged)
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
|
|
t.Run("nil structs in patch are ignored", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
patch.Struct1p = nil
|
|
patch.Struct1.Struct2p = nil
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, patch, *merged)
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
|
|
t.Run("nil slices in patch are ignored", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
patch.Sls = nil
|
|
patch.Struct1.Sli = nil
|
|
patch.Struct1.Struct2p.Slf = nil
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, patch, *merged)
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
|
|
t.Run("nil maps in patch are ignored", func(t *testing.T) {
|
|
setupStructs(t)
|
|
|
|
patch.Msi = nil
|
|
patch.Mspi = nil
|
|
patch.Struct1.Mis = nil
|
|
patch.Struct1.Struct2p.Mspi = nil
|
|
|
|
merged, err := mergeTestStructs(base, patch)
|
|
require.NoError(t, err)
|
|
|
|
assert.NotEqual(t, patch, *merged)
|
|
assert.Equal(t, expectedMerged, *merged)
|
|
})
|
|
}
|
|
|
|
func TestMergeWithStructFieldFilter(t *testing.T) {
|
|
t.Run("filter skips merging from patch", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
t2 := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
expected := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
|
|
merged, err := mergeEvenSimplerWithConfig(t1, t2, &utils.MergeConfig{
|
|
StructFieldFilter: func(structField reflect.StructField, base, patch reflect.Value) bool {
|
|
return false
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
|
|
t.Run("filter skips merging configured fields from patch", func(t *testing.T) {
|
|
t1 := evenSimpler{newPointer(true), &evenSimpler2{newPointer("base")}}
|
|
t2 := evenSimpler{newPointer(false), &evenSimpler2{newPointer("patch")}}
|
|
expected := evenSimpler{newPointer(false), &evenSimpler2{newPointer("base")}}
|
|
|
|
merged, err := mergeEvenSimplerWithConfig(t1, t2, &utils.MergeConfig{
|
|
StructFieldFilter: func(structField reflect.StructField, base, patch reflect.Value) bool {
|
|
return structField.Name == "B"
|
|
},
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
assert.Equal(t, expected, *merged)
|
|
})
|
|
}
|
|
|
|
type testStruct struct {
|
|
I int
|
|
I8 int8
|
|
I16 int16
|
|
I32 int32
|
|
I64 int64
|
|
F float64
|
|
F32 float32
|
|
S string
|
|
UI uint
|
|
UI8 uint8
|
|
UI16 uint32
|
|
UI32 uint32
|
|
UI64 uint64
|
|
Pi *int
|
|
Pi8 *int8
|
|
Pi16 *int16
|
|
Pi32 *int32
|
|
Pi64 *int64
|
|
Pf *float64
|
|
Pf32 *float32
|
|
Ps *string
|
|
Pui *uint
|
|
Pui8 *uint8
|
|
Pui16 *uint16
|
|
Pui32 *uint32
|
|
Pui64 *uint64
|
|
Sls []string
|
|
Sli []int
|
|
Slf []float64
|
|
Msi map[string]int
|
|
Mis map[int]string
|
|
Mspi map[string]*int
|
|
Mips map[int]*string
|
|
Struct1 testStructEmbed
|
|
Struct1p *testStructEmbed
|
|
}
|
|
|
|
type testStructEmbed struct {
|
|
I int
|
|
I8 int8
|
|
I16 int16
|
|
I32 int32
|
|
I64 int64
|
|
F float64
|
|
F32 float32
|
|
S string
|
|
UI uint
|
|
UI8 uint8
|
|
UI16 uint32
|
|
UI32 uint32
|
|
UI64 uint64
|
|
Pi *int
|
|
Pi8 *int8
|
|
Pi16 *int16
|
|
Pi32 *int32
|
|
Pi64 *int64
|
|
Pf *float64
|
|
Pf32 *float32
|
|
Ps *string
|
|
Pui *uint
|
|
Pui8 *uint8
|
|
Pui16 *uint16
|
|
Pui32 *uint32
|
|
Pui64 *uint64
|
|
Sls []string
|
|
Sli []int
|
|
Slf []float64
|
|
Msi map[string]int
|
|
Mis map[int]string
|
|
Mspi map[string]*int
|
|
Mips map[int]*string
|
|
Struct2 testStructEmbed2
|
|
Struct2p *testStructEmbed2
|
|
}
|
|
|
|
type testStructEmbed2 struct {
|
|
I int
|
|
I8 int8
|
|
I16 int16
|
|
I32 int32
|
|
I64 int64
|
|
F float64
|
|
F32 float32
|
|
S string
|
|
UI uint
|
|
UI8 uint8
|
|
UI16 uint32
|
|
UI32 uint32
|
|
UI64 uint64
|
|
Pi *int
|
|
Pi8 *int8
|
|
Pi16 *int16
|
|
Pi32 *int32
|
|
Pi64 *int64
|
|
Pf *float64
|
|
Pf32 *float32
|
|
Ps *string
|
|
Pui *uint
|
|
Pui8 *uint8
|
|
Pui16 *uint16
|
|
Pui32 *uint32
|
|
Pui64 *uint64
|
|
Sls []string
|
|
Sli []int
|
|
Slf []float64
|
|
Msi map[string]int
|
|
Mis map[int]string
|
|
Mspi map[string]*int
|
|
Mips map[int]*string
|
|
}
|
|
|
|
// the base structs
|
|
var baseStructEmbed2A, baseStructEmbed2B, baseStructEmbed2C, baseStructEmbed2D testStructEmbed2
|
|
var baseStructEmbedBaseA, baseStructEmbedBaseB testStructEmbed
|
|
var base testStruct
|
|
|
|
// the patch structs
|
|
var patchStructEmbed2A, patchStructEmbed2B, patchStructEmbed2C, patchStructEmbed2D testStructEmbed2
|
|
var patchStructEmbedBaseA, patchStructEmbedBaseB testStructEmbed
|
|
var patch testStruct
|
|
|
|
// The merged structs
|
|
var mergeStructEmbed2A, mergeStructEmbed2B, mergeStructEmbed2C, mergeStructEmbed2D testStructEmbed2
|
|
var mergeStructEmbedBaseA, mergeStructEmbedBaseB testStructEmbed
|
|
var expectedMerged testStruct
|
|
|
|
func setupStructs(t *testing.T) {
|
|
t.Helper()
|
|
|
|
baseStructEmbed2A = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
baseStructEmbed2B = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
baseStructEmbed2C = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
baseStructEmbed2D = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
baseStructEmbedBaseA = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
baseStructEmbed2A, &baseStructEmbed2B,
|
|
}
|
|
|
|
baseStructEmbedBaseB = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
baseStructEmbed2C, &baseStructEmbed2D,
|
|
}
|
|
|
|
base = testStruct{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
baseStructEmbedBaseA, &baseStructEmbedBaseB,
|
|
}
|
|
|
|
patchStructEmbed2A = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
patchStructEmbed2B = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
patchStructEmbed2C = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
patchStructEmbed2D = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
patchStructEmbedBaseA = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
patchStructEmbed2A, &patchStructEmbed2B,
|
|
}
|
|
|
|
patchStructEmbedBaseB = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
patchStructEmbed2C, &patchStructEmbed2D,
|
|
}
|
|
|
|
patch = testStruct{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
patchStructEmbedBaseA, &patchStructEmbedBaseB,
|
|
}
|
|
|
|
mergeStructEmbed2A = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
mergeStructEmbed2B = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
mergeStructEmbed2C = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
mergeStructEmbed2D = testStructEmbed2{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
}
|
|
|
|
mergeStructEmbedBaseA = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
mergeStructEmbed2A, &mergeStructEmbed2B,
|
|
}
|
|
|
|
mergeStructEmbedBaseB = testStructEmbed{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
mergeStructEmbed2C, &mergeStructEmbed2D,
|
|
}
|
|
|
|
expectedMerged = testStruct{1, 2, 3, 4, 5, 1.1, 2.2, "test", 10, 11, 12, 12, 13,
|
|
newPointer(14), newPointer[int8](15), newPointer[int16](16), newPointer[int32](17), newPointer[int64](18),
|
|
newPointer[float64](19.9), newPointer[float32](20.1), newPointer("test pointer"),
|
|
newPointer[uint](21), newPointer[uint8](22), newPointer[uint16](23), newPointer[uint32](24), newPointer[uint64](25),
|
|
[]string{"test", "slice", "strings"}, []int{1, 2, 3, 4}, []float64{1.1, 2.2, 3.3},
|
|
map[string]int{"this": 1, "is": 2, "a": 3, "map": 4}, map[int]string{1: "this", 2: "is", 3: "another"},
|
|
map[string]*int{"wow": newPointer(1), "a map": newPointer(2), "of pointers!": newPointer(3)},
|
|
map[int]*string{1: newPointer("Another"), 2: newPointer("map of"), 3: newPointer("pointers, wow!")},
|
|
mergeStructEmbedBaseA, &mergeStructEmbedBaseB,
|
|
}
|
|
}
|
|
|
|
func mergeSimple(base, patch simple) (*simple, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeEvenSimpler(base, patch evenSimpler) (*evenSimpler, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeEvenSimplerWithConfig(base, patch evenSimpler, mergeConfig *utils.MergeConfig) (*evenSimpler, error) {
|
|
ret, err := utils.Merge(base, patch, mergeConfig)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeSliceStruct(base, patch sliceStruct) (*sliceStruct, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeMapPtr(base, patch mapPtr) (*mapPtr, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeMapPtrState(base, patch mapPtrState) (*mapPtrState, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeMapPtrState2(base, patch mapPtrState2) (*mapPtrState2, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeTestStructs(base, patch testStruct) (*testStruct, error) {
|
|
ret, err := utils.Merge(base, patch, nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return &ret, nil
|
|
}
|
|
|
|
func mergeStringIntMap(base, patch map[string]int) (map[string]int, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeStringPtrIntMap(base, patch map[string]*int) (map[string]*int, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeStringSliceIntMap(base, patch map[string][]int) (map[string][]int, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeMapOfMap(base, patch map[string]map[string]*int) (map[string]map[string]*int, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeInterfaceMap(base, patch map[string]any) (map[string]any, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeStringSlices(base, patch []string) ([]string, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func mergeTestStructsPtrs(base, patch *testStruct) (*testStruct, error) {
|
|
return utils.Merge(base, patch, nil)
|
|
}
|
|
|
|
func newPointer[T any](t T) *T {
|
|
return model.NewPointer(t)
|
|
}
|