mirror of
https://github.com/argoproj/argo-cd.git
synced 2026-02-20 01:28:45 +01:00
354 lines
10 KiB
Go
354 lines
10 KiB
Go
package generators
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
|
|
|
|
argoprojiov1alpha1 "github.com/argoproj/argo-cd/v3/pkg/apis/application/v1alpha1"
|
|
)
|
|
|
|
func getNestedListGenerator(json string) *argoprojiov1alpha1.ApplicationSetNestedGenerator {
|
|
return &argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
List: &argoprojiov1alpha1.ListGenerator{
|
|
Elements: []apiextensionsv1.JSON{{Raw: []byte(json)}},
|
|
},
|
|
}
|
|
}
|
|
|
|
func getTerminalListGeneratorMultiple(jsons []string) argoprojiov1alpha1.ApplicationSetTerminalGenerator {
|
|
elements := make([]apiextensionsv1.JSON, len(jsons))
|
|
|
|
for i, json := range jsons {
|
|
elements[i] = apiextensionsv1.JSON{Raw: []byte(json)}
|
|
}
|
|
|
|
generator := argoprojiov1alpha1.ApplicationSetTerminalGenerator{
|
|
List: &argoprojiov1alpha1.ListGenerator{
|
|
Elements: elements,
|
|
},
|
|
}
|
|
|
|
return generator
|
|
}
|
|
|
|
func listOfMapsToSet(maps []map[string]any) (map[string]bool, error) {
|
|
set := make(map[string]bool, len(maps))
|
|
for _, paramMap := range maps {
|
|
paramMapAsJSON, err := json.Marshal(paramMap)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
set[string(paramMapAsJSON)] = false
|
|
}
|
|
return set, nil
|
|
}
|
|
|
|
func TestMergeGenerate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
baseGenerators []argoprojiov1alpha1.ApplicationSetNestedGenerator
|
|
mergeKeys []string
|
|
expectedErr error
|
|
expected []map[string]any
|
|
}{
|
|
{
|
|
name: "no generators",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{},
|
|
mergeKeys: []string{"b"},
|
|
expectedErr: ErrLessThanTwoGeneratorsInMerge,
|
|
},
|
|
{
|
|
name: "one generator",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
*getNestedListGenerator(`{"a": "1_1","b": "same","c": "1_3"}`),
|
|
},
|
|
mergeKeys: []string{"b"},
|
|
expectedErr: ErrLessThanTwoGeneratorsInMerge,
|
|
},
|
|
{
|
|
name: "happy flow - generate paramSets",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
*getNestedListGenerator(`{"a": "1_1","b": "same","c": "1_3"}`),
|
|
*getNestedListGenerator(`{"a": "2_1","b": "same"}`),
|
|
*getNestedListGenerator(`{"a": "3_1","b": "different","c": "3_3"}`), // gets ignored because its merge key value isn't in the base params set
|
|
},
|
|
mergeKeys: []string{"b"},
|
|
expected: []map[string]any{
|
|
{"a": "2_1", "b": "same", "c": "1_3"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge keys absent - do not merge",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
*getNestedListGenerator(`{"a": "a"}`),
|
|
*getNestedListGenerator(`{"a": "a"}`),
|
|
},
|
|
mergeKeys: []string{"b"},
|
|
expected: []map[string]any{
|
|
{"a": "a"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge key present in first set, absent in second - do not merge",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
*getNestedListGenerator(`{"a": "a"}`),
|
|
*getNestedListGenerator(`{"b": "b"}`),
|
|
},
|
|
mergeKeys: []string{"b"},
|
|
expected: []map[string]any{
|
|
{"a": "a"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge nested matrix with some lists",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
{
|
|
Matrix: toAPIExtensionsJSON(t, &argoprojiov1alpha1.NestedMatrixGenerator{
|
|
Generators: []argoprojiov1alpha1.ApplicationSetTerminalGenerator{
|
|
getTerminalListGeneratorMultiple([]string{`{"a": "1"}`, `{"a": "2"}`}),
|
|
getTerminalListGeneratorMultiple([]string{`{"b": "1"}`, `{"b": "2"}`}),
|
|
},
|
|
}),
|
|
},
|
|
*getNestedListGenerator(`{"a": "1", "b": "1", "c": "added"}`),
|
|
},
|
|
mergeKeys: []string{"a", "b"},
|
|
expected: []map[string]any{
|
|
{"a": "1", "b": "1", "c": "added"},
|
|
{"a": "1", "b": "2"},
|
|
{"a": "2", "b": "1"},
|
|
{"a": "2", "b": "2"},
|
|
},
|
|
},
|
|
{
|
|
name: "merge nested merge with some lists",
|
|
baseGenerators: []argoprojiov1alpha1.ApplicationSetNestedGenerator{
|
|
{
|
|
Merge: toAPIExtensionsJSON(t, &argoprojiov1alpha1.NestedMergeGenerator{
|
|
MergeKeys: []string{"a"},
|
|
Generators: []argoprojiov1alpha1.ApplicationSetTerminalGenerator{
|
|
getTerminalListGeneratorMultiple([]string{`{"a": "1", "b": "1"}`, `{"a": "2", "b": "2"}`}),
|
|
getTerminalListGeneratorMultiple([]string{`{"a": "1", "b": "3", "c": "added"}`, `{"a": "3", "b": "2"}`}), // First gets merged, second gets ignored
|
|
},
|
|
}),
|
|
},
|
|
*getNestedListGenerator(`{"a": "1", "b": "3", "d": "added"}`),
|
|
},
|
|
mergeKeys: []string{"a", "b"},
|
|
expected: []map[string]any{
|
|
{"a": "1", "b": "3", "c": "added", "d": "added"},
|
|
{"a": "2", "b": "2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCaseCopy := testCase // since tests may run in parallel
|
|
|
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
appSet := &argoprojiov1alpha1.ApplicationSet{}
|
|
|
|
mergeGenerator := NewMergeGenerator(
|
|
map[string]Generator{
|
|
"List": &ListGenerator{},
|
|
"Matrix": &MatrixGenerator{
|
|
supportedGenerators: map[string]Generator{
|
|
"List": &ListGenerator{},
|
|
},
|
|
},
|
|
"Merge": &MergeGenerator{
|
|
supportedGenerators: map[string]Generator{
|
|
"List": &ListGenerator{},
|
|
},
|
|
},
|
|
},
|
|
)
|
|
|
|
got, err := mergeGenerator.GenerateParams(&argoprojiov1alpha1.ApplicationSetGenerator{
|
|
Merge: &argoprojiov1alpha1.MergeGenerator{
|
|
Generators: testCaseCopy.baseGenerators,
|
|
MergeKeys: testCaseCopy.mergeKeys,
|
|
Template: argoprojiov1alpha1.ApplicationSetTemplate{},
|
|
},
|
|
}, appSet, nil)
|
|
|
|
if testCaseCopy.expectedErr != nil {
|
|
require.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
|
} else {
|
|
expectedSet, err := listOfMapsToSet(testCaseCopy.expected)
|
|
require.NoError(t, err)
|
|
|
|
actualSet, err := listOfMapsToSet(got)
|
|
require.NoError(t, err)
|
|
|
|
require.NoError(t, err)
|
|
assert.Equal(t, expectedSet, actualSet)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func toAPIExtensionsJSON(t *testing.T, g any) *apiextensionsv1.JSON {
|
|
t.Helper()
|
|
resVal, err := json.Marshal(g)
|
|
if err != nil {
|
|
t.Error("unable to unmarshal json", g)
|
|
return nil
|
|
}
|
|
|
|
res := &apiextensionsv1.JSON{Raw: resVal}
|
|
|
|
return res
|
|
}
|
|
|
|
func TestParamSetsAreUniqueByMergeKeys(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
testCases := []struct {
|
|
name string
|
|
mergeKeys []string
|
|
paramSets []map[string]any
|
|
expectedErr error
|
|
expected map[string]map[string]any
|
|
}{
|
|
{
|
|
name: "no merge keys",
|
|
mergeKeys: []string{},
|
|
expectedErr: ErrNoMergeKeys,
|
|
},
|
|
{
|
|
name: "no paramSets",
|
|
mergeKeys: []string{"key"},
|
|
expected: make(map[string]map[string]any),
|
|
},
|
|
{
|
|
name: "simple key, unique paramSets",
|
|
mergeKeys: []string{"key"},
|
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}},
|
|
expected: map[string]map[string]any{
|
|
`{"key":"a"}`: {"key": "a"},
|
|
`{"key":"b"}`: {"key": "b"},
|
|
},
|
|
},
|
|
{
|
|
name: "simple key object, unique paramSets",
|
|
mergeKeys: []string{"key"},
|
|
paramSets: []map[string]any{{"key": map[string]any{"hello": "world"}}, {"key": "b"}},
|
|
expected: map[string]map[string]any{
|
|
`{"key":{"hello":"world"}}`: {"key": map[string]any{"hello": "world"}},
|
|
`{"key":"b"}`: {"key": "b"},
|
|
},
|
|
},
|
|
{
|
|
name: "simple key, non-unique paramSets",
|
|
mergeKeys: []string{"key"},
|
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
|
},
|
|
{
|
|
name: "simple key, duplicated key name, unique paramSets",
|
|
mergeKeys: []string{"key", "key"},
|
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}},
|
|
expected: map[string]map[string]any{
|
|
`{"key":"a"}`: {"key": "a"},
|
|
`{"key":"b"}`: {"key": "b"},
|
|
},
|
|
},
|
|
{
|
|
name: "simple key, duplicated key name, non-unique paramSets",
|
|
mergeKeys: []string{"key", "key"},
|
|
paramSets: []map[string]any{{"key": "a"}, {"key": "b"}, {"key": "b"}},
|
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key":"b"}`),
|
|
},
|
|
{
|
|
name: "compound key, unique paramSets",
|
|
mergeKeys: []string{"key1", "key2"},
|
|
paramSets: []map[string]any{
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "a", "key2": "b"},
|
|
{"key1": "b", "key2": "a"},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
|
},
|
|
},
|
|
{
|
|
name: "compound key object, unique paramSets",
|
|
mergeKeys: []string{"key1", "key2"},
|
|
paramSets: []map[string]any{
|
|
{"key1": "a", "key2": map[string]any{"hello": "world"}},
|
|
{"key1": "a", "key2": "b"},
|
|
{"key1": "b", "key2": "a"},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
`{"key1":"a","key2":{"hello":"world"}}`: {"key1": "a", "key2": map[string]any{"hello": "world"}},
|
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
|
},
|
|
},
|
|
{
|
|
name: "compound key, duplicate key names, unique paramSets",
|
|
mergeKeys: []string{"key1", "key1", "key2"},
|
|
paramSets: []map[string]any{
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "a", "key2": "b"},
|
|
{"key1": "b", "key2": "a"},
|
|
},
|
|
expected: map[string]map[string]any{
|
|
`{"key1":"a","key2":"a"}`: {"key1": "a", "key2": "a"},
|
|
`{"key1":"a","key2":"b"}`: {"key1": "a", "key2": "b"},
|
|
`{"key1":"b","key2":"a"}`: {"key1": "b", "key2": "a"},
|
|
},
|
|
},
|
|
{
|
|
name: "compound key, non-unique paramSets",
|
|
mergeKeys: []string{"key1", "key2"},
|
|
paramSets: []map[string]any{
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "b", "key2": "a"},
|
|
},
|
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key1":"a","key2":"a"}`),
|
|
},
|
|
{
|
|
name: "compound key, duplicate key names, non-unique paramSets",
|
|
mergeKeys: []string{"key1", "key1", "key2"},
|
|
paramSets: []map[string]any{
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "a", "key2": "a"},
|
|
{"key1": "b", "key2": "a"},
|
|
},
|
|
expectedErr: fmt.Errorf("%w. Duplicate key was %s", ErrNonUniqueParamSets, `{"key1":"a","key2":"a"}`),
|
|
},
|
|
}
|
|
|
|
for _, testCase := range testCases {
|
|
testCaseCopy := testCase // since tests may run in parallel
|
|
|
|
t.Run(testCaseCopy.name, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
got, err := getParamSetsByMergeKey(testCaseCopy.mergeKeys, testCaseCopy.paramSets)
|
|
|
|
if testCaseCopy.expectedErr != nil {
|
|
require.EqualError(t, err, testCaseCopy.expectedErr.Error())
|
|
} else {
|
|
require.NoError(t, err)
|
|
assert.Equal(t, testCaseCopy.expected, got)
|
|
}
|
|
})
|
|
}
|
|
}
|