Skip to content

Commit 3ddbc3d

Browse files
committed
Refactor matching path segment functions and add tests
1 parent ca7704b commit 3ddbc3d

File tree

2 files changed

+201
-49
lines changed

2 files changed

+201
-49
lines changed

utils/url/url.go

Lines changed: 36 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -9,38 +9,49 @@ import (
99

1010
const defaultPathSeparator = "/"
1111

12-
// HasMatchingPathSegments checks whether two path strings match based on their segments.
13-
func HasMatchingPathSegments(pathA, pathB string) (match bool, err error) {
14-
if reflection.IsEmpty(pathA) {
15-
err = commonerrors.UndefinedVariable("pathA")
16-
return
17-
}
18-
19-
pathASegments := SplitPath(pathA)
20-
pathBSegments := SplitPath(pathB)
21-
if len(pathASegments) != len(pathBSegments) {
22-
return
23-
}
24-
25-
for i := range pathBSegments {
26-
pathBSeg, pathASeg := pathBSegments[i], pathASegments[i]
27-
if pathBSeg != pathASeg {
28-
return
29-
}
30-
}
12+
// IsParamSegment checks whether the segment string is a path parameter
13+
func IsParamSegment(segment string) bool {
14+
return len(segment) >= 2 && segment[0] == '{' && segment[len(segment)-1] == '}'
15+
}
3116

32-
match = true
33-
return
17+
// HasMatchingPathSegments checks whether two path strings match based on their segments.
18+
func HasMatchingPathSegments(pathA, pathB string) (bool, error) {
19+
return hasMatchingPathSegments(pathA, pathB, func(segmentA, segmentB string) bool {
20+
return segmentA == segmentB
21+
})
3422
}
3523

36-
// HasMatchingPathSegmentsWithParams is similar to MatchingPathSegments but considers segments as matching if at least one of them contains a path parameter.
24+
// HasMatchingPathSegmentsWithParams is similar to HasMatchingPathSegments but also considers segments as matching if at least one of them contains a path parameter.
3725
//
3826
// HasMatchingPathSegmentsWithParams("/some/{param}/path", "/some/{param}/path") // true
3927
// HasMatchingPathSegmentsWithParams("/some/abc/path", "/some/{param}/path") // true
4028
// HasMatchingPathSegmentsWithParams("/some/abc/path", "/some/def/path") // false
41-
func HasMatchingPathSegmentsWithParams(pathA, pathB string) (match bool, err error) {
29+
func HasMatchingPathSegmentsWithParams(pathA, pathB string) (bool, error) {
30+
return hasMatchingPathSegments(pathA, pathB, func(pathASeg, pathBSeg string) bool {
31+
switch {
32+
case IsParamSegment(pathASeg):
33+
return pathBSeg != ""
34+
case IsParamSegment(pathBSeg):
35+
return pathASeg != ""
36+
default:
37+
return pathASeg == pathBSeg
38+
}
39+
})
40+
}
41+
42+
func hasMatchingPathSegments(pathA, pathB string, segmentMatcherFn func(segmentA, segmentB string) bool) (match bool, err error) {
4243
if reflection.IsEmpty(pathA) {
43-
err = commonerrors.UndefinedVariable("pathA")
44+
err = commonerrors.UndefinedVariable("path A")
45+
return
46+
}
47+
48+
if reflection.IsEmpty(pathB) {
49+
err = commonerrors.UndefinedVariable("path B")
50+
return
51+
}
52+
53+
if segmentMatcherFn == nil {
54+
err = commonerrors.UndefinedVariable("segment matcher function")
4455
return
4556
}
4657

@@ -51,21 +62,7 @@ func HasMatchingPathSegmentsWithParams(pathA, pathB string) (match bool, err err
5162
}
5263

5364
for i := range pathBSegments {
54-
pathBSeg, pathASeg := pathBSegments[i], pathASegments[i]
55-
if IsParamSegment(pathASeg) {
56-
if pathBSeg == "" {
57-
return
58-
}
59-
continue
60-
}
61-
62-
if IsParamSegment(pathBSeg) {
63-
if pathASeg == "" {
64-
return
65-
}
66-
continue
67-
}
68-
if pathBSeg != pathASeg {
65+
if !segmentMatcherFn(pathASegments[i], pathBSegments[i]) {
6966
return
7067
}
7168
}
@@ -97,11 +94,6 @@ func SplitPathWithSeparator(path string, separator string) []string {
9794
return out
9895
}
9996

100-
// IsParamSegment checks whether the segment string is a path parameter
101-
func IsParamSegment(segment string) bool {
102-
return len(segment) >= 2 && segment[0] == '{' && segment[len(segment)-1] == '}'
103-
}
104-
10597
// JoinPaths returns a single concatenated path string from the supplied paths and correctly sets the default "/" separator between them.
10698
func JoinPaths(paths ...string) (joinedPath string, err error) {
10799
return JoinPathsWithSeparator(defaultPathSeparator, paths...)

utils/url/url_test.go

Lines changed: 165 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
package url
22

33
import (
4-
"fmt"
54
"testing"
65

76
"github.com/ARM-software/golang-utils/utils/commonerrors"
@@ -19,6 +18,167 @@ func TestUrl_IsParamSegment(t *testing.T) {
1918
})
2019
}
2120

21+
func TestUrl_HasMatchingPathSegments(t *testing.T) {
22+
tests := []struct {
23+
name string
24+
pathA string
25+
pathB string
26+
result bool
27+
err error
28+
}{
29+
{
30+
"empty pathA",
31+
"",
32+
"abc/123",
33+
false,
34+
commonerrors.ErrUndefined,
35+
},
36+
{
37+
"empty pathB",
38+
"abc/123",
39+
"",
40+
false,
41+
commonerrors.ErrUndefined,
42+
},
43+
{
44+
"identical paths",
45+
"abc/123",
46+
"abc/123",
47+
true,
48+
nil,
49+
},
50+
{
51+
"identical paths with multiple segments",
52+
"abc/123/def/456/zzz",
53+
"abc/123/def/456/zzz",
54+
true,
55+
nil,
56+
},
57+
{
58+
"root paths",
59+
"/",
60+
"/",
61+
true,
62+
nil,
63+
},
64+
{
65+
"paths with different segment values",
66+
"abc/123",
67+
"abc/456",
68+
false,
69+
nil,
70+
},
71+
{
72+
"paths with different lengths",
73+
"abc/123",
74+
"abc/123/456",
75+
false,
76+
nil,
77+
},
78+
{
79+
"path with trailing slashes",
80+
"/abc/123/",
81+
"abc/123",
82+
true,
83+
nil,
84+
},
85+
}
86+
87+
for i := range tests {
88+
test := tests[i]
89+
t.Run(test.name, func(t *testing.T) {
90+
match, err := HasMatchingPathSegments(test.pathA, test.pathB)
91+
92+
errortest.AssertError(t, err, test.err)
93+
assert.Equal(t, test.result, match)
94+
})
95+
}
96+
}
97+
98+
func TestUrl_HasMatchingPathSegmentsWithParams(t *testing.T) {
99+
tests := []struct {
100+
name string
101+
pathA string
102+
pathB string
103+
result bool
104+
err error
105+
}{
106+
{
107+
"empty pathA",
108+
"",
109+
"abc/123",
110+
false,
111+
commonerrors.ErrUndefined,
112+
},
113+
{
114+
"empty pathB",
115+
"abc/123",
116+
"",
117+
false,
118+
commonerrors.ErrUndefined,
119+
},
120+
{
121+
"identical paths",
122+
"abc/123",
123+
"abc/123",
124+
true,
125+
nil,
126+
},
127+
{
128+
"identical paths with multiple segments",
129+
"abc/123/def/456/zzz",
130+
"abc/123/def/456/zzz",
131+
true,
132+
nil,
133+
},
134+
{
135+
"path with parameter segment",
136+
"/abc/{id}/123",
137+
"/abc/123/123",
138+
true,
139+
nil,
140+
},
141+
{
142+
"both paths with matching parameter segments",
143+
"/abc/{param}/123",
144+
"/abc/{param}/123",
145+
true,
146+
nil,
147+
},
148+
{
149+
"paths with different segments",
150+
"/abc/123/xyz",
151+
"/def/123/zzz",
152+
false,
153+
nil,
154+
},
155+
{
156+
"paths with different segments with parameter",
157+
"/abc/{param}/123",
158+
"/def/123/zzz",
159+
false,
160+
nil,
161+
},
162+
{
163+
"paths with different lengths and params",
164+
"/abc/{param}",
165+
"/abc/{param}/123",
166+
false,
167+
nil,
168+
},
169+
}
170+
171+
for i := range tests {
172+
test := tests[i]
173+
t.Run(test.name, func(t *testing.T) {
174+
match, err := HasMatchingPathSegmentsWithParams(test.pathA, test.pathB)
175+
176+
errortest.AssertError(t, err, test.err)
177+
assert.Equal(t, test.result, match)
178+
})
179+
}
180+
}
181+
22182
func TestUrl_JoinPaths(t *testing.T) {
23183
tests := []struct {
24184
name string
@@ -66,7 +226,7 @@ func TestUrl_JoinPaths(t *testing.T) {
66226

67227
for i := range tests {
68228
test := tests[i]
69-
t.Run(fmt.Sprintf("JoinPaths_%v", test.name), func(t *testing.T) {
229+
t.Run(test.name, func(t *testing.T) {
70230
joinedPaths, err := JoinPaths(test.paths...)
71231

72232
errortest.AssertError(t, err, test.error)
@@ -143,7 +303,7 @@ func TestUrl_JoinPathsWithSeparator(t *testing.T) {
143303

144304
for i := range tests {
145305
test := tests[i]
146-
t.Run(fmt.Sprintf("JoinPathsWithSeparator_%v", test.name), func(t *testing.T) {
306+
t.Run(test.name, func(t *testing.T) {
147307
joinedPaths, err := JoinPathsWithSeparator(test.separator, test.paths...)
148308

149309
errortest.AssertError(t, err, test.error)
@@ -193,7 +353,7 @@ func TestUrl_SplitPath(t *testing.T) {
193353

194354
for i := range tests {
195355
test := tests[i]
196-
t.Run(fmt.Sprintf("JoinPaths_%v", test.name), func(t *testing.T) {
356+
t.Run(test.name, func(t *testing.T) {
197357
segments := SplitPath(test.path)
198358

199359
if test.result != nil {
@@ -261,7 +421,7 @@ func TestUrl_SplitPathWithSeparator(t *testing.T) {
261421

262422
for i := range tests {
263423
test := tests[i]
264-
t.Run(fmt.Sprintf("JoinPaths_%v", test.name), func(t *testing.T) {
424+
t.Run(test.name, func(t *testing.T) {
265425
segments := SplitPathWithSeparator(test.path, test.separator)
266426

267427
if test.result != nil {

0 commit comments

Comments
 (0)