1import { assert, it } from "@effect/vitest"
2import { FindMyWay as Router } from "effect/http"
3
4it("route with matching regex", () => {
5 const router = Router.make<boolean>()
6 router.on("GET", "/test/:id(^\\d+$)", true)
7 assert.deepStrictEqual(router.find("GET", "/test/12")?.handler, true)
8})
9
10it("route without matching regex", () => {
11 const router = Router.make<boolean>()
12 router.on("GET", "/test/:id(^\\d+$)", true)
13 assert.isUndefined(router.find("GET", "/test/test"))
14})
15
16it("route with an extension regex 2", () => {
17 const router = Router.make<number>()
18
19 router.on("GET", "/test/S/:file(^\\S+).png", 1)
20 router.on("GET", "/test/D/:file(^\\D+).png", 2)
21
22 assert.strictEqual(router.find("GET", "/test/S/foo.png")?.handler, 1)
23 assert.strictEqual(router.find("GET", "/test/D/foo.png")?.handler, 2)
24})
25
26it("nested route with matching regex", () => {
27 const router = Router.make<boolean>()
28 router.on("GET", "/test/:id(^\\d+$)/hello", true)
29 assert.deepStrictEqual(router.find("GET", "/test/12/hello")?.handler, true)
30})
31
32it("mixed nested route with matching regex", () => {
33 const router = Router.make<boolean>()
34 router.on("GET", "/test/:id(^\\d+$)/hello/:world", true)
35 assert.deepStrictEqual(router.find("GET", "/test/12/hello/world")?.params, {
36 id: "12",
37 world: "world"
38 })
39})
40
41it("mixed nested route with double matching regex", () => {
42 const router = Router.make<boolean>()
43 router.on("GET", "/test/:id(^\\d+$)/hello/:world(^\\d+$)", true)
44 assert.deepStrictEqual(router.find("GET", "/test/12/hello/15")?.params, {
45 id: "12",
46 world: "15"
47 })
48})
49
50it("mixed nested route without double matching regex", () => {
51 const router = Router.make<boolean>()
52 router.on("GET", "/test/:id(^\\d+$)/hello/:world(^\\d+$)", true)
53 assert.isUndefined(router.find("GET", "/test/12/hello/test"))
54})
55
56it("route with an extension regex", () => {
57 const router = Router.make<boolean>()
58 router.on("GET", "/test/:file(^\\d+).png", true)
59 assert.deepStrictEqual(router.find("GET", "/test/12.png")?.handler, true)
60})
61
62it("route with an extension regex - no match", () => {
63 const router = Router.make<boolean>()
64 router.on("GET", "/test/:file(^\\d+).png", true)
65 assert.isUndefined(router.find("GET", "/test/aa.png"))
66})
67
68it("safe decodeURIComponent", () => {
69 const router = Router.make<boolean>()
70 router.on("GET", "/test/:id(^\\d+$)", true)
71 assert.isUndefined(router.find("GET", "/test/hel%\"Flo"))
72})
73
74it("rejects truncated percent encodings", () => {
75 const router = Router.make<boolean>()
76 router.on("GET", "/test/:id", true)
77
78 assert.isUndefined(router.find("GET", "/test/a%"))
79 assert.isUndefined(router.find("GET", "/test/a%2"))
80})
81
82it("does not match an empty segment against a non-empty regex", () => {
83 const router = Router.make<boolean>({
84 ignoreTrailingSlash: false
85 })
86 router.on("GET", "/users/:userId(^\\d+)", true)
87
88 assert.isUndefined(router.find("GET", "/users/"))
89})
90
91it("matches undefined regex captures as empty strings", () => {
92 const router = Router.make<boolean>({
93 ignoreTrailingSlash: false
94 })
95 router.on("GET", "/test/:id(^((?!abc).)*$)", true)
96
97 assert.deepStrictEqual(router.find("GET", "/test/")?.params, {
98 id: ""
99 })
100})
101
102it("falls back when a parameter exceeds maxParamLength", () => {
103 const router = Router.make<string>({
104 maxParamLength: 3
105 })
106 router.on("GET", "/users/:userId", "parameter")
107 router.on("GET", "/users/*", "wildcard")
108
109 assert.strictEqual(router.find("GET", "/users/long")?.handler, "wildcard")
110})
111
112it("avoids backtracking across static parameter separators", { timeout: 1_000 }, () => {
113 const router = Router.make<boolean>()
114 router.on("GET", "/:foo-:bar-", true)
115
116 router.find("GET", "/" + "-".repeat(16_000) + "a")
117})
118
119it("avoids backtracking across mixed static parameter separators", { timeout: 1_000 }, () => {
120 const router = Router.make<boolean>()
121 router.on("GET", "/:foo-:bar-", true)
122
123 router.find("GET", "/" + "a-".repeat(8_000) + "b")
124})