From 991b5836f61a45486505360a30ae3a240d244aa4 Mon Sep 17 00:00:00 2001 From: cheatfate Date: Tue, 13 Apr 2021 11:56:21 +0300 Subject: [PATCH] Add `redirect` macro to allow path redirection. Add tests for `redirect` macro. Add check for unique patterns in path and tests. --- presto/btrees.nim | 12 +++ presto/route.nim | 84 ++++++++++++++++--- presto/segpath.nim | 66 +++++++++++++++ tests/testroute.nim | 186 ++++++++++++++++++++++++++++++++++++++++++ tests/testsegpath.nim | 11 +++ 5 files changed, 348 insertions(+), 11 deletions(-) diff --git a/presto/btrees.nim b/presto/btrees.nim index e8eed4c..cc4c93e 100644 --- a/presto/btrees.nim +++ b/presto/btrees.nim @@ -45,6 +45,18 @@ proc getOrDefault*[Key, Val](b: BTree[Key, Val], key: Key): Val = for j in 0..= 2: if item[0] == '{' and item[^1] == '}': doAssert(len(item) > 2, "Patterns with empty names are not allowed") res.patterns = res.patterns or (1'u64 shl counter) + if item in patterns: + raiseAssert "Only unique patterns allowed in path" + else: + patterns.add(item) res.data.add(item) inc(counter) continue diff --git a/tests/testroute.nim b/tests/testroute.nim index a2d4d74..fe995f1 100644 --- a/tests/testroute.nim +++ b/tests/testroute.nim @@ -345,6 +345,22 @@ suite "REST API router & macro tests": router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback) router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback) + # Use HTTP method GET and redirect + router.addRedirect(MethodGet, "/redirect/path/1", "/unique/path/1") + router.addRedirect(MethodGet, "/redirect/path/2", "/unique/path/2") + router.addRedirect(MethodGet, "/redirect/path/{pattern1}", + "/unique/path/{pattern1}") + router.addRedirect(MethodGet, "/redirect/path/{pattern2}", + "/unique/path/{pattern2") + + # Use HTTP method POST and redirect + router.addRedirect(MethodPost, "/redirect/path/1", "/unique/path/1") + router.addRedirect(MethodPost, "/redirect/path/2", "/unique/path/2") + router.addRedirect(MethodPost, "/redirect/path/{pattern1}", + "/unique/path/{pattern1}") + router.addRedirect(MethodPost, "/redirect/path/{pattern2}", + "/unique/path/{pattern2") + expect AssertionError: router.addRoute(MethodGet, "/unique/path/1", apiCallback) expect AssertionError: @@ -354,6 +370,15 @@ suite "REST API router & macro tests": expect AssertionError: router.addRoute(MethodGet, "/unique/path/{pattern2}", apiCallback) + expect AssertionError: + router.addRoute(MethodGet, "/redirect/path/1", apiCallback) + expect AssertionError: + router.addRoute(MethodGet, "/redirect/path/2", apiCallback) + expect AssertionError: + router.addRoute(MethodGet, "/redirect/path/{pattern1}", apiCallback) + expect AssertionError: + router.addRoute(MethodGet, "/redirect/path/{pattern2}", apiCallback) + expect AssertionError: router.addRoute(MethodPost, "/unique/path/1", apiCallback) expect AssertionError: @@ -362,3 +387,164 @@ suite "REST API router & macro tests": router.addRoute(MethodPost, "/unique/path/{pattern1}", apiCallback) expect AssertionError: router.addRoute(MethodPost, "/unique/path/{pattern2}", apiCallback) + + expect AssertionError: + router.addRoute(MethodPost, "/redirect/path/1", apiCallback) + expect AssertionError: + router.addRoute(MethodPost, "/redirect/path/2", apiCallback) + expect AssertionError: + router.addRoute(MethodPost, "/redirect/path/{pattern1}", apiCallback) + expect AssertionError: + router.addRoute(MethodPost, "/redirect/path/{pattern2}", apiCallback) + + expect AssertionError: + router.addRedirect(MethodGet, "/unique/path/1", "/unique/1") + expect AssertionError: + router.addRedirect(MethodGet, "/unique/path/2", "/unique/2") + expect AssertionError: + router.addRedirect(MethodGet, "/unique/path/{pattern1}", + "/unique/{pattern1}") + expect AssertionError: + router.addRedirect(MethodGet, "/unique/path/{pattern2}", + "/unique/{pattern2}") + + expect AssertionError: + router.addRedirect(MethodGet, "/redirect/path/1", "/another/1") + expect AssertionError: + router.addRedirect(MethodGet, "/redirect/path/2", "/another/2") + expect AssertionError: + router.addRedirect(MethodGet, "/redirect/path/{pattern1}", + "/another/{pattern1}") + expect AssertionError: + router.addRedirect(MethodGet, "/redirect/path/{pattern2}", + "/another/{pattern2}") + + expect AssertionError: + router.addRedirect(MethodPost, "/unique/path/1", "/unique/1") + expect AssertionError: + router.addRedirect(MethodPost, "/unique/path/2", "/unique/2") + expect AssertionError: + router.addRedirect(MethodPost, "/unique/path/{pattern1}", + "/unique/{pattern1}") + expect AssertionError: + router.addRedirect(MethodPost, "/unique/path/{pattern2}", + "/unique/{pattern2}") + + expect AssertionError: + router.addRedirect(MethodPost, "/redirect/path/1", "/another/1") + expect AssertionError: + router.addRedirect(MethodPost, "/redirect/path/2", "/another/2") + expect AssertionError: + router.addRedirect(MethodPost, "/redirect/path/{pattern1}", + "/another/{pattern1}") + expect AssertionError: + router.addRedirect(MethodPost, "/redirect/path/{pattern2}", + "/another/{pattern2}") + + test "Redirection test": + var router = RestRouter.init(testValidate) + + router.api(MethodGet, "/test/empty/1") do ( + ) -> RestApiResponse: + return ok(ContentBody(contentType: "test/test", data: "ok-1".toBytes())) + router.api(MethodGet, "/test/empty/p1/p2/p3/1") do ( + ) -> RestApiResponse: + return ok(ContentBody(contentType: "test/test", data: "ok-2".toBytes())) + router.api(MethodGet, "/test/empty/p1/p2/p3/p5/p6/p7/p8/p9/1") do ( + ) -> RestApiResponse: + return ok(ContentBody(contentType: "test/test", data: "ok-3".toBytes())) + router.api(MethodGet, "/test/basic/path1/{smp1}") do ( + smp1: int) -> RestApiResponse: + let s1 = smp1.get() + if s1 == 999999: + return ok(ContentBody(contentType: "test/test", + data: "ok-1".toBytes())) + router.api(MethodGet, "/test/basic/path1/path2/{smp1}/{smp2}") do ( + smp1: int, smp2: string) -> RestApiResponse: + let s1 = smp1.get() + let s2 = smp2.get() + if (s1 == 999999) and (s2 == "string1"): + return ok(ContentBody(contentType: "test/test", + data: "ok-2".toBytes())) + router.api(MethodGet, + "/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") do ( + smp1: int, smp2: string, smp3: seq[byte]) -> RestApiResponse: + let s1 = smp1.get() + let s2 = smp2.get() + let s3 = smp3.get() + if (s1 == 999999) and (s2 == "string1") and + (bytesToString(s3) == "bytes1"): + return ok(ContentBody(contentType: "test/test", + data: "ok-3".toBytes())) + + router.redirect(MethodGet, "/api/1", "/test/empty/1") + router.redirect(MethodGet, "/api/2", "/test/empty/p1/p2/p3/1") + router.redirect(MethodGet, "/api/3", + "/test/empty/p1/p2/p3/p5/p6/p7/p8/p9/1") + router.redirect(MethodGet, "/api/basic/{smp1}", + "/test/basic/path1/{smp1}") + router.redirect(MethodGet, "/api/basic/{smp1}/{smp2}", + "/test/basic/path1/path2/{smp1}/{smp2}") + router.redirect(MethodGet, "/api/basic/{smp1}/{smp2}/{smp3}", + "/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") + # Patterns with mixed order + router.redirect(MethodGet, "/api/basic/{smp3}/{smp1}/{smp2}", + "/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") + router.redirect(MethodGet, "/api/basic/{smp2}/{smp3}/{smp1}", + "/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") + router.redirect(MethodGet, "/api/basic/{smp2}/{smp1}/{smp3}", + "/test/basic/path1/path2/path3/{smp1}/{smp2}/{smp3}") + router.redirect(MethodGet, "/api/basic/{smp2}/p1/p2/p3/{smp1}", + "/test/basic/path1/path2/{smp1}/{smp2}") + router.redirect(MethodGet, "/api/basic/{smp2}/p1/p2/p3/p4/p5/p6/{smp1}", + "/test/basic/path1/path2/{smp1}/{smp2}") + router.redirect(MethodGet, + "/api/basic/{smp2}/p1/p2/p3/p4/p5/p6/p7/{smp1}/p8", + "/test/basic/path1/path2/{smp1}/{smp2}") + + let r1 = router.sendMockRequest(MethodGet, "http://l.to/api/1") + let r2 = router.sendMockRequest(MethodGet, "http://l.to/api/2") + let r3 = router.sendMockRequest(MethodGet, "http://l.to/api/3") + let r4 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/999999") + let r5 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/999999/string1") + let r6 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/999999/string1/0x627974657331") + let r7 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/0x627974657331/999999/string1") + let r8 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/string1/0x627974657331/999999") + let r9 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/string1/999999/0x627974657331") + let r10 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/string1/p1/p2/p3/999999") + let r11 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/string1/p1/p2/p3/p4/p5/p6/999999") + let r12 = router.sendMockRequest(MethodGet, + "http://l.to/api/basic/string1/p1/p2/p3/p4/p5/p6/p7/999999/p8") + check: + r1.isOk() + r2.isOk() + r3.isOk() + r4.isOk() + r5.isOk() + r6.isOk() + r7.isOk() + r8.isOk() + r9.isOk() + r10.isOk() + r11.isOk() + r12.isOk() + bytesToString(r1.get().data) == "ok-1" + bytesToString(r2.get().data) == "ok-2" + bytesToString(r3.get().data) == "ok-3" + bytesToString(r4.get().data) == "ok-1" + bytesToString(r5.get().data) == "ok-2" + bytesToString(r6.get().data) == "ok-3" + bytesToString(r7.get().data) == "ok-3" + bytesToString(r8.get().data) == "ok-3" + bytesToString(r9.get().data) == "ok-3" + bytesToString(r10.get().data) == "ok-2" + bytesToString(r11.get().data) == "ok-2" + bytesToString(r12.get().data) == "ok-2" diff --git a/tests/testsegpath.nim b/tests/testsegpath.nim index 3f05496..fdb8a7f 100644 --- a/tests/testsegpath.nim +++ b/tests/testsegpath.nim @@ -53,3 +53,14 @@ suite "SegmentedPath test suite": check: len(path.data) == (i + 2) path.getPatterns() == names + + test "Non-unique patterns test": + const NonUniqueVectors = [ + "/{item1}/{item2}/{item1}", + "/{i1}/{i1}", + "/{a}/{b}/{a}" + ] + for item in NonUniqueVectors: + expect AssertionError: + let path {.used.} = SegmentedPath.init(HttpMethod.MethodGet, item, + validate)