mirror of https://github.com/harness/drone.git
675 lines
18 KiB
Go
675 lines
18 KiB
Go
// Copyright 2023 Harness, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package codeowners
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"github.com/harness/gitness/app/store"
|
|
"github.com/harness/gitness/git"
|
|
)
|
|
|
|
func TestService_ParseCodeOwner(t *testing.T) {
|
|
type fields struct {
|
|
repoStore store.RepoStore
|
|
git git.Interface
|
|
Config Config
|
|
}
|
|
type args struct {
|
|
codeOwnersContent string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
fields fields
|
|
args args
|
|
want []Entry
|
|
wantErr bool
|
|
}{
|
|
{
|
|
name: "Code owners Single",
|
|
args: args{`**/contracts/openapi/v1/ user1@harness.io user2@harness.io`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 1,
|
|
Pattern: "**/contracts/openapi/v1/",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: "Code owners Multiple",
|
|
args: args{`
|
|
**/contracts/openapi/v1/ user1@harness.io user2@harness.io
|
|
/scripts/api user3@harness.io user4@harness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "**/contracts/openapi/v1/",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 3,
|
|
Pattern: "/scripts/api",
|
|
Owners: []string{"user3@harness.io", "user4@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with full line comments",
|
|
args: args{`
|
|
# codeowner file
|
|
**/contracts/openapi/v1/ user1@harness.io user2@harness.io
|
|
#
|
|
/scripts/api user1@harness.io user2@harness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 3,
|
|
Pattern: "**/contracts/openapi/v1/",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 5,
|
|
Pattern: "/scripts/api",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with reset",
|
|
args: args{`
|
|
* user1@harness.io
|
|
/scripts/api
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "*",
|
|
Owners: []string{"user1@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 3,
|
|
Pattern: "/scripts/api",
|
|
Owners: []string{},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with escaped characters in pattern",
|
|
args: args{`
|
|
# escaped escape character
|
|
\\ user1@harness.io
|
|
# escaped control characters (are unescaped)
|
|
\ \ \# user2@harness.io
|
|
# escaped special pattern syntax characters (stay escaped)
|
|
\*\?\[\]\{\}\-\!\^ user3@harness.io
|
|
# mix of escapes
|
|
\\\ \*\\\\\? user4@harness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
|
|
LineNumber: 3,
|
|
Pattern: "\\\\",
|
|
Owners: []string{"user1@harness.io"},
|
|
},
|
|
{
|
|
|
|
LineNumber: 5,
|
|
Pattern: " #",
|
|
Owners: []string{"user2@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 7,
|
|
Pattern: "\\*\\?\\[\\]\\{\\}\\-\\!\\^",
|
|
Owners: []string{"user3@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 9,
|
|
Pattern: "\\\\ \\*\\\\\\\\\\?",
|
|
Owners: []string{"user4@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with multiple spaces as divider",
|
|
args: args{`
|
|
* user1@harness.io user2@harness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "*",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with invalid escaping standalone '\\'",
|
|
args: args{`
|
|
\
|
|
`},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: " Code owners with invalid escaping unsupported char",
|
|
args: args{`
|
|
\a
|
|
`},
|
|
wantErr: true,
|
|
},
|
|
{
|
|
name: " Code owners with utf8",
|
|
args: args{`
|
|
D∆NCE user@h∆rness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "D∆NCE",
|
|
Owners: []string{"user@h∆rness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with tabs and spaces",
|
|
args: args{`
|
|
a\ user1@harness.io user2@harness.io user3@harness.io
|
|
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "a ",
|
|
Owners: []string{"user1@harness.io", "user2@harness.io", "user3@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
name: " Code owners with inline comments",
|
|
args: args{`
|
|
a #user1@harness.io
|
|
b # user1@harness.io
|
|
c #
|
|
d#
|
|
e# user1@harness.io
|
|
f user1@harness.io#user2@harness.io
|
|
g user1@harness.io # user2@harness.io
|
|
`},
|
|
want: []Entry{
|
|
{
|
|
LineNumber: 2,
|
|
Pattern: "a",
|
|
Owners: []string{},
|
|
},
|
|
{
|
|
LineNumber: 3,
|
|
Pattern: "b",
|
|
Owners: []string{},
|
|
},
|
|
{
|
|
LineNumber: 4,
|
|
Pattern: "c",
|
|
Owners: []string{},
|
|
},
|
|
{
|
|
LineNumber: 5,
|
|
Pattern: "d",
|
|
Owners: []string{},
|
|
},
|
|
{
|
|
LineNumber: 6,
|
|
Pattern: "e",
|
|
Owners: []string{},
|
|
},
|
|
{
|
|
LineNumber: 7,
|
|
Pattern: "f",
|
|
Owners: []string{"user1@harness.io"},
|
|
},
|
|
{
|
|
LineNumber: 8,
|
|
Pattern: "g",
|
|
Owners: []string{"user1@harness.io"},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s := &Service{
|
|
repoStore: tt.fields.repoStore,
|
|
git: tt.fields.git,
|
|
config: tt.fields.Config,
|
|
}
|
|
got, err := s.parseCodeOwner(tt.args.codeOwnersContent)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ParseCodeOwner() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, tt.want) {
|
|
t.Errorf("ParseCodeOwner() got = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func Test_match(t *testing.T) {
|
|
type args struct {
|
|
pattern string
|
|
matchingTargets []string
|
|
nonMatchingTargets []string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
}{
|
|
{
|
|
name: "root",
|
|
args: args{
|
|
pattern: "/",
|
|
matchingTargets: []string{"a.txt", "x/a.txt", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{},
|
|
},
|
|
},
|
|
{
|
|
name: "file exact match",
|
|
args: args{
|
|
pattern: "/a.txt",
|
|
matchingTargets: []string{"a.txt", "a.txt/b.go", "a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt2", "b.txt", "a.go", "x/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "file exact match with directory",
|
|
args: args{
|
|
pattern: "/x/a.txt",
|
|
matchingTargets: []string{"x/a.txt", "x/a.txt/b.txt", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt", "x/a.txt2", "x/b.txt", "x/a.go"},
|
|
},
|
|
},
|
|
{
|
|
name: "file relative match",
|
|
args: args{
|
|
pattern: "a.txt",
|
|
matchingTargets: []string{"a.txt", "x/a.txt", "x/y/a.txt", "x/y/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt2", "b.txt", "a.go", "x/a.txt2"},
|
|
},
|
|
},
|
|
{
|
|
name: "file relative match with directory",
|
|
args: args{
|
|
pattern: "x/a.txt",
|
|
matchingTargets: []string{"x/a.txt", "x/a.txt/b.go", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt2", "b.txt", "a.go", "x/a.txt2", "v/x/a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "folder exact match",
|
|
args: args{
|
|
pattern: "/x/",
|
|
matchingTargets: []string{"x/a.txt", "x/b.go", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{"x", "a.txt", "y/a.txt", "w/x/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "folder relative match",
|
|
args: args{
|
|
pattern: "x/",
|
|
matchingTargets: []string{"x/a.txt", "x/b.txt", "w/x/a.txt", "w/x/y/a.txt"},
|
|
nonMatchingTargets: []string{"x", "w/x", "a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "match-all",
|
|
args: args{
|
|
pattern: "*",
|
|
matchingTargets: []string{"a", "a.txt", "x/a.txt", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{},
|
|
},
|
|
},
|
|
{
|
|
name: "match-all in relative dir",
|
|
args: args{
|
|
pattern: "x/*",
|
|
matchingTargets: []string{"x/a.txt"},
|
|
nonMatchingTargets: []string{"x", "y/a.txt", "w/x/b.go", "x/a.txt/b.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "match-all in absolute dir",
|
|
args: args{
|
|
pattern: "/x/*",
|
|
matchingTargets: []string{"x/a.txt", "x/b.go"},
|
|
nonMatchingTargets: []string{"x", "y/a.txt", "w/x/a.txt", "x/a.txt/b.go"},
|
|
},
|
|
},
|
|
{
|
|
name: "file match-all type",
|
|
args: args{
|
|
pattern: "*.txt",
|
|
matchingTargets: []string{"a.txt", "x/a.txt", "x/y/a.txt", "x/y/a.txt/b.go", "x/y/a.txt/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt2", "a.go"},
|
|
},
|
|
},
|
|
{
|
|
name: "file match-all type in root folder",
|
|
args: args{
|
|
pattern: "/*.txt",
|
|
matchingTargets: []string{"a.txt", "a.txt/b.go", "a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt2", "a.go", "x/a.txt", "x/y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "file match-all type in absolute sub folder",
|
|
args: args{
|
|
pattern: "/x/*.txt",
|
|
matchingTargets: []string{"x/a.txt", "x/a.txt/b.go", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt", "x/a.txt2", "x/a.go", "w/x/a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "file match-all types in relative sub folder",
|
|
args: args{
|
|
pattern: "x/*.txt",
|
|
matchingTargets: []string{"x/a.txt", "x/a.txt/b.go", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt", "x/a.txt2", "x/a.go", "w/x/a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "inner match-all",
|
|
args: args{
|
|
pattern: "/x/*/a.txt",
|
|
matchingTargets: []string{"x/y/a.txt", "x/y/a.txt/b.go", "x/y/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt", "x/a.txt", "w/x/y/a.txt", "x/y/z/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped match-all",
|
|
args: args{
|
|
pattern: "\\*",
|
|
matchingTargets: []string{"*", "x/y/*", "x/y/*/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt"},
|
|
},
|
|
},
|
|
/*
|
|
TODO: Fix bug in doublestar library, currently doesn't match `a.` ...
|
|
{
|
|
name: "trailing match-all on string",
|
|
args: args{
|
|
pattern: "a.*",
|
|
matchingTargets: []string{"a.", "a.txt", "x/a.txt", "x/a.txt/b.go", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"atxt", "b.txt"},
|
|
},
|
|
},
|
|
*/
|
|
{
|
|
name: "globstar",
|
|
args: args{
|
|
pattern: "**",
|
|
matchingTargets: []string{"a", "a.txt", "x/a.txt", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{},
|
|
},
|
|
},
|
|
{
|
|
name: "trailing globstar absolute path",
|
|
args: args{
|
|
pattern: "/x/**",
|
|
matchingTargets: []string{"x/a.txt", "x/b.txt", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "x", "y/a.txt", "w/x/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "trailing globstar relative path",
|
|
args: args{
|
|
pattern: "x/**",
|
|
matchingTargets: []string{"x/a.txt", "x/b.txt", "x/y/a.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "x", "y/a.txt", "w/x/a.txt"},
|
|
},
|
|
},
|
|
/*
|
|
TODO: Fix bug in doublestar library, currently doesn't match `a.` ...
|
|
{
|
|
name: "trailing globstar on string",
|
|
args: args{
|
|
pattern: "a.**",
|
|
matchingTargets: []string{"a.", "a.txt", "x/a.txt", "x/a.txt/b.go", "x/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"atxt", "b.txt"},
|
|
},
|
|
},
|
|
*/
|
|
{
|
|
name: "leading globstar",
|
|
args: args{
|
|
pattern: "**/a.txt",
|
|
matchingTargets: []string{"a.txt", "x/a.txt", "x/y/a.txt", "x/y/a.txt/b.go", "x/y/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"b.txt", "a.txt2"},
|
|
},
|
|
},
|
|
{
|
|
name: "surrounding globstar",
|
|
args: args{
|
|
pattern: "**/x/**",
|
|
matchingTargets: []string{"x/a.txt", "w/x/a.txt", "x/y/a.txt", "w/x/y/a.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "x", "w/x"},
|
|
},
|
|
},
|
|
{
|
|
name: "inner globstar",
|
|
args: args{
|
|
pattern: "/x/**/a.txt",
|
|
matchingTargets: []string{
|
|
"x/a.txt", "x/y/a.txt", "x/y/z/a.txt", "x/y/z/a.txt/b.go", "x/y/z/a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.txt", "w/x/a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "multi-inner globstar",
|
|
args: args{
|
|
pattern: "/x/**/z/**/a.txt",
|
|
matchingTargets: []string{
|
|
"x/z/a.txt",
|
|
"x/y/z/l/a.txt",
|
|
"x/y/yy/z/l/ll/a.txt",
|
|
"x/y/yy/z/l/ll/a.txt/b.go",
|
|
"x/y/yy/z/l/ll/a.txt/b.go/c.ar",
|
|
},
|
|
nonMatchingTargets: []string{"a.txt", "x/a.txt", "z/a.txt", "w/x/a.txt", "y/a.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "dirty globstar",
|
|
args: args{
|
|
pattern: "/a**.txt",
|
|
matchingTargets: []string{"a.txt", "abc.txt", "a.txt/b.go", "a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a/b/.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped globstar",
|
|
args: args{
|
|
pattern: "\\*\\*",
|
|
matchingTargets: []string{"**", "x/**", "**/y", "x/**/y"},
|
|
nonMatchingTargets: []string{"x"},
|
|
},
|
|
},
|
|
{
|
|
name: "partially escaped globstar",
|
|
args: args{
|
|
pattern: "*\\*",
|
|
matchingTargets: []string{"*", "**", "a*", "x/*", "x/a*", "*/y", "a*/", "x/*/y", "x/a*/y"},
|
|
nonMatchingTargets: []string{"x"},
|
|
},
|
|
},
|
|
{
|
|
name: "single wildchar",
|
|
args: args{
|
|
pattern: "/a.?xt",
|
|
matchingTargets: []string{"a.txt", "a.xxt/b.go", "a.xxt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"x/a.txt", "z/a.txt", "w/x/a.txt", "y/a.txt", "a./xt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped single wildchar",
|
|
args: args{
|
|
pattern: "/a.\\?xt",
|
|
matchingTargets: []string{"a.?xt", "a.?xt/b.go", "a.?xt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{"a.\\?xt", "a.txt", "x/a.?xt"},
|
|
},
|
|
},
|
|
{
|
|
name: "class",
|
|
args: args{
|
|
pattern: "/[abc].txt",
|
|
matchingTargets: []string{"a.txt", "b.txt", "c.txt"},
|
|
nonMatchingTargets: []string{"[a-c].txt", "d.txt", "A.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "range class",
|
|
args: args{
|
|
pattern: "/[a-c].txt",
|
|
matchingTargets: []string{"a.txt", "b.txt", "c.txt"},
|
|
nonMatchingTargets: []string{"[a-c].txt", "d.txt", "A.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped class",
|
|
args: args{
|
|
pattern: "/\\[a-c\\].txt",
|
|
matchingTargets: []string{"[a-c].txt"},
|
|
nonMatchingTargets: []string{"\\[a-c\\].txt", "a.txt", "b.txt", "c.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "class escaped control chars",
|
|
args: args{
|
|
pattern: "/[\\!\\^\\-a-c].txt",
|
|
matchingTargets: []string{"a.txt", "b.txt", "c.txt", "^.txt", "!.txt", "-.txt"},
|
|
nonMatchingTargets: []string{"d.txt", "[\\!\\^\\-a-c].txt", "[!^-a-c].txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "inverted class ^",
|
|
args: args{
|
|
pattern: "/[^a-c].txt",
|
|
matchingTargets: []string{"d.txt", "B.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "b.txt", "c.txt", "[^a-c].txt", "[a-c].txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped inverted class ^",
|
|
args: args{
|
|
pattern: "/\\[^a-c\\].txt",
|
|
matchingTargets: []string{"[^a-c].txt"},
|
|
nonMatchingTargets: []string{"\\[^a-c\\].txt", "a.txt", "b.txt", "c.txt", "d.txt", "[a-c].txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "inverted class !",
|
|
args: args{
|
|
pattern: "/[!a-c].txt",
|
|
matchingTargets: []string{"d.txt", "B.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "b.txt", "c.txt", "[!a-c].txt", "[a-c].txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "escaped inverted class !",
|
|
args: args{
|
|
pattern: "/\\[!a-c\\].txt",
|
|
matchingTargets: []string{"[!a-c].txt"},
|
|
nonMatchingTargets: []string{"\\[!a-c\\].txt", "a.txt", "b.txt", "c.txt", "d.txt", "[a-c].txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "alternate matches",
|
|
args: args{
|
|
pattern: "/{a,b,[c-d],e?,f\\*}.txt",
|
|
matchingTargets: []string{
|
|
"a.txt", "b.txt", "c.txt", "d.txt", "e2.txt", "f*.txt", "a.txt/b.go", "a.txt/b.go/c.ar"},
|
|
nonMatchingTargets: []string{
|
|
"{a,b,[c-d],e?,f\\*}.txt", "{a,b,[c-d],e?,f*}.txt", "e.txt", "f.txt", "g.txt", "ab.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "space",
|
|
args: args{
|
|
pattern: "/a b.txt",
|
|
matchingTargets: []string{"a b.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "b.txt", "ab.txt", "a b.txt"},
|
|
},
|
|
},
|
|
{
|
|
name: "tab",
|
|
args: args{
|
|
pattern: "/a b.txt",
|
|
matchingTargets: []string{"a b.txt"},
|
|
nonMatchingTargets: []string{"a.txt", "b.txt", "ab.txt", "a b.txt", "a b.txt"},
|
|
},
|
|
},
|
|
{
|
|
// Note: it's debatable which behavior is correct - for now keep doublestar default behavior on this.
|
|
// Keeping UT to ensure we don't accidentally change behavior.
|
|
name: "escaped backslash",
|
|
args: args{
|
|
pattern: "/a\\\\/b.txt",
|
|
matchingTargets: []string{"a\\/b.txt"},
|
|
nonMatchingTargets: []string{"a\\\\/b.txt", "a/b.txt", "a/b.txt/c.ar"},
|
|
},
|
|
},
|
|
{
|
|
// Note: it's debatable which behavior is correct - for now keep doublestar default behavior on this.
|
|
// Keeping UT to ensure we don't accidentally change behavior.
|
|
name: "escaped path separator",
|
|
args: args{
|
|
pattern: "/a\\/b.txt",
|
|
matchingTargets: []string{"a/b.txt", "a/b.txt/c.ar"},
|
|
nonMatchingTargets: []string{"a\\/b.txt"},
|
|
},
|
|
},
|
|
}
|
|
testMatch := func(pattern string, target string, want bool) {
|
|
got, err := match(pattern, target)
|
|
if err != nil {
|
|
t.Errorf("failed with error: %s", err)
|
|
} else if got != want {
|
|
t.Errorf("match(%q, %q) = %t but wanted %t)", pattern, target, got, want)
|
|
}
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
for _, target := range tt.args.matchingTargets {
|
|
if len(target) > 0 && target[0] == '/' {
|
|
t.Errorf("target shouldn't start with leading '/'")
|
|
}
|
|
|
|
testMatch(tt.args.pattern, target, true)
|
|
testMatch(tt.args.pattern, "/"+target, true)
|
|
}
|
|
for _, target := range tt.args.nonMatchingTargets {
|
|
if len(target) > 0 && target[0] == '/' {
|
|
t.Errorf("target shouldn't start with leading '/'")
|
|
}
|
|
|
|
testMatch(tt.args.pattern, target, false)
|
|
testMatch(tt.args.pattern, "/"+target, false)
|
|
}
|
|
})
|
|
}
|
|
}
|