1module BuildUrl.Spec where
5import Data.Maybe (Maybe(..))
6import Test.Spec (Spec, describe, it)
7import Test.Spec.Assertions (shouldEqual)
8import Type.Proxy (Proxy(..))
9import Unsafe.Coerce (unsafeCoerce)
10import Yoga.Fetch.Om.BuildUrl (buildUrl, substitutePathParams, appendQueryParams)
11import Yoga.HTTP.API.Path (Path, type (/), type (:))
13--------------------------------------------------------------------------------
14-- SubstitutePathParams Tests
15--------------------------------------------------------------------------------
19 describe "substitutePathParams" do
20 it "substitutes single path param" do
21 let result = substitutePathParams @(id :: Int) { id: 42 } "/users/:id"
22 result `shouldEqual` "/users/42"
24 it "substitutes multiple path params" do
26 result = substitutePathParams @(userId :: Int, postId :: Int)
27 { userId: 1, postId: 99 }
28 "/users/:userId/posts/:postId"
29 result `shouldEqual` "/users/1/posts/99"
31 it "handles String params" do
33 result = substitutePathParams @(slug :: String)
34 { slug: "hello-world" }
36 result `shouldEqual` "/posts/hello-world"
37 it "URL-encodes reserved characters in path params" do
39 result = substitutePathParams @(slug :: String)
42 result `shouldEqual` "/users/a%2Fb%20c%3Fd%23e"
44 it "distinguishes path param names with shared prefixes" do
46 result = substitutePathParams @(id :: Int, id2 :: Int)
49 result `shouldEqual` "/pairs/1/2"
51 it "handles empty params" do
52 let result = substitutePathParams @() {} "/users"
53 result `shouldEqual` "/users"
55 describe "appendQueryParams" do
56 it "appends single query param" do
57 let result = appendQueryParams @(limit :: Int) { limit: 10 } "/users"
58 result `shouldEqual` "/users?limit=10"
60 it "appends multiple query params" do
62 result = appendQueryParams @(limit :: Int, offset :: Int)
63 { limit: 10, offset: 20 }
65 result `shouldEqual` "/users?limit=10&offset=20"
67 it "handles optional params - provided via partial record" do
69 result = appendQueryParams @(limit :: Maybe Int)
70 (unsafeCoerce { limit: 10 } :: Record (limit :: Maybe Int))
72 result `shouldEqual` "/users?limit=10"
74 it "serializes a present explicit Maybe query param" do
75 let result = appendQueryParams @(id :: Maybe Int) { id: Just 42 } "/users"
76 result `shouldEqual` "/users?id=42"
78 it "omits an explicit Nothing query param" do
79 let result = appendQueryParams @(id :: Maybe Int) { id: Nothing } "/users"
80 result `shouldEqual` "/users"
82 it "handles optional params - omitted via partial record" do
84 result = appendQueryParams @(limit :: Maybe Int)
85 (unsafeCoerce {} :: Record (limit :: Maybe Int))
87 result `shouldEqual` "/users"
89 it "handles mixed optional and required params" do
91 result = appendQueryParams @(limit :: Maybe Int, offset :: Int)
92 (unsafeCoerce { offset: 20 } :: Record (limit :: Maybe Int, offset :: Int))
94 result `shouldEqual` "/users?offset=20"
96 it "handles empty query params" do
97 let result = appendQueryParams @() {} "/users"
98 result `shouldEqual` "/users"
100 it "URL-encodes special characters in values" do
101 let result = appendQueryParams @(search :: String) { search: "hello world&more=yes" } "/users"
102 result `shouldEqual` "/users?search=hello%20world%26more%3Dyes"
103 it "appends query params to an existing query string" do
104 let result = appendQueryParams @(search :: String) { search: "new value" } "/users?token=old"
105 result `shouldEqual` "/users?token=old&search=new%20value"
107 it "inserts query params before a URL fragment" do
108 let result = appendQueryParams @(search :: String) { search: "new value" } "/users#details"
109 result `shouldEqual` "/users?search=new%20value#details"
111 it "repeats Array query params one pair per element" do
113 result = appendQueryParams @(categories :: Array Int)
114 { categories: [ 3030, 103130 ] }
116 result `shouldEqual` "/users?categories=3030&categories=103130"
118 it "omits empty Array query params" do
120 result = appendQueryParams @(categories :: Array Int)
123 result `shouldEqual` "/users"
125 it "mixes Array and bare query params" do
127 result = appendQueryParams @(query :: String, categories :: Array Int)
128 { query: "foo", categories: [ 1, 2 ] }
130 result `shouldEqual` "/users?categories=1&categories=2&query=foo"
132 describe "buildUrl" do
133 it "builds URL with path params only" do
136 "https://api.example.com"
137 (Proxy :: _ (Path ("users" / "id" : Int)))
140 result `shouldEqual` "https://api.example.com/users/42"
142 it "builds URL with query params only" do
145 "https://api.example.com"
146 (Proxy :: _ (Path "users"))
148 { limit: 10, offset: 20 }
149 result `shouldEqual` "https://api.example.com/users?limit=10&offset=20"
151 it "appends route paths before base URL query strings and fragments" do
154 "https://api.example.com/base/?token=old#details"
155 (Proxy :: _ (Path "users"))
157 { search: "new value" }
158 result `shouldEqual` "https://api.example.com/base/users?token=old&search=new%20value#details"
160 it "does not substitute path captures inside a base URL query string" do
163 "https://api.example.com?redirect=/:id#details"
164 (Proxy :: _ (Path ("users" / "id" : Int)))
167 result `shouldEqual` "https://api.example.com/users/42?redirect=/:id#details"
keysesc to close
- j k
- move down, up
- enter
- open
- esc
- back, or up a level
- 1 2 3 4
- readme, files, commits, refs
- /
- filter
- y
- copy the clone command
- c
- compare the chosen commit with another (or shift-click both)
- ?
- show or hide this list