mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-25 17:34:17 +00:00
Commit8ac8f49b5("Remove wildcard check from getNamespacesToList") removed the optimization that prevented "*" from being expanded to individual namespace names. This caused getNamespacesToList to return all namespace names instead of "" (cross-namespace listing), resulting in N separate API list calls per resource type instead of 1. On clusters with many namespaces (e.g. 178 on an ACM cluster), this means ~35,000 API calls instead of ~200, causing backups to take 18-20 minutes for just 8 items. Restore the "*" special case in ShouldExpandWildcards so that plain "*" is not expanded, and restore the ShouldInclude("*") check in getNamespacesToList so that cross-namespace listing is used. The restore fix from8ac8f49b5(fromBackup flag) is preserved since restores already return false before reaching the "*" check. Namespace exclusion continues to work correctly: the nsTracker filters excluded namespace objects via ShouldInclude, and backupItem filters namespace-scoped resources at line 124 of item_backupper.go. Fixes #9869 Signed-off-by: Shubham Pampattiwar <spampatt@redhat.com>
664 lines
19 KiB
Go
664 lines
19 KiB
Go
package wildcard
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestShouldExpandWildcards(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
includes []string
|
|
excludes []string
|
|
fromBackup bool
|
|
expected bool
|
|
}{
|
|
{
|
|
name: "no wildcards",
|
|
includes: []string{"ns1", "ns2"},
|
|
excludes: []string{"ns3", "ns4"},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "includes has star - should not expand",
|
|
includes: []string{"*"},
|
|
excludes: []string{"ns1"},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
excludes: []string{"ns3", "ns4"},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "includes has star with wildcard excludes - should not expand",
|
|
includes: []string{"*"},
|
|
excludes: []string{"ns*"},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "includes has star after a wildcard pattern - should not expand",
|
|
includes: []string{"ns*", "*"},
|
|
excludes: []string{"ns1"},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "includes has wildcard pattern",
|
|
includes: []string{"ns*"},
|
|
excludes: []string{"ns1"},
|
|
fromBackup: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "excludes has wildcard pattern",
|
|
includes: []string{"ns1"},
|
|
excludes: []string{"ns*"},
|
|
fromBackup: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "both have wildcard patterns",
|
|
includes: []string{"app-*"},
|
|
excludes: []string{"test-*"},
|
|
fromBackup: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "includes has star and wildcard - should not expand",
|
|
includes: []string{"*", "ns*"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "double asterisk should be detected as wildcard",
|
|
includes: []string{"**"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: true, // ** is a wildcard pattern (but will error during validation)
|
|
},
|
|
{
|
|
name: "empty slices",
|
|
includes: []string{},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "empty includes with wildcard excludes - should not expand",
|
|
includes: []string{},
|
|
excludes: []string{"ns*"},
|
|
expected: false,
|
|
},
|
|
{
|
|
name: "complex wildcard patterns",
|
|
includes: []string{"*-prod"},
|
|
excludes: []string{"test-*-staging"},
|
|
fromBackup: true,
|
|
expected: true,
|
|
},
|
|
{
|
|
name: "question mark wildcard",
|
|
includes: []string{"ns?"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: true, // question mark is now considered a wildcard
|
|
},
|
|
{
|
|
name: "character class wildcard",
|
|
includes: []string{"ns[abc]"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: true, // character class is considered wildcard
|
|
},
|
|
{
|
|
name: "brace alternatives wildcard",
|
|
includes: []string{"ns{prod,staging}"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: false, // brace alternatives are not supported
|
|
},
|
|
{
|
|
name: "dot is literal - not wildcard",
|
|
includes: []string{"app.prod"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: false, // dot is literal, not wildcard
|
|
},
|
|
{
|
|
name: "plus is literal - not wildcard",
|
|
includes: []string{"app+"},
|
|
excludes: []string{},
|
|
fromBackup: true,
|
|
expected: false, // plus is literal, not wildcard
|
|
},
|
|
{
|
|
name: "includes has a wildcard pattern from restore - should not expand",
|
|
includes: []string{"ns*"},
|
|
excludes: []string{"ns1"},
|
|
fromBackup: false,
|
|
expected: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result := ShouldExpandWildcards(tt.includes, tt.excludes, tt.fromBackup)
|
|
assert.Equal(t, tt.expected, result)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExpandWildcards(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
activeNamespaces []string
|
|
includes []string
|
|
excludes []string
|
|
expectedIncludes []string
|
|
expectedExcludes []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "no wildcards",
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3"},
|
|
includes: []string{"ns1", "ns4"},
|
|
excludes: []string{"ns2"},
|
|
expectedIncludes: []string{"ns1", "ns4"},
|
|
expectedExcludes: []string{"ns2"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "wildcard in includes",
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod", "test-ns"},
|
|
includes: []string{"app-*"},
|
|
excludes: []string{"test-ns"},
|
|
expectedIncludes: []string{"app-prod", "app-staging"},
|
|
expectedExcludes: []string{"test-ns"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "wildcard in excludes",
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod", "test-ns"},
|
|
includes: []string{"app-prod"},
|
|
excludes: []string{"*-staging"},
|
|
expectedIncludes: []string{"app-prod"},
|
|
expectedExcludes: []string{"app-staging"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "wildcards in both",
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod", "db-staging", "test-ns"},
|
|
includes: []string{"*-prod"},
|
|
excludes: []string{"*-staging"},
|
|
expectedIncludes: []string{"app-prod", "db-prod"},
|
|
expectedExcludes: []string{"app-staging", "db-staging"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "star pattern in includes",
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3"},
|
|
includes: []string{"*"},
|
|
excludes: []string{},
|
|
expectedIncludes: []string{"ns1", "ns2", "ns3"},
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "empty active namespaces",
|
|
activeNamespaces: []string{},
|
|
includes: []string{"app-*"},
|
|
excludes: []string{"test-*"},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "empty includes and excludes",
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
includes: []string{},
|
|
excludes: []string{},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "complex patterns",
|
|
activeNamespaces: []string{"my-app-prod", "my-app-staging", "your-app-prod", "system-ns"},
|
|
includes: []string{"*-app-*"},
|
|
excludes: []string{"*-staging"},
|
|
expectedIncludes: []string{"my-app-prod", "my-app-staging", "your-app-prod"},
|
|
expectedExcludes: []string{"my-app-staging"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "double asterisk should error",
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3"},
|
|
includes: []string{"**"},
|
|
excludes: []string{},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: true, // ** is invalid
|
|
},
|
|
{
|
|
name: "double asterisk in pattern should error",
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3"},
|
|
includes: []string{"app-**"},
|
|
excludes: []string{},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: true, // app-** contains ** which is invalid
|
|
},
|
|
{
|
|
name: "question mark patterns",
|
|
activeNamespaces: []string{"ns1", "ns2", "ns10", "test"},
|
|
includes: []string{"ns?"},
|
|
excludes: []string{},
|
|
expectedIncludes: []string{"ns1", "ns2"}, // ? matches single character
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "character class patterns",
|
|
activeNamespaces: []string{"nsa", "nsb", "nsc", "nsx", "ns1"},
|
|
includes: []string{"ns[abc]"},
|
|
excludes: []string{},
|
|
expectedIncludes: []string{"nsa", "nsb", "nsc"}, // [abc] matches a, b, or c
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "brace alternative patterns",
|
|
activeNamespaces: []string{"app-prod", "app-staging", "app-dev", "db-prod"},
|
|
includes: []string{"app-{prod,staging}"},
|
|
excludes: []string{},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: true,
|
|
},
|
|
{
|
|
name: "literal dot and plus patterns",
|
|
activeNamespaces: []string{"app.prod", "app-prod", "app+", "app"},
|
|
includes: []string{"app.prod", "app+"},
|
|
excludes: []string{},
|
|
expectedIncludes: []string{"app.prod", "app+"}, // . and + are literal
|
|
expectedExcludes: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "unsupported regex patterns should error",
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
includes: []string{"ns(1|2)"},
|
|
excludes: []string{},
|
|
expectedIncludes: nil,
|
|
expectedExcludes: nil,
|
|
expectError: true, // |, (, ) are not supported
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
includes, excludes, err := ExpandWildcards(tt.activeNamespaces, tt.includes, tt.excludes)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, tt.expectedIncludes, includes)
|
|
assert.ElementsMatch(t, tt.expectedExcludes, excludes)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestExpandWildcardsPrivate(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
patterns []string
|
|
activeNamespaces []string
|
|
expected []string
|
|
expectError bool
|
|
}{
|
|
{
|
|
name: "empty patterns",
|
|
patterns: []string{},
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
expected: nil,
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "non-wildcard patterns",
|
|
patterns: []string{"ns1", "ns3"},
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
expected: []string{"ns1", "ns3"}, // includes ns3 even if not in active
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "star pattern",
|
|
patterns: []string{"*"},
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3"},
|
|
expected: []string{"ns1", "ns2", "ns3"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "simple wildcard",
|
|
patterns: []string{"app-*"},
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod"},
|
|
expected: []string{"app-prod", "app-staging"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "multiple patterns",
|
|
patterns: []string{"app-*", "db-prod", "*-test"},
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod", "service-test", "other"},
|
|
expected: []string{"app-prod", "app-staging", "db-prod", "service-test"},
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "wildcard with no matches",
|
|
patterns: []string{"missing-*"},
|
|
activeNamespaces: []string{"app-prod", "db-staging"},
|
|
expected: []string{}, // returns empty slice, not nil
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "duplicate matches from multiple patterns",
|
|
patterns: []string{"app-*", "*-prod"},
|
|
activeNamespaces: []string{"app-prod", "app-staging", "db-prod"},
|
|
expected: []string{"app-prod", "app-staging", "db-prod"}, // no duplicates
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "question mark pattern - glob wildcard",
|
|
patterns: []string{"ns?"},
|
|
activeNamespaces: []string{"ns1", "ns2", "ns10"},
|
|
expected: []string{"ns1", "ns2"}, // ? is a glob pattern for single character
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "character class patterns",
|
|
patterns: []string{"ns[12]"},
|
|
activeNamespaces: []string{"ns1", "ns2", "ns3", "nsa"},
|
|
expected: []string{"ns1", "ns2"}, // [12] matches 1 or 2
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "character range patterns",
|
|
patterns: []string{"ns[a-c]"},
|
|
activeNamespaces: []string{"nsa", "nsb", "nsc", "nsd", "ns1"},
|
|
expected: []string{"nsa", "nsb", "nsc"}, // [a-c] matches a to c
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "double asterisk should error",
|
|
patterns: []string{"**"},
|
|
activeNamespaces: []string{"app-prod", "app.staging", "db/prod"},
|
|
expected: nil,
|
|
expectError: true, // ** is not allowed
|
|
},
|
|
{
|
|
name: "unsupported regex symbols should error",
|
|
patterns: []string{"ns(1|2)"},
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
expected: nil,
|
|
expectError: true, // |, (, ) not supported
|
|
},
|
|
{
|
|
name: "double asterisk should error",
|
|
patterns: []string{"**"},
|
|
activeNamespaces: []string{"ns1", "ns2"},
|
|
expected: nil,
|
|
expectError: true, // ** not allowed
|
|
},
|
|
{
|
|
name: "double asterisk in pattern should error",
|
|
patterns: []string{"app-**-prod"},
|
|
activeNamespaces: []string{"app-prod"},
|
|
expected: nil,
|
|
expectError: true, // ** not allowed anywhere
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
result, err := expandWildcards(tt.patterns, tt.activeNamespaces)
|
|
|
|
if tt.expectError {
|
|
assert.Error(t, err)
|
|
return
|
|
}
|
|
|
|
require.NoError(t, err)
|
|
if tt.expected == nil {
|
|
assert.Nil(t, result)
|
|
} else if len(tt.expected) == 0 {
|
|
assert.Empty(t, result)
|
|
} else {
|
|
assert.ElementsMatch(t, tt.expected, result)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestValidateBracePatterns(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pattern string
|
|
expectError bool
|
|
errorMsg string
|
|
}{
|
|
// Valid square bracket patterns
|
|
{
|
|
name: "valid square bracket pattern",
|
|
pattern: "ns[abc]",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid square bracket pattern with range",
|
|
pattern: "ns[a-z]",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid square bracket pattern with numbers",
|
|
pattern: "ns[0-9]",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid square bracket pattern with mixed",
|
|
pattern: "ns[a-z0-9]",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid square bracket pattern with single character",
|
|
pattern: "ns[a]",
|
|
expectError: false,
|
|
},
|
|
{
|
|
name: "valid square bracket pattern with text before and after",
|
|
pattern: "prefix-[abc]-suffix",
|
|
expectError: false,
|
|
},
|
|
// Unclosed opening brackets
|
|
{
|
|
name: "unclosed opening bracket at end",
|
|
pattern: "ns[abc",
|
|
expectError: true,
|
|
errorMsg: "unclosed bracket",
|
|
},
|
|
{
|
|
name: "unclosed opening bracket at start",
|
|
pattern: "[abc",
|
|
expectError: true,
|
|
errorMsg: "unclosed bracket",
|
|
},
|
|
{
|
|
name: "unclosed opening bracket in middle",
|
|
pattern: "ns[abc-test",
|
|
expectError: true,
|
|
errorMsg: "unclosed bracket",
|
|
},
|
|
|
|
// Unmatched closing brackets
|
|
{
|
|
name: "unmatched closing bracket at end",
|
|
pattern: "ns-abc]",
|
|
expectError: true,
|
|
errorMsg: "unmatched closing bracket",
|
|
},
|
|
{
|
|
name: "unmatched closing bracket at start",
|
|
pattern: "]ns-abc",
|
|
expectError: true,
|
|
errorMsg: "unmatched closing bracket",
|
|
},
|
|
{
|
|
name: "unmatched closing bracket in middle",
|
|
pattern: "ns-]abc",
|
|
expectError: true,
|
|
errorMsg: "unmatched closing bracket",
|
|
},
|
|
{
|
|
name: "extra closing bracket after valid pair",
|
|
pattern: "ns[abc]]",
|
|
expectError: true,
|
|
errorMsg: "unmatched closing bracket",
|
|
},
|
|
|
|
// Empty bracket patterns
|
|
{
|
|
name: "completely empty brackets",
|
|
pattern: "ns[]",
|
|
expectError: true,
|
|
errorMsg: "empty bracket pattern",
|
|
},
|
|
{
|
|
name: "empty brackets at start",
|
|
pattern: "[]ns",
|
|
expectError: true,
|
|
errorMsg: "empty bracket pattern",
|
|
},
|
|
{
|
|
name: "empty brackets standalone",
|
|
pattern: "[]",
|
|
expectError: true,
|
|
errorMsg: "empty bracket pattern",
|
|
},
|
|
|
|
// Edge cases
|
|
{
|
|
name: "empty pattern",
|
|
pattern: "",
|
|
expectError: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
err := validateBracePatterns(tt.pattern)
|
|
|
|
if tt.expectError {
|
|
require.Error(t, err, "Expected error for pattern: %s", tt.pattern)
|
|
if tt.errorMsg != "" {
|
|
assert.Contains(t, err.Error(), tt.errorMsg, "Error message should contain: %s", tt.errorMsg)
|
|
}
|
|
} else {
|
|
assert.NoError(t, err, "Expected no error for pattern: %s", tt.pattern)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// Edge case tests
|
|
func TestExpandWildcardsEdgeCases(t *testing.T) {
|
|
t.Run("nil inputs", func(t *testing.T) {
|
|
includes, excludes, err := ExpandWildcards(nil, nil, nil)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, includes)
|
|
assert.Nil(t, excludes)
|
|
})
|
|
|
|
t.Run("empty string patterns", func(t *testing.T) {
|
|
activeNamespaces := []string{"ns1", "ns2"}
|
|
result, err := expandWildcards([]string{""}, activeNamespaces)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []string{""}, result) // empty string is treated as literal
|
|
})
|
|
|
|
t.Run("whitespace patterns", func(t *testing.T) {
|
|
activeNamespaces := []string{"ns1", " ", "ns2"}
|
|
result, err := expandWildcards([]string{" "}, activeNamespaces)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []string{" "}, result)
|
|
})
|
|
|
|
t.Run("special characters in namespace names", func(t *testing.T) {
|
|
activeNamespaces := []string{"ns-1", "ns_2", "ns.3", "ns@4"}
|
|
result, err := expandWildcards([]string{"ns*"}, activeNamespaces)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []string{"ns-1", "ns_2", "ns.3", "ns@4"}, result)
|
|
})
|
|
|
|
t.Run("mixed literal and wildcard patterns", func(t *testing.T) {
|
|
activeNamespaces := []string{"app.prod", "app-prod", "app_prod", "test.ns"}
|
|
result, err := expandWildcards([]string{"app.prod", "app?prod"}, activeNamespaces)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t, []string{"app.prod", "app-prod", "app_prod"}, result)
|
|
})
|
|
|
|
t.Run("conservative asterisk validation", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pattern string
|
|
shouldError bool
|
|
}{
|
|
{"single asterisk", "*", false},
|
|
{"double asterisk", "**", true},
|
|
{"triple asterisk", "***", true},
|
|
{"quadruple asterisk", "****", true},
|
|
{"mixed with double", "app-**", true},
|
|
{"double in middle", "app-**-prod", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := expandWildcards([]string{tt.pattern}, []string{"test"})
|
|
if tt.shouldError {
|
|
assert.Error(t, err)
|
|
} else {
|
|
assert.NoError(t, err)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
|
|
t.Run("malformed pattern validation", func(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
pattern string
|
|
shouldError bool
|
|
}{
|
|
{"unclosed bracket", "ns[abc", true},
|
|
{"valid bracket", "ns[abc]", false},
|
|
{"empty bracket", "ns[]", true}, // empty brackets are invalid
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
_, err := expandWildcards([]string{tt.pattern}, []string{"test"})
|
|
if tt.shouldError {
|
|
assert.Error(t, err, "Expected error for pattern: %s", tt.pattern)
|
|
} else {
|
|
assert.NoError(t, err, "Expected no error for pattern: %s", tt.pattern)
|
|
}
|
|
})
|
|
}
|
|
})
|
|
}
|