From 2c52b96e04a6019f8872c43f1bda194b81e139f7 Mon Sep 17 00:00:00 2001 From: Wolfgang Walther Date: Fri, 9 Oct 2020 21:26:03 +0200 Subject: [PATCH] Refactor tests: replace str with json QuasiQuoter where appropriate --- test/Feature/AuthSpec.hs | 9 +-- test/Feature/DeleteSpec.hs | 11 ++- test/Feature/EmbedDisambiguationSpec.hs | 5 +- test/Feature/InsertSpec.hs | 59 +++++++------- test/Feature/QuerySpec.hs | 22 +++--- test/Feature/RpcSpec.hs | 28 +++---- test/Feature/SingularSpec.hs | 100 +++++++++++++----------- test/Feature/UpdateSpec.hs | 2 +- test/Feature/UpsertSpec.hs | 43 +++++----- 9 files changed, 141 insertions(+), 138 deletions(-) diff --git a/test/Feature/AuthSpec.hs b/test/Feature/AuthSpec.hs index dbf9b1b5b..765bf54ba 100644 --- a/test/Feature/AuthSpec.hs +++ b/test/Feature/AuthSpec.hs @@ -6,7 +6,6 @@ import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import PostgREST.Types (PgVersion, pgVersion112) import Protolude hiding (get) @@ -85,7 +84,7 @@ spec actualPgVersion = describe "authorization" $ do it "sql functions can read custom and standard claims variables" $ do let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmdW4iLCJqdGkiOiJmb28iLCJuYmYiOjEzMDA4MTkzODAsImV4cCI6OTk5OTk5OTk5OSwiaHR0cDovL3Bvc3RncmVzdC5jb20vZm9vIjp0cnVlLCJpc3MiOiJqb2UiLCJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWF0IjoxMzAwODE5MzgwfQ.V5fEpXfpb7feqwVqlcDleFdKu86bdwU2cBRT4fcMhXg" request methodPost "/rpc/reveal_big_jwt" [auth] "{}" - `shouldRespondWith` [str|[{"iss":"joe","sub":"fun","exp":9999999999,"nbf":1300819380,"iat":1300819380,"jti":"foo","http://postgrest.com/foo":true}]|] + `shouldRespondWith` [json|[{"iss":"joe","sub":"fun","exp":9999999999,"nbf":1300819380,"iat":1300819380,"jti":"foo","http://postgrest.com/foo":true}]|] it "allows users with permissions to see their tables" $ do let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.B-lReuGNDwAlU1GOC476MlO0vAt9JNoHIlxg2vwMaO0" @@ -148,7 +147,7 @@ spec actualPgVersion = describe "authorization" $ do let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6MX0.gKw7qI50i9hMrSJW8BlTpdMEVmMXJYxlAqueGqpa_mE" in request methodPost "/rpc/get_current_user" [auth] [json| {} |] - `shouldRespondWith` [str|"postgrest_test_author"|] + `shouldRespondWith` [json|"postgrest_test_author"|] { matchStatus = 200 , matchHeaders = [] } @@ -157,7 +156,7 @@ spec actualPgVersion = describe "authorization" $ do let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6Mn0.nwzjMI0YLvVGJQTeoCPEBsK983b__gxdpLXisBNaO2A" in request methodPost "/rpc/get_current_user" [auth] [json| {} |] - `shouldRespondWith` [str|"postgrest_test_default_role"|] + `shouldRespondWith` [json|"postgrest_test_default_role"|] { matchStatus = 200 , matchHeaders = [] } @@ -166,7 +165,7 @@ spec actualPgVersion = describe "authorization" $ do let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6M30.OGxEJAf60NKZiTn-tIb2jy4rqKs_ZruLGWZ40TjrJsM" in request methodPost "/rpc/get_current_user" [auth] [json| {} |] - `shouldRespondWith` [str|{"hint":"Please contact administrator","details":null,"code":"P0001","message":"Disabled ID --> 3"}|] + `shouldRespondWith` [json|{"hint":"Please contact administrator","details":null,"code":"P0001","message":"Disabled ID --> 3"}|] { matchStatus = 400 , matchHeaders = [] } diff --git a/test/Feature/DeleteSpec.hs b/test/Feature/DeleteSpec.hs index 2de588e74..db4b0993f 100644 --- a/test/Feature/DeleteSpec.hs +++ b/test/Feature/DeleteSpec.hs @@ -6,7 +6,6 @@ import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import Protolude hiding (get) @@ -23,7 +22,7 @@ spec = it "returns the deleted item and count if requested" $ request methodDelete "/items?id=eq.2" [("Prefer", "return=representation"), ("Prefer", "count=exact")] "" - `shouldRespondWith` [str|[{"id":2}]|] + `shouldRespondWith` [json|[{"id":2}]|] { matchStatus = 200 , matchHeaders = ["Content-Range" <:> "*/1"] } @@ -42,18 +41,18 @@ spec = it "returns the deleted item and shapes the response" $ request methodDelete "/complex_items?id=eq.2&select=id,name" [("Prefer", "return=representation")] "" - `shouldRespondWith` [str|[{"id":2,"name":"Two"}]|] + `shouldRespondWith` [json|[{"id":2,"name":"Two"}]|] { matchStatus = 200 , matchHeaders = ["Content-Range" <:> "*/*"] } it "can rename and cast the selected columns" $ request methodDelete "/complex_items?id=eq.3&select=ciId:id::text,ciName:name" [("Prefer", "return=representation")] "" - `shouldRespondWith` [str|[{"ciId":"3","ciName":"Three"}]|] + `shouldRespondWith` [json|[{"ciId":"3","ciName":"Three"}]|] it "can embed (parent) entities" $ request methodDelete "/tasks?id=eq.8&select=id,name,project:projects(id)" [("Prefer", "return=representation")] "" - `shouldRespondWith` [str|[{"id":8,"name":"Code OSX","project":{"id":4}}]|] + `shouldRespondWith` [json|[{"id":8,"name":"Code OSX","project":{"id":4}}]|] { matchStatus = 200 , matchHeaders = ["Content-Range" <:> "*/*"] } @@ -61,7 +60,7 @@ spec = it "actually clears items ouf the db" $ do _ <- request methodDelete "/items?id=lt.15" [] "" get "/items" - `shouldRespondWith` [str|[{"id":15}]|] + `shouldRespondWith` [json|[{"id":15}]|] { matchStatus = 200 , matchHeaders = ["Content-Range" <:> "0-0/*"] } diff --git a/test/Feature/EmbedDisambiguationSpec.hs b/test/Feature/EmbedDisambiguationSpec.hs index 8f26e8988..bb6e2330f 100644 --- a/test/Feature/EmbedDisambiguationSpec.hs +++ b/test/Feature/EmbedDisambiguationSpec.hs @@ -5,7 +5,6 @@ import Network.Wai (Application) import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import Protolude hiding (get) import SpecHelper @@ -154,7 +153,7 @@ spec = it "can embed parent with view!fk and grandparent by using fk" $ get "/tasks?id=eq.1&select=id,name,projects_view!project(id,name,client(id,name))" `shouldRespondWith` - [str|[{"id":1,"name":"Design w7","projects_view":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|] + [json|[{"id":1,"name":"Design w7","projects_view":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|] it "can embed by using a composite FK name" $ get "/unit_workdays?select=unit_id,day,fst_shift(car_id,schedule(name)),snd_shift(camera_id,schedule(name))" `shouldRespondWith` @@ -230,7 +229,7 @@ spec = it "can embed parent by using view!column and grandparent by using the column" $ get "/tasks?id=eq.1&select=id,name,project:projects_view!project_id(id,name,client:client_id(id,name))" `shouldRespondWith` - [str|[{"id":1,"name":"Design w7","project":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|] + [json|[{"id":1,"name":"Design w7","project":{"id":1,"name":"Windows 7","client":{"id":1,"name":"Microsoft"}}}]|] it "can specify table!column" $ get "/message?select=id,body,sender:person!sender(name),recipient:person!recipient(name)&id=lt.4" `shouldRespondWith` diff --git a/test/Feature/InsertSpec.hs b/test/Feature/InsertSpec.hs index f42f1a0da..d1b3daa40 100644 --- a/test/Feature/InsertSpec.hs +++ b/test/Feature/InsertSpec.hs @@ -42,7 +42,7 @@ spec actualPgVersion = do "integer": 14, "double": 3.14159, "varchar": "testing!" , "boolean": false, "date": "1900-01-01", "money": "$3.99" , "enum": "foo" - }] |] `shouldRespondWith` [str|[{"integer":14,"varchar":"testing!"}]|] + }] |] `shouldRespondWith` [json|[{"integer":14,"varchar":"testing!"}]|] { matchStatus = 201 , matchHeaders = [matchContentTypeJson] } @@ -86,7 +86,7 @@ spec actualPgVersion = do it "includes related data after insert" $ request methodPost "/projects?select=id,name,clients(id,name)" [("Prefer", "return=representation"), ("Prefer", "count=exact")] - [str|{"id":6,"name":"New Project","client_id":2}|] `shouldRespondWith` [str|[{"id":6,"name":"New Project","clients":{"id":2,"name":"Apple"}}]|] + [json|{"id":6,"name":"New Project","client_id":2}|] `shouldRespondWith` [json|[{"id":6,"name":"New Project","clients":{"id":2,"name":"Apple"}}]|] { matchStatus = 201 , matchHeaders = [ matchContentTypeJson , "Location" <:> "/projects?id=eq.6" @@ -96,8 +96,8 @@ spec actualPgVersion = do it "can rename and cast the selected columns" $ request methodPost "/projects?select=pId:id::text,pName:name,cId:client_id::text" [("Prefer", "return=representation")] - [str|{"id":7,"name":"New Project","client_id":2}|] `shouldRespondWith` - [str|[{"pId":"7","pName":"New Project","cId":"2"}]|] + [json|{"id":7,"name":"New Project","client_id":2}|] `shouldRespondWith` + [json|[{"pId":"7","pName":"New Project","cId":"2"}]|] { matchStatus = 201 , matchHeaders = [ matchContentTypeJson , "Location" <:> "/projects?id=eq.7" @@ -106,8 +106,8 @@ spec actualPgVersion = do it "should not throw and return location header when selecting without PK" $ request methodPost "/projects?select=name,client_id" [("Prefer", "return=representation")] - [str|{"id":10,"name":"New Project","client_id":2}|] `shouldRespondWith` - [str|[{"name":"New Project","client_id":2}]|] + [json|{"id":10,"name":"New Project","client_id":2}|] `shouldRespondWith` + [json|[{"name":"New Project","client_id":2}]|] { matchStatus = 201 , matchHeaders = [ matchContentTypeJson , "Location" <:> "/projects?id=eq.10" @@ -117,7 +117,7 @@ spec actualPgVersion = do context "requesting no representation" $ it "should not throw and return location header when selecting without PK" $ request methodPost "/projects?select=name,client_id" [] - [str|{"id":11,"name":"New Project","client_id":2}|] `shouldRespondWith` "" + [json|{"id":11,"name":"New Project","client_id":2}|] `shouldRespondWith` "" { matchStatus = 201 , matchHeaders = [ "Location" <:> "/projects?id=eq.11" , "Content-Range" <:> "*/*" ] @@ -263,7 +263,7 @@ spec actualPgVersion = do request methodPost "/json_table" [("Prefer", "return=representation")] inserted - `shouldRespondWith` [str|[{"data":{"foo":"bar"}}]|] + `shouldRespondWith` [json|[{"data":{"foo":"bar"}}]|] { matchStatus = 201 } @@ -272,7 +272,7 @@ spec actualPgVersion = do request methodPost "/json_table" [("Prefer", "return=representation")] inserted - `shouldRespondWith` [str|[{"data":[1,2,3]}]|] + `shouldRespondWith` [json|[{"data":[1,2,3]}]|] { matchStatus = 201 } @@ -423,24 +423,23 @@ spec actualPgVersion = do describe "Row level permission" $ it "set user_id when inserting rows" $ do - let auth = authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.B-lReuGNDwAlU1GOC476MlO0vAt9JNoHIlxg2vwMaO0" - _ <- post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] - _ <- post "/postgrest/users" [json| { "id":"jroe", "pass": "1234", "role": "postgrest_test_author" } |] + post "/postgrest/users" [json| { "id":"jdoe", "pass": "1234", "role": "postgrest_test_author" } |] + post "/postgrest/users" [json| { "id":"jroe", "pass": "1234", "role": "postgrest_test_author" } |] - p1 <- request methodPost "/authors_only" - [ auth, ("Prefer", "return=representation") ] - [json| { "secret": "nyancat" } |] - liftIO $ do - simpleBody p1 `shouldBe` [str|[{"owner":"jdoe","secret":"nyancat"}]|] - simpleStatus p1 `shouldBe` created201 + request methodPost "/authors_only" + [ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqZG9lIn0.B-lReuGNDwAlU1GOC476MlO0vAt9JNoHIlxg2vwMaO0", ("Prefer", "return=representation") ] + [json| { "secret": "nyancat" } |] + `shouldRespondWith` + [json|[{"owner":"jdoe","secret":"nyancat"}]|] + { matchStatus = 201 } - p2 <- request methodPost "/authors_only" - -- jwt token for jroe - [ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqcm9lIn0.2e7mx0U4uDcInlbJVOBGlrRufwqWLINDIEDC1vS0nw8", ("Prefer", "return=representation") ] - [json| { "secret": "lolcat", "owner": "hacker" } |] - liftIO $ do - simpleBody p2 `shouldBe` [str|[{"owner":"jroe","secret":"lolcat"}]|] - simpleStatus p2 `shouldBe` created201 + request methodPost "/authors_only" + -- jwt token for jroe + [ authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIiwiaWQiOiJqcm9lIn0.2e7mx0U4uDcInlbJVOBGlrRufwqWLINDIEDC1vS0nw8", ("Prefer", "return=representation") ] + [json| { "secret": "lolcat", "owner": "hacker" } |] + `shouldRespondWith` + [json|[{"owner":"jroe","secret":"lolcat"}]|] + { matchStatus = 201 } context "tables with self reference foreign keys" $ do it "embeds parent after insert" $ @@ -456,7 +455,7 @@ spec actualPgVersion = do context "table with limited privileges" $ do it "succeeds inserting if correct select is applied" $ request methodPost "/limited_article_stars?select=article_id,user_id" [("Prefer", "return=representation")] - [json| {"article_id": 2, "user_id": 1} |] `shouldRespondWith` [str|[{"article_id":2,"user_id":1}]|] + [json| {"article_id": 2, "user_id": 1} |] `shouldRespondWith` [json|[{"article_id":2,"user_id":1}]|] { matchStatus = 201 , matchHeaders = [] } @@ -465,9 +464,9 @@ spec actualPgVersion = do request methodPost "/limited_article_stars?select=article_id,user_id,created_at" [("Prefer", "return=representation")] [json| {"article_id": 2, "user_id": 2} |] `shouldRespondWith` ( if actualPgVersion >= pgVersion112 then - [str|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] else - [str|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] ) { matchStatus = 401 , matchHeaders = [] @@ -477,9 +476,9 @@ spec actualPgVersion = do request methodPost "/limited_article_stars" [("Prefer", "return=representation")] [json| {"article_id": 3, "user_id": 1} |] `shouldRespondWith` ( if actualPgVersion >= pgVersion112 then - [str|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for view limited_article_stars"}|] else - [str|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] + [json|{"hint":null,"details":null,"code":"42501","message":"permission denied for relation limited_article_stars"}|] ) { matchStatus = 401 , matchHeaders = [] diff --git a/test/Feature/QuerySpec.hs b/test/Feature/QuerySpec.hs index 9a4c67dde..f0ae64532 100644 --- a/test/Feature/QuerySpec.hs +++ b/test/Feature/QuerySpec.hs @@ -8,8 +8,6 @@ import Test.Hspec hiding (pendingWith) import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc - import PostgREST.Types (PgVersion, pgVersion112, pgVersion121, pgVersion96) import Protolude hiding (get) @@ -72,19 +70,19 @@ spec actualPgVersion = do [json| [{"a": null, "b": null}] |] { matchHeaders = [matchContentTypeJson] } - get "/nullable_integer?a=is.null" `shouldRespondWith` [str|[{"a":null}]|] + get "/nullable_integer?a=is.null" `shouldRespondWith` [json|[{"a":null}]|] it "matches with like" $ do get "/simple_pk?k=like.*yx" `shouldRespondWith` - [str|[{"k":"xyyx","extra":"u"}]|] + [json|[{"k":"xyyx","extra":"u"}]|] get "/simple_pk?k=like.xy*" `shouldRespondWith` - [str|[{"k":"xyyx","extra":"u"}]|] + [json|[{"k":"xyyx","extra":"u"}]|] get "/simple_pk?k=like.*YY*" `shouldRespondWith` - [str|[{"k":"xYYx","extra":"v"}]|] + [json|[{"k":"xYYx","extra":"v"}]|] it "matches with like using not operator" $ get "/simple_pk?k=not.like.*yx" `shouldRespondWith` - [str|[{"k":"xYYx","extra":"v"}]|] + [json|[{"k":"xYYx","extra":"v"}]|] it "matches with ilike" $ do get "/simple_pk?k=ilike.xy*&order=extra.asc" `shouldRespondWith` @@ -251,11 +249,11 @@ spec actualPgVersion = do describe "Shaping response with select parameter" $ do it "selectStar works in absense of parameter" $ get "/complex_items?id=eq.3" `shouldRespondWith` - [str|[{"id":3,"name":"Three","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":[1,2,3],"field-with_sep":1}]|] + [json|[{"id":3,"name":"Three","settings":{"foo":{"int":1,"bar":"baz"}},"arr_data":[1,2,3],"field-with_sep":1}]|] it "dash `-` in column names is accepted" $ get "/complex_items?id=eq.3&select=id,field-with_sep" `shouldRespondWith` - [str|[{"id":3,"field-with_sep":1}]|] + [json|[{"id":3,"field-with_sep":1}]|] it "one simple column" $ get "/complex_items?select=id" `shouldRespondWith` @@ -320,7 +318,7 @@ spec actualPgVersion = do it "requesting parents and filtering parent columns" $ get "/projects?id=eq.1&select=id, name, clients(id)" `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7","clients":{"id":1}}]|] + [json|[{"id":1,"name":"Windows 7","clients":{"id":1}}]|] it "rows with missing parents are included" $ get "/projects?id=in.(1,5)&select=id,clients(id)" `shouldRespondWith` @@ -329,7 +327,7 @@ spec actualPgVersion = do it "rows with no children return [] instead of null" $ get "/projects?id=in.(5)&select=id,tasks(id)" `shouldRespondWith` - [str|[{"id":5,"tasks":[]}]|] + [json|[{"id":5,"tasks":[]}]|] it "requesting children 2 levels" $ get "/clients?id=eq.1&select=id,projects(id,tasks(id))" `shouldRespondWith` @@ -660,7 +658,7 @@ spec actualPgVersion = do it "ordering embeded parents does not break things" $ get "/projects?id=eq.1&select=id, name, clients(id, name)&clients.order=name.asc" `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"}}]|] + [json|[{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"}}]|] context "order syntax errors" $ do it "gives meaningful error messages when asc/desc/nulls{first,last} are misspelled" $ do diff --git a/test/Feature/RpcSpec.hs b/test/Feature/RpcSpec.hs index 8fd581ef4..1b4ce0a32 100644 --- a/test/Feature/RpcSpec.hs +++ b/test/Feature/RpcSpec.hs @@ -123,9 +123,9 @@ spec actualPgVersion = context "shaping the response returned by a proc" $ do it "returns a project" $ do post "/rpc/getproject" [json| { "id": 1} |] `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7","client_id":1}]|] + [json|[{"id":1,"name":"Windows 7","client_id":1}]|] get "/rpc/getproject?id=1" `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7","client_id":1}]|] + [json|[{"id":1,"name":"Windows 7","client_id":1}]|] it "can filter proc results" $ do post "/rpc/getallprojects?id=gt.1&id=lt.5&select=id" [json| {} |] `shouldRespondWith` @@ -147,9 +147,9 @@ spec actualPgVersion = it "select works on the first level" $ do post "/rpc/getproject?select=id,name" [json| { "id": 1} |] `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7"}]|] + [json|[{"id":1,"name":"Windows 7"}]|] get "/rpc/getproject?id=1&select=id,name" `shouldRespondWith` - [str|[{"id":1,"name":"Windows 7"}]|] + [json|[{"id":1,"name":"Windows 7"}]|] context "foreign entities embedding" $ do it "can embed if related tables are in the exposed schema" $ do @@ -624,7 +624,7 @@ spec actualPgVersion = get "/rpc/overloaded?a=1&b=2" `shouldRespondWith` [str|3|] it "overloaded(text, text, text)" $ - get "/rpc/overloaded?a=1&b=2&c=3" `shouldRespondWith` [str|"123"|] + get "/rpc/overloaded?a=1&b=2&c=3" `shouldRespondWith` [json|"123"|] context "only for POST rpc" $ do it "gives a parse filter error if GET style proc args are specified" $ @@ -688,7 +688,7 @@ spec actualPgVersion = [("Custom-Header", "test")] [json| { "name": "request.header.custom-header" } |] `shouldRespondWith` - [str|"test"|] + [json|"test"|] { matchStatus = 200 , matchHeaders = [ matchContentTypeJson ] } @@ -697,7 +697,7 @@ spec actualPgVersion = [("Origin", "http://example.com")] [json| { "name": "request.header.origin" } |] `shouldRespondWith` - [str|"http://example.com"|] + [json|"http://example.com"|] { matchStatus = 200 , matchHeaders = [ matchContentTypeJson ] } @@ -705,7 +705,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [] [json| { "name": "request.jwt.claim.role" } |] `shouldRespondWith` - [str|"postgrest_test_anonymous"|] + [json|"postgrest_test_anonymous"|] { matchStatus = 200 , matchHeaders = [ matchContentTypeJson ] } @@ -713,7 +713,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue")] [json| {"name":"request.cookie.acookie"} |] `shouldRespondWith` - [str|"cookievalue"|] + [json|"cookievalue"|] { matchStatus = 200 , matchHeaders = [] } @@ -721,7 +721,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [("Cookie","acookie=cookievalue;secondcookie=anothervalue")] [json| {"name":"request.cookie.secondcookie"} |] `shouldRespondWith` - [str|"anothervalue"|] + [json|"anothervalue"|] { matchStatus = 200 , matchHeaders = [] } @@ -729,7 +729,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [] [json| { "name": "app.settings.app_host" } |] `shouldRespondWith` - [str|"localhost"|] + [json|"localhost"|] { matchStatus = 200 , matchHeaders = [ matchContentTypeJson ] } @@ -737,7 +737,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [authHeaderJWT "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"] [json| {"name":"request.header.authorization"} |] `shouldRespondWith` - [str|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|] + [json|"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoicG9zdGdyZXN0X3Rlc3RfYXV0aG9yIn0.Xod-F15qsGL0WhdOCr2j3DdKuTw9QJERVgoFD3vGaWA"|] { matchStatus = 200 , matchHeaders = [] } @@ -745,7 +745,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [] [json| {"name":"request.method"} |] `shouldRespondWith` - [str|"POST"|] + [json|"POST"|] { matchStatus = 200 , matchHeaders = [] } @@ -753,7 +753,7 @@ spec actualPgVersion = request methodPost "/rpc/get_guc_value" [] [json| {"name":"request.path"} |] `shouldRespondWith` - [str|"/rpc/get_guc_value"|] + [json|"/rpc/get_guc_value"|] { matchStatus = 200 , matchHeaders = [] } diff --git a/test/Feature/SingularSpec.hs b/test/Feature/SingularSpec.hs index 3e67567d4..0edd7e542 100644 --- a/test/Feature/SingularSpec.hs +++ b/test/Feature/SingularSpec.hs @@ -7,7 +7,6 @@ import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import Protolude hiding (get) import SpecHelper @@ -26,31 +25,37 @@ spec = it "will select an existing object" $ do request methodGet "/items?id=eq.5" [singular] "" - `shouldRespondWith` [str|{"id":5}|] + `shouldRespondWith` + [json|{"id":5}|] + { matchHeaders = [matchContentTypeSingular] } -- also test without the +json suffix request methodGet "/items?id=eq.5" - [("Accept", "application/vnd.pgrst.object")] "" - `shouldRespondWith` [str|{"id":5}|] + [("Accept", "application/vnd.pgrst.object")] "" + `shouldRespondWith` + [json|{"id":5}|] + { matchHeaders = [matchContentTypeSingular] } it "can combine multiple prefer values" $ request methodGet "/items?id=eq.5" [singular, ("Prefer","count=none")] "" - `shouldRespondWith` [str|{"id":5}|] + `shouldRespondWith` + [json|{"id":5}|] + { matchHeaders = [matchContentTypeSingular] } it "can shape plurality singular object routes" $ request methodGet "/projects_view?id=eq.1&select=id,name,clients(*),tasks(id,name)" [singular] "" `shouldRespondWith` [json|{"id":1,"name":"Windows 7","clients":{"id":1,"name":"Microsoft"},"tasks":[{"id":1,"name":"Design w7"},{"id":2,"name":"Code w7"}]}|] - { matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] } + { matchHeaders = [matchContentTypeSingular] } context "when updating rows" $ do it "works for one row with return=rep" $ do - _ <- post "/addresses" [json| { id: 97, address: "A Street" } |] - request methodPatch - "/addresses?id=eq.97" - [("Prefer", "return=representation"), singular] - [json| { address: "B Street" } |] + post "/addresses" [json| { id: 97, address: "A Street" } |] + request methodPatch "/addresses?id=eq.97" + [("Prefer", "return=representation"), singular] + [json| { address: "B Street" } |] `shouldRespondWith` - [str|{"id":97,"address":"B Street"}|] + [json|{"id":97,"address":"B Street"}|] + { matchHeaders = [matchContentTypeSingular] } it "works for one row with return=minimal" $ request methodPatch @@ -72,7 +77,7 @@ spec = isErrorFormat (simpleBody p) `shouldBe` True -- the rows should not be updated, either - get "/addresses?id=eq.98" `shouldRespondWith` [str|[{"id":98,"address":"xxx"}]|] + get "/addresses?id=eq.98" `shouldRespondWith` [json|[{"id":98,"address":"xxx"}]|] it "raises an error for multiple rows with return=rep" $ do _ <- post "/addresses" [json| { id: 100, address: "xxx" } |] @@ -85,33 +90,36 @@ spec = isErrorFormat (simpleBody p) `shouldBe` True -- the rows should not be updated, either - get "/addresses?id=eq.100" `shouldRespondWith` [str|[{"id":100,"address":"xxx"}]|] + get "/addresses?id=eq.100" `shouldRespondWith` [json|[{"id":100,"address":"xxx"}]|] it "raises an error for zero rows" $ request methodPatch "/items?id=gt.0&id=lt.0" [singular] [json|{"id":1}|] `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } it "raises an error for zero rows with return=rep" $ request methodPatch "/items?id=gt.0&id=lt.0" [("Prefer", "return=representation"), singular] [json|{"id":1}|] `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } context "when creating rows" $ do it "works for one row with return=rep" $ do - p <- request methodPost - "/addresses" - [("Prefer", "return=representation"), singular] - [json| [ { id: 102, address: "xxx" } ] |] - liftIO $ simpleBody p `shouldBe` [str|{"id":102,"address":"xxx"}|] + request methodPost "/addresses" + [("Prefer", "return=representation"), singular] + [json| [ { id: 102, address: "xxx" } ] |] + `shouldRespondWith` + [json|{"id":102,"address":"xxx"}|] + { matchStatus = 201 + , matchHeaders = [matchContentTypeSingular] + } it "works for one row with return=minimal" $ do request methodPost "/addresses" @@ -123,7 +131,7 @@ spec = } -- and the element should exist get "/addresses?id=eq.103" - `shouldRespondWith` [str|[{"id":103,"address":"xxx"}]|] + `shouldRespondWith` [json|[{"id":103,"address":"xxx"}]|] { matchStatus = 200 , matchHeaders = [] } @@ -153,9 +161,9 @@ spec = [("Prefer", "return=minimal"), singular] [json| [ { id: 204, address: "xxx" }, { id: 205, address: "yyy" } ] |] `shouldRespondWith` - [str|{"details":"Results contain 2 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 2 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } -- the rows should not exist, either @@ -166,9 +174,9 @@ spec = [singular] [json| [ ] |] `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } it "raises an error when creating zero entities with return=rep" $ @@ -176,9 +184,9 @@ spec = [("Prefer", "return=representation"), singular] [json| [ ] |] `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } context "when deleting rows" $ do @@ -186,7 +194,7 @@ spec = p <- request methodDelete "/items?id=eq.11" [("Prefer", "return=representation"), singular] "" - liftIO $ simpleBody p `shouldBe` [str|{"id":11}|] + liftIO $ simpleBody p `shouldBe` [json|{"id":11}|] it "works for one row with return=minimal" $ do p <- request methodDelete @@ -222,18 +230,18 @@ spec = request methodDelete "/items?id=lt.0" [singular] "" `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } it "raises an error when deleting zero entities with return=rep" $ request methodDelete "/items?id=lt.0" [("Prefer", "return=representation"), singular] "" `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } context "when calling a stored proc" $ do @@ -241,9 +249,9 @@ spec = request methodPost "/rpc/getproject" [singular] [json|{ "id": 9999999}|] `shouldRespondWith` - [str|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 0 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } -- this one may be controversial, should vnd.pgrst.object include @@ -255,30 +263,32 @@ spec = it "returns a single object for json proc" $ request methodPost "/rpc/getproject" - [singular] [json|{ "id": 1}|] `shouldRespondWith` - [str|{"id":1,"name":"Windows 7","client_id":1}|] + [singular] [json|{ "id": 1}|] + `shouldRespondWith` + [json|{"id":1,"name":"Windows 7","client_id":1}|] + { matchHeaders = [matchContentTypeSingular] } it "fails for multiple rows" $ request methodPost "/rpc/getallprojects" [singular] "{}" `shouldRespondWith` - [str|{"details":"Results contain 5 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 5 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } it "executes the proc exactly once per request" $ do request methodPost "/rpc/getproject?select=id,name" [] [json| {"id": 1} |] - `shouldRespondWith` [str|[{"id":1,"name":"Windows 7"}]|] + `shouldRespondWith` [json|[{"id":1,"name":"Windows 7"}]|] request methodPost "/rpc/setprojects" [singular] [json| {"id_l": 1, "id_h": 2, "name": "changed"} |] `shouldRespondWith` - [str|{"details":"Results contain 2 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] + [json|{"details":"Results contain 2 rows, application/vnd.pgrst.object+json requires 1 row","message":"JSON object requested, multiple (or no) rows returned"}|] { matchStatus = 406 - , matchHeaders = ["Content-Type" <:> "application/vnd.pgrst.object+json; charset=utf-8"] + , matchHeaders = [matchContentTypeSingular] } -- should not actually have executed the function request methodPost "/rpc/getproject?select=id,name" [] [json| {"id": 1} |] - `shouldRespondWith` [str|[{"id":1,"name":"Windows 7"}]|] + `shouldRespondWith` [json|[{"id":1,"name":"Windows 7"}]|] diff --git a/test/Feature/UpdateSpec.hs b/test/Feature/UpdateSpec.hs index c1044e494..0d09f097c 100644 --- a/test/Feature/UpdateSpec.hs +++ b/test/Feature/UpdateSpec.hs @@ -85,7 +85,7 @@ spec = do it "returns updated object as array when return=rep" $ request methodPatch "/items?id=eq.2" [("Prefer", "return=representation")] [json| { "id":2 } |] - `shouldRespondWith` [str|[{"id":2}]|] + `shouldRespondWith` [json|[{"id":2}]|] { matchStatus = 200, matchHeaders = ["Content-Range" <:> "0-0/*"] } diff --git a/test/Feature/UpsertSpec.hs b/test/Feature/UpsertSpec.hs index 7f7c3a8c5..a77129398 100644 --- a/test/Feature/UpsertSpec.hs +++ b/test/Feature/UpsertSpec.hs @@ -6,7 +6,6 @@ import Network.HTTP.Types import Test.Hspec import Test.Hspec.Wai import Test.Hspec.Wai.JSON -import Text.Heredoc import Protolude hiding (get, put) import SpecHelper @@ -161,73 +160,73 @@ spec = context "Restrictions" $ do it "fails if Range is specified" $ request methodPut "/tiobe_pls?name=eq.Javascript" [("Range", "0-5")] - [str| [ { "name": "Javascript", "rank": 1 } ]|] + [json| [ { "name": "Javascript", "rank": 1 } ]|] `shouldRespondWith` [json|{"message":"Range header and limit/offset querystring parameters are not allowed for PUT"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } it "fails if limit is specified" $ put "/tiobe_pls?name=eq.Javascript&limit=1" - [str| [ { "name": "Javascript", "rank": 1 } ]|] + [json| [ { "name": "Javascript", "rank": 1 } ]|] `shouldRespondWith` [json|{"message":"Range header and limit/offset querystring parameters are not allowed for PUT"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } it "fails if offset is specified" $ put "/tiobe_pls?name=eq.Javascript&offset=1" - [str| [ { "name": "Javascript", "rank": 1 } ]|] + [json| [ { "name": "Javascript", "rank": 1 } ]|] `shouldRespondWith` [json|{"message":"Range header and limit/offset querystring parameters are not allowed for PUT"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } it "rejects every other filter than pk cols eq's" $ do put "/tiobe_pls?rank=eq.19" - [str| [ { "name": "Go", "rank": 19 } ]|] + [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } put "/tiobe_pls?id=not.eq.Java" - [str| [ { "name": "Go", "rank": 19 } ]|] + [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } put "/tiobe_pls?id=in.(Go)" - [str| [ { "name": "Go", "rank": 19 } ]|] + [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } put "/tiobe_pls?and=(id.eq.Go)" - [str| [ { "name": "Go", "rank": 19 } ]|] + [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } it "fails if not all composite key cols are specified as eq filters" $ do put "/employees?first_name=eq.Susan" - [str| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] + [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } put "/employees?last_name=eq.Heidt" - [str| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] + [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } it "fails if the uri primary key doesn't match the payload primary key" $ do - put "/tiobe_pls?name=eq.MATLAB" [str| [ { "name": "Perl", "rank": 17 } ]|] + put "/tiobe_pls?name=eq.MATLAB" [json| [ { "name": "Perl", "rank": 17 } ]|] `shouldRespondWith` [json|{"message":"Payload values do not match URL in primary key column(s)"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } put "/employees?first_name=eq.Wendy&last_name=eq.Anderson" - [str| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] + [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] `shouldRespondWith` [json|{"message":"Payload values do not match URL in primary key column(s)"}|] { matchStatus = 400 , matchHeaders = [matchContentTypeJson] } it "fails if the table has no PK" $ - put "/no_pk?a=eq.one&b=eq.two" [str| [ { "a": "one", "b": "two" } ]|] + put "/no_pk?a=eq.one&b=eq.two" [json| [ { "a": "one", "b": "two" } ]|] `shouldRespondWith` [json|{"message":"Filters must include all and only primary key columns with 'eq' operators"}|] { matchStatus = 405 , matchHeaders = [matchContentTypeJson] } @@ -235,14 +234,14 @@ spec = context "Inserting row" $ do it "succeeds on table with single pk col" $ do get "/tiobe_pls?name=eq.Go" `shouldRespondWith` "[]" - put "/tiobe_pls?name=eq.Go" [str| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` 204 + put "/tiobe_pls?name=eq.Go" [json| [ { "name": "Go", "rank": 19 } ]|] `shouldRespondWith` 204 get "/tiobe_pls?name=eq.Go" `shouldRespondWith` [json| [ { "name": "Go", "rank": 19 } ]|] { matchHeaders = [matchContentTypeJson] } it "succeeds on table with composite pk" $ do get "/employees?first_name=eq.Susan&last_name=eq.Heidt" `shouldRespondWith` "[]" put "/employees?first_name=eq.Susan&last_name=eq.Heidt" - [str| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] + [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "48000", "company": "GEX", "occupation": "Railroad engineer" } ]|] `shouldRespondWith` 204 get "/employees?first_name=eq.Susan&last_name=eq.Heidt" `shouldRespondWith` @@ -251,19 +250,19 @@ spec = it "succeeds if the table has only PK cols and no other cols" $ do get "/only_pk?id=eq.10" `shouldRespondWith` "[]" - put "/only_pk?id=eq.10" [str|[ { "id": 10 } ]|] `shouldRespondWith` 204 + put "/only_pk?id=eq.10" [json|[ { "id": 10 } ]|] `shouldRespondWith` 204 get "/only_pk?id=eq.10" `shouldRespondWith` [json|[ { "id": 10 } ]|] { matchHeaders = [matchContentTypeJson] } context "Updating row" $ do it "succeeds on table with single pk col" $ do get "/tiobe_pls?name=eq.Go" `shouldRespondWith` [json|[ { "name": "Go", "rank": 19 } ]|] { matchHeaders = [matchContentTypeJson] } - put "/tiobe_pls?name=eq.Go" [str| [ { "name": "Go", "rank": 13 } ]|] `shouldRespondWith` 204 + put "/tiobe_pls?name=eq.Go" [json| [ { "name": "Go", "rank": 13 } ]|] `shouldRespondWith` 204 get "/tiobe_pls?name=eq.Go" `shouldRespondWith` [json| [ { "name": "Go", "rank": 13 } ]|] { matchHeaders = [matchContentTypeJson] } it "succeeds if the payload has more than one row, but it only puts the first element" $ request methodPut "/tiobe_pls?name=eq.Go" [("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.object+json")] - [str| [ { "name": "Go", "rank": 19 }, { "name": "Swift", "rank": 12 } ] |] + [json| [ { "name": "Go", "rank": 19 }, { "name": "Swift", "rank": 12 } ] |] `shouldRespondWith` [json|{ "name": "Go", "rank": 19 }|] { matchStatus = 200 , matchHeaders = [matchContentTypeSingular] } @@ -274,7 +273,7 @@ spec = [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "$48,000.00", "company": "GEX", "occupation": "Railroad engineer" } ]|] { matchHeaders = [matchContentTypeJson] } put "/employees?first_name=eq.Susan&last_name=eq.Heidt" - [str| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "60000", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|] + [json| [ { "first_name": "Susan", "last_name": "Heidt", "salary": "60000", "company": "Gamma Gas", "occupation": "Railroad engineer" } ]|] `shouldRespondWith` 204 get "/employees?first_name=eq.Susan&last_name=eq.Heidt" `shouldRespondWith` @@ -283,13 +282,13 @@ spec = it "succeeds if the table has only PK cols and no other cols" $ do get "/only_pk?id=eq.10" `shouldRespondWith` [json|[ { "id": 10 } ]|] { matchHeaders = [matchContentTypeJson] } - put "/only_pk?id=eq.10" [str|[ { "id": 10 } ]|] `shouldRespondWith` 204 + put "/only_pk?id=eq.10" [json|[ { "id": 10 } ]|] `shouldRespondWith` 204 get "/only_pk?id=eq.10" `shouldRespondWith` [json|[ { "id": 10 } ]|] { matchHeaders = [matchContentTypeJson] } it "works with return=representation and vnd.pgrst.object+json" $ request methodPut "/tiobe_pls?name=eq.Ruby" [("Prefer", "return=representation"), ("Accept", "application/vnd.pgrst.object+json")] - [str| [ { "name": "Ruby", "rank": 11 } ]|] + [json| [ { "name": "Ruby", "rank": 11 } ]|] `shouldRespondWith` [json|{ "name": "Ruby", "rank": 11 }|] { matchHeaders = [matchContentTypeSingular] } context "with a camel case pk column" $ do @@ -315,6 +314,6 @@ spec = } it "works with PUT" $ do - put "/UnitTest?idUnitTest=eq.1" [str| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|] `shouldRespondWith` 204 + put "/UnitTest?idUnitTest=eq.1" [json| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|] `shouldRespondWith` 204 get "/UnitTest?idUnitTest=eq.1" `shouldRespondWith` [json| [ { "idUnitTest": 1, "nameUnitTest": "unit test 1" } ]|] { matchHeaders = [matchContentTypeJson] }