Skip to content

Commit 223df28

Browse files
authored
fix(tree): surface all conflicts on multi-method route registration (#99)
1 parent da7d2c1 commit 223df28

2 files changed

Lines changed: 32 additions & 19 deletions

File tree

fox_test.go

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1933,105 +1933,90 @@ func TestRouter_Add_Conflict(t *testing.T) {
19331933
name string
19341934
routes []string
19351935
insert string
1936-
wantErr error
19371936
wantMatch []string
19381937
}{
19391938
{
19401939
name: "static route already exist",
19411940
routes: []string{"/foo/bar", "/foo/baz"},
19421941
insert: "/foo/bar",
1943-
wantErr: ErrRouteNotFound,
19441942
wantMatch: []string{"/foo/bar"},
19451943
},
19461944
{
19471945
name: "route with same parameters",
19481946
routes: []string{"/foo/{foo}"},
19491947
insert: "/foo/{foo}",
1950-
wantErr: ErrRouteNotFound,
19511948
wantMatch: []string{"/foo/{foo}"},
19521949
},
19531950
{
19541951
name: "route with same wildcard",
19551952
routes: []string{"/foo/+{foo}"},
19561953
insert: "/foo/+{foo}",
1957-
wantErr: ErrRouteNotFound,
19581954
wantMatch: []string{"/foo/+{foo}"},
19591955
},
19601956
{
19611957
name: "route with same parameters but different name",
19621958
routes: []string{"/foo/{foo}"},
19631959
insert: "/foo/{bar}",
1964-
wantErr: ErrRouteNotFound,
19651960
wantMatch: []string{"/foo/{foo}"},
19661961
},
19671962
{
19681963
name: "route with same wildcard but different name",
19691964
routes: []string{"/foo/+{foo}"},
19701965
insert: "/foo/+{bar}",
1971-
wantErr: ErrRouteNotFound,
19721966
wantMatch: []string{"/foo/+{foo}"},
19731967
},
19741968
{
19751969
name: "route with middle same parameters but different name",
19761970
routes: []string{"/{foo}/bar"},
19771971
insert: "/{other}/bar",
1978-
wantErr: ErrRouteNotFound,
19791972
wantMatch: []string{"/{foo}/bar"},
19801973
},
19811974
{
19821975
name: "route with middle same wildcard but different name",
19831976
routes: []string{"/+{foo}/bar"},
19841977
insert: "/+{other}/bar",
1985-
wantErr: ErrRouteNotFound,
19861978
wantMatch: []string{"/+{foo}/bar"},
19871979
},
19881980
{
19891981
name: "route with same regexp parameter",
19901982
routes: []string{"/foo/{foo:[A-z]+}"},
19911983
insert: "/foo/{foo:[A-z]+}",
1992-
wantErr: ErrRouteNotFound,
19931984
wantMatch: []string{"/foo/{foo:[A-z]+}"},
19941985
},
19951986
{
19961987
name: "route with same regexp parameter but different name",
19971988
routes: []string{"/foo/{foo:[A-z]+}"},
19981989
insert: "/foo/{bar:[A-z]+}",
1999-
wantErr: ErrRouteNotFound,
20001990
wantMatch: []string{"/foo/{foo:[A-z]+}"},
20011991
},
20021992
{
20031993
name: "route with same regexp wildcard",
20041994
routes: []string{"/foo/+{foo:[A-z]+}"},
20051995
insert: "/foo/+{foo:[A-z]+}",
2006-
wantErr: ErrRouteNotFound,
20071996
wantMatch: []string{"/foo/+{foo:[A-z]+}"},
20081997
},
20091998
{
20101999
name: "route with same regexp wildcard but different name",
20112000
routes: []string{"/foo/+{foo:[A-z]+}"},
20122001
insert: "/foo/+{bar:[A-z]+}",
2013-
wantErr: ErrRouteNotFound,
20142002
wantMatch: []string{"/foo/+{foo:[A-z]+}"},
20152003
},
20162004
{
20172005
name: "route with middle same regexp parameter but different name",
20182006
routes: []string{"/{foo:[A-z]+}/bar"},
20192007
insert: "/{other:[A-z]+}/bar",
2020-
wantErr: ErrRouteNotFound,
20212008
wantMatch: []string{"/{foo:[A-z]+}/bar"},
20222009
},
20232010
{
20242011
name: "route with middle same regexp wildcard but different name",
20252012
routes: []string{"/+{foo:[A-z]+}/bar"},
20262013
insert: "/+{other:[A-z]+}/bar",
2027-
wantErr: ErrRouteNotFound,
20282014
wantMatch: []string{"/+{foo:[A-z]+}/bar"},
20292015
},
20302016
{
20312017
name: "simple hostname conflict",
20322018
routes: []string{"a.{b}.c/fox", "{a}.b.c/fox"},
20332019
insert: "a.{d}.c/fox",
2034-
wantErr: ErrRouteNotFound,
20352020
wantMatch: []string{"a.{b}.c/fox"},
20362021
},
20372022
}
@@ -2053,6 +2038,30 @@ func TestRouter_Add_Conflict(t *testing.T) {
20532038
}
20542039
}
20552040

2041+
func TestRouter_Add_Conflict_MultiMethod(t *testing.T) {
2042+
f := MustRouter()
2043+
f.MustAdd(MethodGet, "/hello/{name}", emptyHandler,
2044+
WithSchemeMatcher("https"),
2045+
WithQueryMatcher("foo", "bar"),
2046+
)
2047+
f.MustAdd(MethodPost, "/hello/{name}", emptyHandler,
2048+
WithSchemeMatcher("https"),
2049+
WithQueryMatcher("foo", "bar"),
2050+
)
2051+
2052+
got := onlyError(f.Add([]string{http.MethodGet, http.MethodPost}, "/hello/{name}", emptyHandler,
2053+
WithSchemeMatcher("https"),
2054+
WithQueryMatcher("foo", "bar"),
2055+
))
2056+
var conflict *RouteConflictError
2057+
require.ErrorAs(t, got, &conflict)
2058+
require.Len(t, conflict.Conflicts, 2)
2059+
gotMethods := iterutil.Map(slices.Values(conflict.Conflicts), func(r *Route) string {
2060+
return strings.Join(r.methods, ",")
2061+
})
2062+
assert.Equal(t, []string{http.MethodGet, http.MethodPost}, slices.Collect(gotMethods))
2063+
}
2064+
20562065
func TestRouter_Update_Conflict(t *testing.T) {
20572066
cases := []struct {
20582067
name string

tree.go

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -287,10 +287,14 @@ func (t *tXn) insertTokens(p, n *node, tokens []token, route *Route) (*node, err
287287
switch t.mode {
288288
case modeInsert:
289289
if n.isLeaf() {
290-
if idx := slices.IndexFunc(n.routes, func(r *Route) bool {
291-
return r.matchersEqual(route.matchers) && slicesutil.Overlap(r.methods, route.methods)
292-
}); idx >= 0 {
293-
return nil, &RouteConflictError{New: route, Conflicts: []*Route{n.routes[idx]}}
290+
var conflicts []*Route
291+
for _, r := range n.routes {
292+
if r.matchersEqual(route.matchers) && slicesutil.Overlap(r.methods, route.methods) {
293+
conflicts = append(conflicts, r)
294+
}
295+
}
296+
if len(conflicts) > 0 {
297+
return nil, &RouteConflictError{New: route, Conflicts: conflicts}
294298
}
295299
}
296300

0 commit comments

Comments
 (0)