package strutil import ( "encoding/base64" "encoding/json" "reflect" "testing" ) func TestStrUtil_StrListDelete(t *testing.T) { output := StrListDelete([]string{"item1", "item2", "item3"}, "item1") if StrListContains(output, "item1") { t.Fatal("bad: 'item1' should not have been present") } output = StrListDelete([]string{"item1", "item2", "item3"}, "item2") if StrListContains(output, "item2") { t.Fatal("bad: 'item2' should not have been present") } output = StrListDelete([]string{"item1", "item2", "item3"}, "item3") if StrListContains(output, "item3") { t.Fatal("bad: 'item3' should not have been present") } output = StrListDelete([]string{"item1", "item1", "item3"}, "item1") if !StrListContains(output, "item1") { t.Fatal("bad: 'item1' should have been present") } output = StrListDelete(output, "item1") if StrListContains(output, "item1") { t.Fatal("bad: 'item1' should not have been present") } output = StrListDelete(output, "random") if len(output) != 1 { t.Fatalf("bad: expected: 1, actual: %d", len(output)) } output = StrListDelete(output, "item3") if StrListContains(output, "item3") { t.Fatal("bad: 'item3' should not have been present") } } func TestStrutil_EquivalentSlices(t *testing.T) { slice1 := []string{"test2", "test1", "test3"} slice2 := []string{"test3", "test2", "test1"} if !EquivalentSlices(slice1, slice2) { t.Fatalf("bad: expected a match") } slice2 = append(slice2, "test4") if EquivalentSlices(slice1, slice2) { t.Fatalf("bad: expected a mismatch") } } func TestStrutil_ListContainsGlob(t *testing.T) { haystack := []string{ "dev", "ops*", "root/*", "*-dev", "_*_", } if StrListContainsGlob(haystack, "tubez") { t.Fatalf("Value shouldn't exist") } if !StrListContainsGlob(haystack, "root/test") { t.Fatalf("Value should exist") } if !StrListContainsGlob(haystack, "ops_test") { t.Fatalf("Value should exist") } if !StrListContainsGlob(haystack, "ops") { t.Fatalf("Value should exist") } if !StrListContainsGlob(haystack, "dev") { t.Fatalf("Value should exist") } if !StrListContainsGlob(haystack, "test-dev") { t.Fatalf("Value should exist") } if !StrListContainsGlob(haystack, "_test_") { t.Fatalf("Value should exist") } } func TestStrutil_ListContains(t *testing.T) { haystack := []string{ "dev", "ops", "prod", "root", } if StrListContains(haystack, "tubez") { t.Fatalf("Bad") } if !StrListContains(haystack, "root") { t.Fatalf("Bad") } } func TestStrutil_ListSubset(t *testing.T) { parent := []string{ "dev", "ops", "prod", "root", } child := []string{ "prod", "ops", } if !StrListSubset(parent, child) { t.Fatalf("Bad") } if !StrListSubset(parent, parent) { t.Fatalf("Bad") } if !StrListSubset(child, child) { t.Fatalf("Bad") } if !StrListSubset(child, nil) { t.Fatalf("Bad") } if StrListSubset(child, parent) { t.Fatalf("Bad") } if StrListSubset(nil, child) { t.Fatalf("Bad") } } func TestStrutil_ParseKeyValues(t *testing.T) { actual := make(map[string]string) expected := map[string]string{ "key1": "value1", "key2": "value2", } var input string var err error input = "key1=value1,key2=value2" err = ParseKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } input = "key1 = value1, key2 = value2" err = ParseKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } input = "key1 = value1, key2 = " err = ParseKeyValues(input, actual, ",") if err == nil { t.Fatalf("expected an error") } for k, _ := range actual { delete(actual, k) } input = "key1 = value1, = value2 " err = ParseKeyValues(input, actual, ",") if err == nil { t.Fatalf("expected an error") } for k, _ := range actual { delete(actual, k) } input = "key1" err = ParseKeyValues(input, actual, ",") if err == nil { t.Fatalf("expected an error") } } func TestStrutil_ParseArbitraryKeyValues(t *testing.T) { actual := make(map[string]string) expected := map[string]string{ "key1": "value1", "key2": "value2", } var input string var err error // Test = as comma separated string input = "key1=value1,key2=value2" err = ParseArbitraryKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } // Test = as base64 encoded comma separated string input = base64.StdEncoding.EncodeToString([]byte(input)) err = ParseArbitraryKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } // Test JSON encoded = tuples input = `{"key1":"value1", "key2":"value2"}` err = ParseArbitraryKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } // Test base64 encoded JSON string of = tuples input = base64.StdEncoding.EncodeToString([]byte(input)) err = ParseArbitraryKeyValues(input, actual, ",") if err != nil { t.Fatal(err) } if !reflect.DeepEqual(expected, actual) { t.Fatalf("bad: expected: %#v\nactual: %#v", expected, actual) } for k, _ := range actual { delete(actual, k) } } func TestStrutil_ParseArbitraryStringSlice(t *testing.T) { input := `CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}';GRANT "foo-role" TO "{{name}}";ALTER ROLE "{{name}}" SET search_path = foo;GRANT CONNECT ON DATABASE "postgres" TO "{{name}}";` jsonExpected := []string{ `DO $$ BEGIN IF NOT EXISTS (SELECT * FROM pg_catalog.pg_roles WHERE rolname='foo-role') THEN CREATE ROLE "foo-role"; CREATE SCHEMA IF NOT EXISTS foo AUTHORIZATION "foo-role"; ALTER ROLE "foo-role" SET search_path = foo; GRANT TEMPORARY ON DATABASE "postgres" TO "foo-role"; GRANT ALL PRIVILEGES ON SCHEMA foo TO "foo-role"; GRANT ALL PRIVILEGES ON ALL TABLES IN SCHEMA foo TO "foo-role"; GRANT ALL PRIVILEGES ON ALL SEQUENCES IN SCHEMA foo TO "foo-role"; GRANT ALL PRIVILEGES ON ALL FUNCTIONS IN SCHEMA foo TO "foo-role"; END IF; END $$`, `CREATE ROLE "{{name}}" WITH LOGIN PASSWORD '{{password}}' VALID UNTIL '{{expiration}}'`, `GRANT "foo-role" TO "{{name}}"`, `ALTER ROLE "{{name}}" SET search_path = foo`, `GRANT CONNECT ON DATABASE "postgres" TO "{{name}}"`, ``, } nonJSONExpected := jsonExpected[1:] var actual []string var inputB64 string var err error // Test non-JSON string actual = ParseArbitraryStringSlice(input, ";") if !reflect.DeepEqual(nonJSONExpected, actual) { t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", nonJSONExpected, actual) } // Test base64-encoded non-JSON string inputB64 = base64.StdEncoding.EncodeToString([]byte(input)) actual = ParseArbitraryStringSlice(inputB64, ";") if !reflect.DeepEqual(nonJSONExpected, actual) { t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", nonJSONExpected, actual) } // Test JSON encoded inputJSON, err := json.Marshal(jsonExpected) if err != nil { t.Fatal(err) } actual = ParseArbitraryStringSlice(string(inputJSON), ";") if !reflect.DeepEqual(jsonExpected, actual) { t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", string(inputJSON), actual) } // Test base64 encoded JSON string of = tuples inputB64 = base64.StdEncoding.EncodeToString(inputJSON) actual = ParseArbitraryStringSlice(inputB64, ";") if !reflect.DeepEqual(jsonExpected, actual) { t.Fatalf("bad: expected:\n%#v\nactual:\n%#v", jsonExpected, actual) } } func TestGlobbedStringsMatch(t *testing.T) { type tCase struct { item string val string expect bool } tCases := []tCase{ tCase{"", "", true}, tCase{"*", "*", true}, tCase{"**", "**", true}, tCase{"*t", "t", true}, tCase{"*t", "test", true}, tCase{"t*", "test", true}, tCase{"*test", "test", true}, tCase{"*test", "a test", true}, tCase{"test", "a test", false}, tCase{"*test", "tests", false}, tCase{"test*", "test", true}, tCase{"test*", "testsss", true}, tCase{"test**", "testsss", false}, tCase{"test**", "test*", true}, tCase{"**test", "*test", true}, tCase{"TEST", "test", false}, tCase{"test", "test", true}, } for _, tc := range tCases { actual := GlobbedStringsMatch(tc.item, tc.val) if actual != tc.expect { t.Fatalf("Bad testcase %#v, expected %t, got %t", tc, tc.expect, actual) } } } func TestTrimStrings(t *testing.T) { input := []string{"abc", "123", "abcd ", "123 "} expected := []string{"abc", "123", "abcd", "123"} actual := TrimStrings(input) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Bad TrimStrings: expected:%#v, got:%#v", expected, actual) } } func TestRemoveEmpty(t *testing.T) { input := []string{"abc", "", "abc", ""} expected := []string{"abc", "abc"} actual := RemoveEmpty(input) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Bad TrimStrings: expected:%#v, got:%#v", expected, actual) } input = []string{""} expected = []string{} actual = RemoveEmpty(input) if !reflect.DeepEqual(expected, actual) { t.Fatalf("Bad TrimStrings: expected:%#v, got:%#v", expected, actual) } } func TestStrutil_AppendIfMissing(t *testing.T) { keys := []string{} keys = AppendIfMissing(keys, "foo") if len(keys) != 1 { t.Fatalf("expected slice to be length of 1: %v", keys) } if keys[0] != "foo" { t.Fatalf("expected slice to contain key 'foo': %v", keys) } keys = AppendIfMissing(keys, "bar") if len(keys) != 2 { t.Fatalf("expected slice to be length of 2: %v", keys) } if keys[0] != "foo" { t.Fatalf("expected slice to contain key 'foo': %v", keys) } if keys[1] != "bar" { t.Fatalf("expected slice to contain key 'bar': %v", keys) } keys = AppendIfMissing(keys, "foo") if len(keys) != 2 { t.Fatalf("expected slice to still be length of 2: %v", keys) } if keys[0] != "foo" { t.Fatalf("expected slice to still contain key 'foo': %v", keys) } if keys[1] != "bar" { t.Fatalf("expected slice to still contain key 'bar': %v", keys) } } func TestStrUtil_RemoveDuplicates(t *testing.T) { type tCase struct { input []string expect []string lowercase bool } tCases := []tCase{ tCase{[]string{}, []string{}, false}, tCase{[]string{}, []string{}, true}, tCase{[]string{"a", "b", "a"}, []string{"a", "b"}, false}, tCase{[]string{"A", "b", "a"}, []string{"A", "a", "b"}, false}, tCase{[]string{"A", "b", "a"}, []string{"a", "b"}, true}, } for _, tc := range tCases { actual := RemoveDuplicates(tc.input, tc.lowercase) if !reflect.DeepEqual(actual, tc.expect) { t.Fatalf("Bad testcase %#v, expected %v, got %v", tc, tc.expect, actual) } } } func TestStrUtil_RemoveDuplicatesStable(t *testing.T) { type tCase struct { input []string expect []string caseInsensitive bool } tCases := []tCase{ tCase{[]string{}, []string{}, false}, tCase{[]string{}, []string{}, true}, tCase{[]string{"a", "b", "a"}, []string{"a", "b"}, false}, tCase{[]string{"A", "b", "a"}, []string{"A", "b", "a"}, false}, tCase{[]string{"A", "b", "a"}, []string{"A", "b"}, true}, tCase{[]string{" ", "d", "c", "d"}, []string{"d", "c"}, false}, tCase{[]string{"Z ", " z", " z ", "y"}, []string{"Z ", "y"}, true}, tCase{[]string{"Z ", " z", " z ", "y"}, []string{"Z ", " z", "y"}, false}, } for _, tc := range tCases { actual := RemoveDuplicatesStable(tc.input, tc.caseInsensitive) if !reflect.DeepEqual(actual, tc.expect) { t.Fatalf("Bad testcase %#v, expected %v, got %v", tc, tc.expect, actual) } } } func TestStrUtil_ParseStringSlice(t *testing.T) { type tCase struct { input string sep string expect []string } tCases := []tCase{ tCase{"", "", []string{}}, tCase{" ", ",", []string{}}, tCase{", ", ",", []string{"", ""}}, tCase{"a", ",", []string{"a"}}, tCase{" a, b, c ", ",", []string{"a", "b", "c"}}, tCase{" a; b; c ", ";", []string{"a", "b", "c"}}, tCase{" a :: b :: c ", "::", []string{"a", "b", "c"}}, } for _, tc := range tCases { actual := ParseStringSlice(tc.input, tc.sep) if !reflect.DeepEqual(actual, tc.expect) { t.Fatalf("Bad testcase %#v, expected %v, got %v", tc, tc.expect, actual) } } } func TestStrUtil_MergeSlices(t *testing.T) { res := MergeSlices([]string{"a", "c", "d"}, []string{}, []string{"c", "f", "a"}, nil, []string{"foo"}) expect := []string{"a", "c", "d", "f", "foo"} if !reflect.DeepEqual(res, expect) { t.Fatalf("expected %v, got %v", expect, res) } } func TestDifference(t *testing.T) { testCases := []struct { Name string SetA []string SetB []string Lowercase bool ExpectedResult []string }{ { Name: "case_sensitive", SetA: []string{"a", "b", "c"}, SetB: []string{"b", "c"}, Lowercase: false, ExpectedResult: []string{"a"}, }, { Name: "case_insensitive", SetA: []string{"a", "B", "c"}, SetB: []string{"b", "C"}, Lowercase: true, ExpectedResult: []string{"a"}, }, { Name: "no_match", SetA: []string{"a", "b", "c"}, SetB: []string{"d"}, Lowercase: false, ExpectedResult: []string{"a", "b", "c"}, }, { Name: "empty_set_a", SetA: []string{}, SetB: []string{"d", "e"}, Lowercase: false, ExpectedResult: []string{}, }, { Name: "empty_set_b", SetA: []string{"a", "b"}, SetB: []string{}, Lowercase: false, ExpectedResult: []string{"a", "b"}, }, } for _, tc := range testCases { t.Run(tc.Name, func(t *testing.T) { actualResult := Difference(tc.SetA, tc.SetB, tc.Lowercase) if !reflect.DeepEqual(actualResult, tc.ExpectedResult) { t.Fatalf("expected %v, got %v", tc.ExpectedResult, actualResult) } }) } } func TestStrUtil_EqualStringMaps(t *testing.T) { m1 := map[string]string{ "foo": "a", } m2 := map[string]string{ "foo": "a", "bar": "b", } var m3 map[string]string m4 := map[string]string{ "dog": "", } m5 := map[string]string{ "cat": "", } tests := []struct { a map[string]string b map[string]string result bool }{ {m1, m1, true}, {m2, m2, true}, {m1, m2, false}, {m2, m1, false}, {m2, m2, true}, {m3, m1, false}, {m3, m3, true}, {m4, m5, false}, } for i, test := range tests { actual := EqualStringMaps(test.a, test.b) if actual != test.result { t.Fatalf("case %d, expected %v, got %v", i, test.result, actual) } } }